Allow setting of jquery ui slider to above max value or below the min value - jquery-ui

I have a jquery ui slider that is linked to a numerical textbox. The slider has a max and min value.
See ui slider with text box input or
Using knockout js with jquery ui sliders for a knockout js implementation.
My question is: is it possible to set the value of the slider to above the max or below the min?
If value is outside the range it is set to the max or min of the range:
$(element).slider("value", value);
So for example, say the slider represents the percentage of your monthly salary between 50 and 100. Monthly salary is set to 10000. If you slide the slider it will vary from 5000 to 10000, but I still want users to be able to input values outside of the range. So if the user inputs 12000 the slider will slide to max, and if the user inputs 2000 the slider will slide to the min.

You can accomplish this by overriding the _trimAlignValue function that I noted in my comment:
$.ui.slider.prototype._trimAlignValue = function (val) {
var step = (this.options.step > 0) ? this.options.step : 1,
valModStep = val % step,
alignValue = val - valModStep;
if (Math.abs(valModStep) * 2 >= step) {
alignValue += (valModStep > 0) ? step : (-step);
}
return parseFloat(alignValue.toFixed(5));
};
This will effect every slider on the page--if this isn't the desired effect you should wrap this functionality in your own plugin (I can provide an example that does that too, if need be).
This combined with your existing KnockoutJS custom binding seems to work well.
Example: http://jsfiddle.net/Aa5nK/7/

Adapted from the answer in Using knockout js with jquery ui sliders
<h2>Slider Demo</h2>
Savings:
<input data-bind="value: savings, valueUpdate: 'afterkeydown'"
/>
<div style="margin: 10px" data-bind="slider: savings, sliderOptions: {min: 0, max: 100, range: 'min', step: 1}"></div>
And here's the custom binding:
ko.bindingHandlers.slider = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().sliderOptions || {};
$(element).slider(options);
ko.utils.registerEventHandler(element, "slidechange", function (event, ui) {
var value = valueAccessor();
if(!(value < $(element).slider('option', 'min') || value > $(element).slider('option', 'max')))
{
valueAccessor(ui.value);
}
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).slider("destroy");
});
ko.utils.registerEventHandler(element, "slide", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (isNaN(value)) value = 0;
if (value < $(element).slider('option', 'min')) {
value = $(element).slider("option", "min");
} else if (value > $(element).slider('option', 'max')) {
value = $(element).slider("option", "max");
}
$(element).slider("value", value);
}
};
var ViewModel = function () {
var self = this;
self.savings = ko.observable(10);
self.spent = ko.observable(5);
self.net = ko.computed(function () {
return self.savings() - self.spent();
});
};
ko.applyBindings(new ViewModel());
I adapted the jsFiddle from that answer too.

Related

Anystock comparisonMode same value in tooltip

When using a stock chart, I am using the comparisonMode with a date. The value displayed by the crosshair is correct, but the value in the tooltip is the real value (not compared). How could I display the compared value instead?
As you can see on the picture, the compared value is 107.1 but the tooltip is displaying the actual value 893.5. I am using anychart 8.0.0
I'm glad to inform you that in the new version of AnyStock 8.1.0 the calculated change value is available right from the point information. It may be used in tooltips and legends. I guess this is exactly what you were looking for.
The example of using this feature you may find on this link.
Now the context of every point includes valueChange and valuePercentChange properties.
This feature requires a few additional lines of JS code, I prepared an example below to show how it works. Now compared value is shown in cross-hair label, in the tooltip, and in legend.
anychart.onDocumentReady(function() {
var dataTable = anychart.data.table();
dataTable.addData(get_dji_daily_short_data());
var firstMapping = dataTable.mapAs({'value': 1});
var secondMapping = dataTable.mapAs({'value': 3});
chart = anychart.stock();
var plot = chart.plot();
var series0 = plot.line(firstMapping);
var series1 = plot.line(secondMapping);
var yScale = plot.yScale();
// Set comparison mode.
yScale.comparisonMode("value");
var xScale = chart.xScale();
chart.container("container");
chart.draw();
//reference points of both series
var firstVisibleValue0 = null;
var firstVisibleValue1 = null;
//after chart rendering format tooltip and legend
getVisibleValues();
tooltipLegendFormat(firstVisibleValue0, firstVisibleValue1);
//after every scroll change recalculate reference points
//and reformat tooltip and legend
chart.scroller().listen('scrollerchange', function() {
getVisibleValues();
tooltipLegendFormat(firstVisibleValue0, firstVisibleValue1);
});
function getVisibleValues() {
// Gets scale minimum.
var minimum = xScale.getMinimum();
//select data from mappings
var selectable0 = firstMapping.createSelectable();
var selectable1 = secondMapping.createSelectable();
// Sets value for search.
var select0 = selectable0.search(minimum, "nearest");
var select1 = selectable1.search(minimum, "nearest");
// get values in first visible points
firstVisibleValue0 = select0.get('value');
firstVisibleValue1 = select1.get('value');
}
function tooltipLegendFormat(firstVisibleValue0, firstVisibleValue1) {
//format tooltips and legends of both series
series0.tooltip().format(function () {
return 'Series 0: ' + Math.round(this.value - firstVisibleValue0);
});
series0.legendItem().format(function(){
return 'Series 0: ' + Math.round(this.value - firstVisibleValue0);
});
series1.tooltip().format(function () {
return 'Series 1: ' + Math.round(this.value - firstVisibleValue1);
});
series1.legendItem().format(function(){
return 'Series 1: ' + Math.round(this.value - firstVisibleValue1);
});
}
});
html, body, #container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
<script src="https://cdn.anychart.com/releases/8.0.1/js/anychart-base.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.0.1/js/anychart-stock.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.0.1/js/anychart-exports.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.0.1/js/anychart-ui.min.js"></script>
<script src="https://cdn.anychart.com/csv-data/dji-daily-short.js"></script>
<link rel="stylesheet" href="https://cdn.anychart.com/releases/8.0.1/css/anychart-ui.min.css" />
<div id="container"></div>

Set Umbraco Property Editor Input to jQueryUI Datepicker

I'm close but still can't quite get this to work.
I have a new custom property editor that is loading correctly and is doing almost everything expected until I try to set the text field to be a jQuery UI element.
As soon as I add a directive in Angular for setting it to call the jQuery UI datepicker function, I get the following error suggesting it hasn't loaded the jQueryUI script library correctly:
TypeError: Object [object Object] has no method 'datepicker'
Trouble is, I can't see where I should be adding it as the logical places (to my mind, at least) seem to make no difference. Here is the code in full:
function MultipleDatePickerController($scope, assetsService) {
//tell the assetsService to load the markdown.editor libs from the markdown editors
//plugin folder
//assetsService
// .load([
// "http://code.jquery.com/ui/1.10.4/jquery-ui.min.js"
// ])
// .then(function () {
// //this function will execute when all dependencies have loaded
// });
//load the seperat css for the editor to avoid it blocking our js loading
assetsService.loadCss("/css/jquery-ui.custom.min.css");
if (!$scope.model.value) {
$scope.model.value = [];
}
//add any fields that there isn't values for
//if ($scope.model.config.min > 0) {
if ($scope.model.value.length > 0) {
for (var i = 0; i < $scope.model.value.length; i++) {
if ((i + 1) > $scope.model.value.length) {
$scope.model.value.push({ value: "" });
}
}
}
$scope.add = function () {
//if ($scope.model.config.max <= 0 || $scope.model.value.length < $scope.model.config.max) {
if ($scope.model.value.length <= 52) {
$scope.model.value.push({ value: "" });
}
};
$scope.remove = function (index) {
var remainder = [];
for (var x = 0; x < $scope.model.value.length; x++) {
if (x !== index) {
remainder.push($scope.model.value[x]);
}
}
$scope.model.value = remainder;
};
}
var datePicker = angular.module("umbraco").controller("AcuIT.MultidateController", MultipleDatePickerController);
datePicker.directive('jqdatepicker', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
$(function () {
element.datepicker({
dateFormat: 'dd/mm/yy',
onSelect: function (date) {
scope.$apply(function () {
ngModelCtrl.$setViewValue(date);
});
}
});
});
}
}
});
I faced the same problem when adapting a jQuery Date Range Picker for my Date Range Picker package for Umbraco 7. It's frustrating! The problem (I think) is that Angular's ng-model listens for "input" changes to trigger events and so doesn't pick up on a jQuery triggered event.
The way around it I found was to force the input event of the element you wish to update to fire manually, using jQuery's .trigger() event.
For example, the date picker I was using had this code for when a date was changed:
updateInputText: function () {
if (this.element.is('input')) {
this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
}
},
I just adapted it to force an input trigger by adding this.element.trigger('input') to the code block, so it now reads:
updateInputText: function () {
if (this.element.is('input')) {
this.element.val(this.startDate.format(this.format) + this.separator + this.endDate.format(this.format));
this.element.trigger('input');
}
},
This forces Angular to "see" the change and then ng-model is updated. There may well be a more elegant way (as I'm an Angular newbie), but I know this worked for me.
Got it. This is probably a bit of a hack, but it's simple and effective so it's a win nonetheless.
The assetsService call is the key, where I've put code into the deferred .then statement to call jQueryUI's datepicker on any item that has the "jqdp" CSS class:
//tell the assetsService to load the markdown.editor libs from the markdown editors
//plugin folder
assetsService
.load([
"/App_Plugins/Multidate/jquery-ui.min.js"
])
.then(function () {
//this function will execute when all dependencies have loaded
$('.jqdp').datepicker({ dateFormat: 'dd/mm/yy' });
});
I've then gone and added that class to my view:
<input type="text" jqdatepicker name="item_{{$index}}" ng-model="item.value" class="jqdp" id="dp-{{model.alias}}-{{$index}}" />
Finally, I've added a directive to ensure that dynamically-added items also display a datepicker:
datePicker.directive('jqdatepicker', function () {
return function (scope, element, attrs) {
scope.$watch("jqdatepicker", function () {
try{
$(element).datepicker({ dateFormat: 'dd/mm/yy' });
}
catch(e)
{}
});
};
});
As I said, this is possibly a bit hacky but it achieves the right result and seems like a simple solution.

Highcharts large data set clustering

I have tens of thousands (possibly hundreds of thousands) of points that I need plotted with Highcharts. Is there a way where I can cluster the data on the server, so it will show less than 1000 points, but as you zoom in it will make AJAX calls to the server to get the data for that zoomed region (it would probably run through the same cluster algorithm). How would this interface with the Highcharts API?
There is a highstock demo that does this http://www.highcharts.com/stock/demo/lazy-loading.
But you can do the same thing with highcharts http://jsfiddle.net/RHkgr/
The important bit is the afterSetExtremes function
...
xAxis : {
events : {
afterSetExtremes : afterSetExtremes
},
...
/**
* Load new data depending on the selected min and max
*/
function afterSetExtremes(e) {
var url,
currentExtremes = this.getExtremes(),
range = e.max - e.min;
var chart = $('#container').highcharts();
chart.showLoading('Loading data from server...');
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start='+ Math.round(e.min) +
'&end='+ Math.round(e.max) +'&callback=?', function(data) {
chart.series[0].setData(data);
chart.hideLoading();
});
}
Here is an improvement for Barbara's answer,
It registers to the setExtremes event,
to know if this is a reset zoom event.
If it is - it gets the entire dataset,
thus allowing reset zoom to work correctly.
It also allows zooming in both x and y.
http://jsfiddle.net/DktpS/8/
var isReset = false;
...
xAxis: {
events: {
afterSetExtremes : afterSetExtremes,
setExtremes: function (e) {
if (e.max == null || e.min == null) {
isReset = true;
}
else
{
isReset = false;
}
}
},
minRange: 3600 * 1000 // one hour
},
series: [{
data: data,
dataGrouping: {
enabled: false
}
}]
});
});
});
/**
* Load new data depending on the selected min and max
*/
function afterSetExtremes(e) {
var url,
currentExtremes = this.getExtremes(),
range = e.max - e.min;
var chart = $('#container').highcharts();
var min = 0;
var max = 1.35e12;
if(!isReset)
{
min = e.min;
max = e.max;
}
chart.showLoading('Loading data from server...');
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start=' + Math.round(min) +
'&end=' + Math.round(max) + '&callback=?', function (data) {
chart.series[0].setData(data);
chart.hideLoading();
});
}
In case when you will not have a limit of points, you can increase turboThreshold paramter.

Datepicker using Jquery loses focus to the textbox after date selected.

Datepicker using Jquery loses focus to the textbox after date selected. I am using jquery-ui-1.9.2.When a date is selected the focus not coming to the textbox.Any solution?
Try using the below code.
HTML code:
<input type="text" id="date"/>
JQuery:
$("#date").datepicker({
onClose: function () {
$(this).focus();
}
});
JSFiddle1
EDIT: The above code has a problem in IE, the datepicker is not getting closed. Here in this blog you can find the more information.
<script language='javascript' src="jquery-migrate-1.2.1.js"></script> // download and add this
$("#date").datepicker({
/* fix buggy IE focus functionality */
fixFocusIE: false,
onClose: function(dateText, inst) {
this.fixFocusIE = true;
this.focus();
},
beforeShow: function(input, inst) {
var result = $.browser.msie ? !this.fixFocusIE : true;
this.fixFocusIE = false;
return result;
}
});
JSFiddle2
$(".datepicker").datepicker({
onClose: function () {
$(this).parents().nextAll().find($(":input[type !='hidden']")).first().focus();
}
});
});
I have found an easier way that will put the focus on the next input, no matter how nested it is. You can always swap out the condition after the .find to whatever you like and it will bring the focus to that.
Initialise all the datepcikers on Doc Ready
$('.datepicker').datepicker(
{
onClose: function () {
this.focus();
}
});
Exapnding Praveen's answer.
I had one problem with it. On IE datepicker refused to show up each odd time I focused a field.
Also, there was a slight logical issue with that solution (which did not affect anything, but still not correct to my eye): fixFocusIE field is being set on options, but then later it is being called on "this", when "this" refers to DOM element and not options object. So essentially there are two fixFocusIE - one in options (unused) and the second one on DOM element itself.
And also $.browser.msie did not work anymore, I had to invent my own IE detector.
My working code looks like that:
var el = $("selector of element I need to assign to datepicker");
var options = {}; // actually I fill options from defaults and do some other manipulations, but I left it as empty object here for brevity
options.fixFocusIE = false;
function detectIE() {
var ua = window.navigator.userAgent;
if(ua.indexOf('MSIE ') > 0 ||
ua.indexOf('Trident/') > 0 ||
ua.indexOf('Edge/') > 0) {
return true;
}
// other browser
return false;
}
/* blur() and change() are needed to update Angular bindings, if any */
options.onSelect = function(dateText, inst) {
options.fixFocusIE = true;
$(this).blur().change().focus();
};
options.onClose = function(dateText, inst) {
options.fixFocusIE = true;
this.focus();
};
options.beforeShow = function(input, inst) {
var result = detectIE() ? !options.fixFocusIE : true;
options.fixFocusIE = false;
return result;
};
/* and this one will reset fixFocusIE to prevent datepicker from refusing to show when focusing the field for second time in IE */
el.blur(function(){
options.fixFocusIE = false;
});
el.datepicker(options);

jQuery UI Spinner with letters A-Z or other custom range

Is there a way to customize jQuery UI spinner, so that A-Z letters (or any custom range) is possible?
Yes, this is possible. Here's a simple example using A-Z, adapted from the provided time example:
$.widget("ui.alphaspinner", $.ui.spinner, {
options: {
min: 65,
max: 90
},
_parse: function(value) {
if (typeof value === "string") {
return value.charCodeAt(0);
}
return value;
},
_format: function(value) {
return String.fromCharCode(value);
}
});
Usage:
$("#my-input").alphaspinner();
Example: http://jsfiddle.net/4nwTc/1/
The above example creates a new widget called alphaspinner that inherits from spinner. You can do this for just one spinner with the following:
$(function() {
var spinner = $("#alpha-spinner").spinner({
min: 65,
max: 90
}).data("spinner");
spinner._parse = function (value) {
if (typeof value === "string") {
return value.charCodeAt(0);
}
return value;
};
spinner._format = function (value) {
return String.fromCharCode(value);
}
});​
Example: http://jsfiddle.net/4nwTc/2/
I built up on Andrews code and built a spinner widget that takes a string array for input.
You can see the solution here.

Resources