1/*! 2 * roundSlider v1.3 | (c) 2015-2016, Soundar 3 * MIT license | http://roundsliderui.com/licence.html 4 */ 5 6(function ($, window, undefined) { 7 "use strict"; 8 /*jslint nomen: true */ 9 10 var pluginName = "roundSlider"; 11 12 // The plugin initialization 13 $.fn[pluginName] = function (options) { 14 return CreateRoundSlider.call(this, options, arguments); 15 }; 16 17 RoundSlider.prototype = { 18 19 pluginName: pluginName, 20 version: "1.3", 21 22 // after the control initialization the updated default values 23 // are merged into the options 24 options: {}, 25 26 // default properties of the plugin. while add a new property, 27 // that type should be included in the "_props:" for validation 28 defaults: { 29 min: 0, 30 max: 100, 31 step: 1, 32 value: null, 33 radius: 85, 34 width: 10, 35 handleSize: "+0", 36 startAngle: 0, 37 endAngle: "+360", 38 animation: true, 39 showTooltip: true, 40 editableTooltip: true, 41 readOnly: false, 42 disabled: false, 43 keyboardAction: true, 44 mouseScrollAction: false, 45 lineCap: "round", 46 sliderType: "default", 47 circleShape: "full", 48 handleShape: "round", 49 50 // events 51 beforeCreate: null, 52 create: null, 53 start: null, 54 drag: null, 55 change: null, 56 stop: null, 57 tooltipFormat: null 58 }, 59 _props: function () { 60 return { 61 numberType: ["min", "max", "step", "radius", "width", "startAngle"], 62 booleanType: ["animation", "showTooltip", "editableTooltip", "readOnly", "disabled", 63 "keyboardAction", "mouseScrollAction"], 64 stringType: ["sliderType", "circleShape", "handleShape", "lineCap"] 65 }; 66 }, 67 control: null, 68 _init: function () { 69 this._isBrowserSupport = this._isBrowserSupported(); 70 this._isKO = false; 71 this._isAngular = false; 72 if (this.control.is("input")) { 73 this._isInputType = true; 74 this._hiddenField = this.control; 75 this.control = createElement("div"); 76 this.control.insertAfter(this._hiddenField); 77 this.options.value = this._hiddenField.val() || this.options.value; 78 var that = this; 79 this._checkKO() && setTimeout(function () { that._checkKO(); }, 1); 80 this._checkAngular(); 81 } 82 this._bindOnDrag = false; 83 var _updateOn = this._dataElement().attr("data-updateon"); 84 if (typeof _updateOn == "string") { if (_updateOn == "drag") this._bindOnDrag = true; } 85 else if (this._isAngular) this._bindOnDrag = true; 86 87 this._onInit(); 88 }, 89 _onInit: function () { 90 this._initialize(); 91 this._update(); 92 this._render(); 93 }, 94 _initialize: function () { 95 if (!this._isBrowserSupport) return; 96 this._isReadOnly = false; 97 this._checkDataType(); 98 this._refreshCircleShape(); 99 }, 100 _render: function () { 101 this.container = createElement("div.rs-container"); 102 this.innerContainer = createElement("div.rs-inner-container"); 103 this.block = createElement("div.rs-block rs-outer rs-border"); 104 this.container.append(this.innerContainer.append(this.block)); 105 this.control.addClass("rs-control").empty().append(this.container); 106 107 this._setRadius(); 108 if (this._isBrowserSupport) { 109 this._createLayers(); 110 this._setProperties(); 111 this._setValue(); 112 this._updateTooltipPos(); 113 this._bindControlEvents("_bind"); 114 this._checkIE(); 115 } 116 else { 117 var msg = createElement("div.rs-msg"); 118 msg.html(typeof this._throwError === "function" ? this._throwError() : this._throwError); 119 this.control.empty().addClass("rs-error").append(msg); 120 if (this._isInputType) this.control.append(this._dataElement()); 121 } 122 }, 123 _update: function () {
124 this._validateSliderType(); 125 this._updateStartEnd(); 126 this._validateStartEnd(); 127 this._handle1 = this._handle2 = this._handleDefaults(); 128 this._analyzeModelValue(); 129 this._validateModelValue(); 130 }, 131 _createLayers: function () { 132 var padd = this.options.width, start = this._start, path; 133 path = createElement("div.rs-path rs-transition"); 134 135 if (this._rangeSlider || this._showRange) { 136 this.block1 = path.clone().addClass("rs-range-color").rsRotate(start); 137 this.block2 = path.clone().addClass("rs-range-color").css("opacity", "0").rsRotate(start); 138 this.block3 = path.clone().addClass("rs-path-color").rsRotate(start); 139 this.block4 = path.addClass("rs-path-color").css({ "opacity": "1", "z-index": "1" }).rsRotate(start - 180); 140 141 this.block.append(this.block1, this.block2, this.block3, this.block4).addClass("rs-split"); 142 } 143 else this.block.append(path.addClass("rs-path-color")); 144 145 this.lastBlock = createElement("span.rs-block").css({ "padding": padd }); 146 this.innerBlock = createElement("div.rs-inner rs-bg-color rs-border"); 147 this.lastBlock.append(this.innerBlock); 148 this.block.append(this.lastBlock); 149 this._appendHandle(); 150 this._appendOverlay(); 151 this._appendHiddenField(); 152 }, 153 _setProperties: function () { 154 this._updatePre(); 155 this._setHandleShape(); 156 this._addAnimation(); 157 this._appendTooltip(); 158 if (!this.options.showTooltip) this._removeTooltip(); 159 if (this.options.disabled) this.disable(); 160 else if (this.options.readOnly) this._readOnly(true); 161 if (this.options.mouseScrollAction) this._bindScrollEvents("_bind"); 162 }, 163 _updatePre: function () { 164 this._prechange = this._predrag = this.options.value; 165 }, 166 _setValue: function () { 167 if (this._rangeSlider) { 168 this._setHandleValue(1); 169 this._setHandleValue(2); 170 } 171 else { 172 if (this._showRange) this._setHandleValue(1); 173 var index = (this.options.sliderType == "default") ? (this._active || 1) : parseFloat(this.bar.children().attr("index")); 174 this._setHandleValue(index); 175 } 176 }, 177 _appendTooltip: function () { 178 if (this.container.children(".rs-tooltip").length !== 0) return; 179 this.tooltip = createElement("span.rs-tooltip rs-tooltip-text"); 180 this.container.append(this.tooltip); 181 this._tooltipEditable(); 182 this._updateTooltip(); 183 }, 184 _removeTooltip: function () { 185 if (this.container.children(".rs-tooltip").length == 0) return; 186 this.tooltip && this.tooltip.remove(); 187 }, 188 _tooltipEditable: function () { 189 if (!this.tooltip || !this.options.showTooltip) return; 190 var hook; 191 if (this.options.editableTooltip) { 192 this.tooltip.addClass("edit"); 193 hook = "_bind"; 194 } 195 else { 196 this.tooltip.removeClass("edit"); 197 hook = "_unbind"; 198 } 199 this[hook](this.tooltip, "click", this._editTooltip); 200 }, 201 _editTooltip: function (e) { 202 if (!this.tooltip.hasClass("edit") || this._isReadOnly) return; 203 var border = parseFloat(this.tooltip.css("border-left-width")) * 2; 204 this.input = createElement("input.rs-input rs-tooltip-text").css({ 205 height: this.tooltip.outerHeight() - border, 206 width: this.tooltip.outerWidth() - border 207 }); 208 this.tooltip.html(this.input).removeClass("edit").addClass("hover"); 209 210 this.input.focus().val(this._getTooltipValue(true)); 211 212 this._bind(this.input, "blur", this._focusOut); 213 this._bind(this.input, "change", this._focusOut); 214 }, 215 _focusOut: function (e) { 216 if (e.type == "change") { 217 this.options.value = this.input.val().replace("-", ","); 218 this._analyzeModelValue();
219 this._validateModelValue(); 220 this._setValue(); 221 this.input.val(this._getTooltipValue(true)); 222 } 223 else { 224 this.tooltip.addClass("edit").removeClass("hover"); 225 this._updateTooltip(); 226 } 227 this._raiseEvent("change"); 228 }, 229 _setHandleShape: function () { 230 var type = this.options.handleShape; 231 this._handles().removeClass("rs-handle-dot rs-handle-square"); 232 if (type == "dot") this._handles().addClass("rs-handle-dot"); 233 else if (type == "square") this._handles().addClass("rs-handle-square"); 234 else this.options.handleShape = this.defaults.handleShape; 235 }, 236 _setHandleValue: function (index) { 237 this._active = index; 238 var handle = this["_handle" + index]; 239 if (this.options.sliderType != "min-range") this.bar = this._activeHandleBar(); 240 this._changeSliderValue(handle.value, handle.angle); 241 }, 242 _setAnimation: function () { 243 if (this.options.animation) this._addAnimation(); 244 else this._removeAnimation(); 245 }, 246 _addAnimation: function () { 247 if (this.options.animation) this.control.addClass("rs-animation"); 248 }, 249 _removeAnimation: function () { 250 this.control.removeClass("rs-animation"); 251 }, 252 _setRadius: function () { 253 var r = this.options.radius, d = r * 2; 254 var circleShape = this.options.circleShape, height = d, width = d; 255 this.container.removeClass().addClass("rs-container"); 256 257 if (circleShape.indexOf("half") === 0) { 258 switch (circleShape) { 259 case "half-top": 260 case "half-bottom": 261 height = r; width = d; break; 262 case "half-left": 263 case "half-right": 264 height = d; width = r; break; 265 } 266 this.container.addClass(circleShape.replace("half-", "") + " half"); 267 } 268 else if (circleShape.indexOf("quarter") === 0) { 269 height = width = r; 270 var s = circleShape.split("-"); 271 this.container.addClass(s[0] + " " + s[1] + " " + s[2]); 272 } 273 else this.container.addClass("full " + circleShape); 274 275 var style = { "height": height, "width": width }; 276 this.control.css(style); 277 this.container.css(style); 278 }, 279 _border: function (seperator) { 280 if (seperator) return parseFloat(this._startLine.children().css("border-bottom-width")); 281 return parseFloat(this.block.css("border-top-width")) * 2; 282 }, 283 _appendHandle: function () { 284 if (this._rangeSlider || !this._showRange) this._createHandle(1); 285 if (this._rangeSlider || this._showRange) this._createHandle(2); 286 this._startLine = this._addSeperator(this._start, "rs-start"); 287 this._endLine = this._addSeperator(this._start + this._end, "rs-end"); 288 this._refreshSeperator(); 289 }, 290 _addSeperator: function (pos, cls) { 291 var line = createElement("span.rs-seperator rs-border"), width = this.options.width, _border = this._border(); 292 var lineWrap = createElement("span.rs-bar rs-transition " + cls).append(line).rsRotate(pos); 293 this.container.append(lineWrap); 294 return lineWrap; 295 }, 296 _refreshSeperator: function () { 297 var bars = this._startLine.add(this._endLine), seperators = bars.children().removeAttr("style"); 298 var opt = this.options, width = opt.width, _border = this._border(), size = width + _border; 299 if (opt.lineCap == "round" && opt.circleShape != "full") { 300 bars.addClass("rs-rounded"); 301 seperators.css({ width: size, height: (size / 2) + 1 }); 302 this._startLine.children().css("margin-top", -1).addClass(opt.sliderType == "min-range" ? "rs-range-color" : "rs-path-color"); 303 this._endLine.children().css("margin-top", size / -2).addClass("rs-path-color"); 304 } 305 else { 306 bars.removeClass("rs-rounded"); 307 seperators.css({ "width": size, "margin-top": this._border(true) / -2 }).removeClass("rs-range-color rs-path-color"); 308 } 309 }, 310 _updateSeperator: function () { 311 this._startLine.rsRotate(this._start); 312 this._endLine.rsRotate(this._start + this._end); 313 }, 314 _createHandle: function (index) { 315 var handle = createElement("div.rs-handle rs-move"), o = this.options, hs; 316 if ((hs = o.handleShape) != "round") handle.addClass("rs-handle-" + hs);
317 handle.attr({ "index": index, "tabIndex": "0" }); 318 319 var id = this._dataElement()[0].id, id = id ? id + "_" : ""; 320 var label = id + "handle" + (o.sliderType == "range" ? "_" + (index == 1 ? "start" : "end") : ""); 321 handle.attr({ "role": "slider", "aria-label": label }); // WAI-ARIA support 322 323 var bar = createElement("div.rs-bar rs-transition").css("z-index", "7").append(handle).rsRotate(this._start); 324 bar.addClass(o.sliderType == "range" && index == 2 ? "rs-second" : "rs-first"); 325 this.container.append(bar); 326 this._refreshHandle(); 327 328 this.bar = bar; 329 this._active = index; 330 if (index != 1 && index != 2) this["_handle" + index] = this._handleDefaults(); 331 this._bind(handle, "focus", this._handleFocus); 332 this._bind(handle, "blur", this._handleBlur); 333 return handle; 334 }, 335 _refreshHandle: function () { 336 var hSize = this.options.handleSize, h, w, isSquare = true; 337 if (typeof hSize === "string" && isNumber(hSize)) { 338 if (hSize.charAt(0) === "+" || hSize.charAt(0) === "-") { 339 try { hSize = eval(this.options.width + hSize.charAt(0) + Math.abs(parseFloat(hSize))); } 340 catch (e) { console.warn(e); } 341 } 342 else if (hSize.indexOf(",")) { 343 var s = hSize.split(","); 344 if (isNumber(s[0]) && isNumber(s[1])) w = parseFloat(s[0]), h = parseFloat(s[1]), isSquare = false; 345 } 346 } 347 if (isSquare) h = w = isNumber(hSize) ? parseFloat(hSize) : this.options.width; 348 var diff = (this.options.width + this._border() - w) / 2; 349 this._handles().css({ height: h, width: w, "margin": -h / 2 + "px 0 0 " + diff + "px" }); 350 }, 351 _handleDefaults: function () { 352 return { angle: this._valueToAngle(this.options.min), value: this.options.min }; 353 }, 354 _handles: function () { 355 return this.container.children("div.rs-bar").find(".rs-handle"); 356 }, 357 _activeHandleBar: function (index) { 358 index = (index != undefined) ? index : this._active; 359 return $(this.container.children("div.rs-bar")[index - 1]); 360 }, 361 _handleArgs: function (index) { 362 index = (index != undefined) ? index : this._active; 363 var _handle = this["_handle" + index]; 364 return { 365 element: this._activeHandleBar(index).children(), 366 index: index, 367 isActive: index == this._active, 368 value: _handle ? _handle.value : null, 369 angle: _handle ? _handle.angle : null 370 }; 371 }, 372 _dataElement: function () { 373 return this._isInputType ? this._hiddenField : this.control; 374 }, 375 _raiseEvent: function (event) { 376 var preValue = this["_pre" + event]; 377 if (preValue !== this.options.value) { 378 this["_pre" + event] = this.options.value; 379 this._updateTooltip(); 380 if ((event == "change") || (this._bindOnDrag && event == "drag")) this._updateHidden(); 381 return this._raise(event, { value: this.options.value, preValue: preValue, "handle": this._handleArgs() }); 382 } 383 }, 384 385 // Events handlers 386 _elementDown: function (e) { 387 if (this._isReadOnly) return; 388 var $target = $(e.target); 389 390 if ($target.hasClass("rs-handle")) { 391 this._handleDown(e); 392 } 393 else { 394 var point = this._getXY(e), center = this._getCenterPoint(); 395 var distance = getdistance(point, center); 396 var outerDistance = this.block.outerWidth() / 2; 397 var innerDistance = outerDistance - (this.options.width + this._border()); 398 399 if (distance >= innerDistance && distance <= outerDistance) { 400 e.preventDefault(); 401 var handle = this.control.find(".rs-handle.rs-focus"), angle, value; 402 //if (handle.length == 0) 403 this.control.attr("tabindex", "0").focus().removeAttr("tabindex"); 404 if ($target.hasClass("rs-seperator")) { 405 value = $target.parent().hasClass("rs-start") ? this.options.min : this.options.max; 406 angle = this._valueToAngle(value); 407 } 408 else { 409 var d = this._getAngleValue(point, center); 410 angle = d.angle, value = d.value; 411 } 412 if (this._rangeSlider) { 413 handle = this.control.find(".rs-handle.rs-focus"); 414 if (handle.length == 1) this._active = parseFloat(handle.attr("index")); 415 else this._active = (this._handle2.value - value) < (value - this._handle1.value) ? 2 : 1; 416 this.bar = this._activeHandleBar(); 417 } 418 419 this._changeSliderValue(value, angle); 420 this._raiseEvent("change"); 421 } 422 } 423 }, 424 _handleDown: function (e) { 425 e.preventDefault(); 426 var $target = $(e.target);
427 $target.focus(); 428 this._removeAnimation(); 429 this._bindMouseEvents("_bind"); 430 this.bar = $target.parent(); 431 this._active = parseFloat($target.attr("index")); 432 this._handles().removeClass("rs-move"); 433 this._raise("start", { value: this.options.value, "handle": this._handleArgs() }); 434 }, 435 _handleMove: function (e) { 436 e.preventDefault(); 437 var point = this._getXY(e), center = this._getCenterPoint(); 438 var d = this._getAngleValue(point, center, true), angle, value; 439 angle = d.angle, value = d.value; 440 441 this._changeSliderValue(value, angle); 442 this._raiseEvent("drag"); 443 }, 444 _handleUp: function (e) { 445 this._handles().addClass("rs-move"); 446 this._bindMouseEvents("_unbind"); 447 this._addAnimation(); 448 this._raiseEvent("change"); 449 this._raise("stop", { value: this.options.value, "handle": this._handleArgs() }); 450 }, 451 _handleFocus: function (e) { 452 if (this._isReadOnly) return; 453 var $target = $(e.target); 454 this._handles().removeClass("rs-focus"); 455 $target.addClass("rs-focus"); 456 this.bar = $target.parent(); 457 this._active = parseFloat($target.attr("index")); 458 if (this.options.keyboardAction) { 459 this._bindKeyboardEvents("_unbind"); 460 this._bindKeyboardEvents("_bind"); 461 } 462 463 // updates the class for change z-index 464 this.control.find("div.rs-bar").css("z-index", "7"); 465 this.bar.css("z-index", "8"); 466 }, 467 _handleBlur: function (e) { 468 this._handles().removeClass("rs-focus"); 469 if (this.options.keyboardAction) this._bindKeyboardEvents("_unbind"); 470 }, 471 _handleKeyDown: function (e) { 472 if (this._isReadOnly) return; 473 var key = e.keyCode; 474 if (key == 27) this._handles().blur(); 475 if (!(key >= 35 && key <= 40)) return; 476 if (key >= 37 && key <= 40) this._removeAnimation(); 477 var h = this["_handle" + this._active], val, ang; 478 479 e.preventDefault(); 480 if (key == 38 || key == 37) val = this._round(this._limitValue(h.value + this.options.step)); // Up || Left Key 481 else if (key == 39 || key == 40) val = this._round(this._limitValue(h.value - this._getMinusStep(h.value))); // Right || Down Key 482 else if (key == 36) val = this._getKeyValue("Home"); // Home Key 483 else if (key == 35) val = this._getKeyValue("End"); // End Key 484 485 ang = this._valueToAngle(val); 486 this._changeSliderValue(val, ang); 487 this._raiseEvent("change"); 488 }, 489 _handleKeyUp: function (e) { 490 this._addAnimation(); 491 }, 492 _getMinusStep: function (val) { 493 if (val == this.options.max) { 494 var step = (this.options.max - this.options.min) % this.options.step; 495 return step == 0 ? this.options.step : step; 496 } 497 return this.options.step; 498 }, 499 _getKeyValue: function (key) { 500 if (this._rangeSlider) { 501 if (key == "Home") return (this._active == 1) ? this.options.min : this._handle1.value; 502 else return (this._active == 1) ? this._handle2.value : this.options.max; 503 } 504 return (key == "Home") ? this.options.min : this.options.max; 505 }, 506 _elementScroll: function (event) { 507 if (this._isReadOnly) return; 508 event.preventDefault(); 509 var e = event.originalEvent || event, h, val, ang, delta; 510 delta = e.wheelDelta ? e.wheelDelta / 60 : (e.detail ? -e.detail / 2 : 0); 511 if (delta == 0) return; 512 513 this._updateActiveHandle(event); 514 h = this["_handle" + this._active]; 515 val = h.value + (delta > 0 ? this.options.step : -this._getMinusStep(h.value)); 516 val = this._limitValue(val); 517 ang = this._valueToAngle(val); 518 519 this._removeAnimation(); 520 this._changeSliderValue(val, ang); 521 this._raiseEvent("change"); 522 this._addAnimation(); 523 }, 524 _updateActiveHandle: function (e) { 525 var $target = $(e.target); 526 if ($target.hasClass("rs-handle") && $target.parent().parent()[0] == this.control[0]) { 527 this.bar = $target.parent(); 528 this._active = parseFloat($target.attr("index")); 529 } 530 if (!this.bar.find(".rs-handle").hasClass("rs-focus")) this.bar.find(".rs-handle").focus(); 531 }, 532 533 // Events binding 534 _bindControlEvents: function (hook) { 535 this[hook](this.control, "mousedown", this._elementDown); 536 this[hook](this.control, "touchstart", this._elementDown); 537 }, 538 _bindScrollEvents: function (hook) { 539 this[hook](this.control, "mousewheel", this._elementScroll); 540 this[hook](this.control, "DOMMouseScroll", this._elementScroll); 541 }, 542 _bindMouseEvents: function (hook) { 543 this[hook]($(document), "mousemove", this._handleMove); 544 this[hook]($(document), "mouseup", this._handleUp); 545 this[hook]($(document), "mouseleave", this._handleUp); 546 547 // *** for Touch support *** // 548 this[hook]($(document), "touchmove", this._handleMove); 549 this[hook]($(document), "touchend", this._handleUp); 550 this[hook]($(document), "touchcancel", this._handleUp); 551 }, 552 _bindKeyboardEvents: function (hook) { 553 this[hook]($(document), "keydown", this._handleKeyDown); 554 this[hook]($(document), "keyup", this._handleKeyUp); 555 }, 556 557 // internal methods 558 _changeSliderValue: function (value, angle) { 559 var oAngle = this._oriAngle(angle), lAngle = this._limitAngle(angle); 560 if (!this._rangeSlider && !this._showRange) { 561 562 this["_handle" + this._active] = { angle: angle, value: value }; 563 this.options.value = value; 564 this.bar.rsRotate(lAngle); 565 this._updateARIA(value); 566 } 567 else if ((this._active == 1 && oAngle <= this._oriAngle(this._handle2.angle)) || 568 (this._active == 2 && oAngle >= this._oriAngle(this._handle1.angle)) || this._invertRange) { 569 570 this["_handle" + this._active] = { angle: angle, value: value }; 571 this.options.value = this._rangeSlider ? this._handle1.value + "," + this._handle2.value : value; 572 this.bar.rsRotate(lAngle); 573 this._updateARIA(value); 574 575 var dAngle = this._oriAngle(this._handle2.angle) - this._oriAngle(this._handle1.angle), o2 = "1", o3 = "0"; 576 if (dAngle <= 180 && !(dAngle < 0 && dAngle > -180)) o2 = "0", o3 = "1"; 577 this.block2.css("opacity", o2); 578 this.block3.css("opacity", o3); 579 580 (this._active == 1 ? this.block4 : this.block2).rsRotate(lAngle - 180); 581 (this._active == 1 ? this.block1 : this.block3).rsRotate(lAngle); 582 } 583 }, 584 // WAI-ARIA support 585 _updateARIA: function (value) { 586 var min = this.options.min, max = this.options.max; 587 this.bar.children().attr({ "aria-valuenow": value });
588 if (this.options.sliderType == "range") { 589 var handles = this._handles(); 590 handles.eq(0).attr({ "aria-valuemin": min }); 591 handles.eq(1).attr({ "aria-valuemax": max }); 592 593 if (this._active == 1) handles.eq(1).attr({ "aria-valuemin": value }); 594 else handles.eq(0).attr({ "aria-valuemax": value }); 595 } 596 else this.bar.children().attr({ "aria-valuemin": min, "aria-valuemax": max }); 597 }, 598 // Listener for KO binding 599 _checkKO: function () { 600 var _data = this._dataElement().data("bind"); 601 if (typeof _data == "string" && typeof ko == "object") { 602 var _vm = ko.dataFor(this._dataElement()[0]); 603 if (typeof _vm == "undefined") return true; 604 var _all = _data.split(","), _handler; 605 for (var i = 0; i < _all.length; i++) { 606 var d = _all[i].split(":"); 607 if ($.trim(d[0]) == "value") { 608 _handler = $.trim(d[1]); 609 break; 610 } 611 } 612 if (_handler) { 613 this._isKO = true; 614 ko.computed(function () { this.option("value", _vm[_handler]()); }, this); 615 } 616 } 617 }, 618 // Listener for Angular binding 619 _checkAngular: function () { 620 if (typeof angular == "object" && typeof angular.element == "function") { 621 this._ngName = this._dataElement().attr("ng-model"); 622 if (typeof this._ngName == "string") { 623 this._isAngular = true; var that = this; 624 this._scope().$watch(this._ngName, function (newValue, oldValue) { that.option("value", newValue); }); 625 } 626 } 627 }, 628 _scope: function () { 629 return angular.element(this._dataElement()).scope(); 630 }, 631 _getXY: function (e) { 632 if (e.type.indexOf("mouse") == -1) e = (e.originalEvent || e).changedTouches[0]; 633 return { x: e.pageX, y: e.pageY }; 634 }, 635 _getCenterPoint: function () { 636 var offset = this.block.offset(), center; 637 center = { 638 x: offset.left + (this.block.outerWidth() / 2), 639 y: offset.top + (this.block.outerHeight() / 2) 640 }; 641 return center; 642 }, 643 _getAngleValue: function (point, center, isDrag) { 644 var deg = Math.atan2(point.y - center.y, center.x - point.x); 645 var angle = (-deg / (Math.PI / 180)); 646 if (angle < this._start) angle += 360; 647 angle = this._checkAngle(angle, isDrag); 648 return this._processStepByAngle(angle);; 649 }, 650 _checkAngle: function (angle, isDrag) { 651 var o_angle = this._oriAngle(angle), 652 preAngle = this["_handle" + this._active].angle, 653 o_preAngle = this._oriAngle(preAngle); 654 655 if (o_angle > this._end) { 656 if (!isDrag) return preAngle; 657 angle = this._start + (o_preAngle <= this._end - o_preAngle ? 0 : this._end); 658 } 659 else if (isDrag) { 660 var d = this._handleDragDistance; 661 if (isNumber(d)) if (Math.abs(o_angle - o_preAngle) > d) return preAngle; 662 } 663 return angle; 664 }, 665 _processStepByAngle: function (angle) { 666 var value = this._angleToValue(angle); 667 return this._processStepByValue(value); 668 }, 669 _processStepByValue: function (value) { 670 var step = this.options.step, remain, currVal, nextVal, preVal, val, ang; 671 remain = (value - this.options.min) % step; 672 currVal = value - remain; 673 nextVal = this._limitValue(currVal + step); 674 preVal = this._limitValue(currVal - step); 675 676 if (value >= currVal) val = (value - currVal < nextVal - value) ? currVal : nextVal; 677 else val = (currVal - value > value - preVal) ? currVal : preVal; 678 679 val = this._round(val), ang = this._valueToAngle(val); 680 return { value: val, angle: ang }; 681 }, 682 _round: function (val) { 683 var s = this.options.step.toString().split("."); 684 return s[1] ? parseFloat(val.toFixed(s[1].length)) : Math.round(val); 685 }, 686 _oriAngle: function (angle) { 687 var ang = angle - this._start;
688 if (ang < 0) ang += 360; 689 return ang; 690 }, 691 _limitAngle: function (angle) { 692 if (angle > 360 + this._start) angle -= 360; 693 if (angle < this._start) angle += 360; 694 return angle; 695 }, 696 _limitValue: function (value) { 697 if (value < this.options.min) value = this.options.min; 698 if (value > this.options.max) value = this.options.max; 699 return value; 700 }, 701 _angleToValue: function (angle) { 702 var m = this.options, value; 703 value = (this._oriAngle(angle) / this._end) * (m.max - m.min) + m.min; 704 return value; 705 }, 706 _valueToAngle: function (value) { 707 var m = this.options, angle; 708 angle = (((value - m.min) / (m.max - m.min)) * this._end) + this._start; 709 return angle; 710 }, 711 _appendHiddenField: function () { 712 this._hiddenField = this._hiddenField || createElement("input"); 713 this._hiddenField.attr({ 714 "type": "hidden", "name": this._dataElement()[0].id || "" 715 }); 716 this.control.append(this._hiddenField); 717 this._updateHidden(); 718 }, 719 _updateHidden: function () { 720 var val = this.options.value; 721 this._hiddenField.val(val); 722 if (this._isKO || this._isAngular) this._hiddenField.trigger("change"); 723 if (this._isAngular) this._scope()[this._ngName] = val; 724 }, 725 _updateTooltip: function () { 726 if (this.tooltip && !this.tooltip.hasClass("hover")) 727 this.tooltip.html(this._getTooltipValue()); 728 this._updateTooltipPos(); 729 }, 730 _updateTooltipPos: function () { 731 this.tooltip && this.tooltip.css(this._getTooltipPos()); 732 }, 733 _getTooltipPos: function () { 734 var circleShape = this.options.circleShape, pos; 735 if (circleShape == "full" || circleShape == "pie" || circleShape.indexOf("custom") === 0) 736 return { 737 "margin-top": -this.tooltip.outerHeight() / 2, 738 "margin-left": -this.tooltip.outerWidth() / 2 739 }; 740 else if (circleShape.indexOf("half") != -1) { 741 switch (circleShape) { 742 case "half-top": 743 case "half-bottom": 744 pos = { "margin-left": -this.tooltip.outerWidth() / 2 }; break; 745 case "half-left": 746 case "half-right": 747 pos = { "margin-top": -this.tooltip.outerHeight() / 2 }; break; 748 } 749 return pos; 750 } 751 return {}; 752 }, 753 _getTooltipValue: function (isNormal) { 754 if (this._rangeSlider) { 755 var p = this.options.value.split(","); 756 if (isNormal) return p[0] + " - " + p[1]; 757 return this._tooltipValue(p[0], 1) + " - " + this._tooltipValue(p[1], 2); 758 } 759 if (isNormal) return this.options.value; 760 return this._tooltipValue(this.options.value); 761 }, 762 _tooltipValue: function (value, index) { 763 var val = this._raise("tooltipFormat", { value: value, "handle": this._handleArgs(index) }); 764 return (val != null && typeof val !== "boolean") ? val : value; 765 }, 766 _validateStartAngle: function () { 767 var start = this.options.startAngle; 768 start = (isNumber(start) ? parseFloat(start) : 0) % 360; 769 if (start < 0) start += 360; 770 this.options.startAngle = start; 771 return start; 772 }, 773 _validateEndAngle: function () { 774 var end = this.options.endAngle; 775 if (typeof end === "string" && isNumber(end) && (end.charAt(0) === "+" || end.charAt(0) === "-")) { 776 try { end = eval(this.options.startAngle + end.charAt(0) + Math.abs(parseFloat(end))); } 777 catch (e) { console.warn(e); } 778 } 779 end = (isNumber(end) ? parseFloat(end) : 360) % 360; 780 if (end <= this.options.startAngle) end += 360; 781 return end; 782 }, 783 _refreshCircleShape: function () { 784 var circleShape = this.options.circleShape; 785 var circel_shapes = ["half-top", "half-bottom", "half-left", "half-right", 786 "quarter-top-left", "quarter-top-right", "quarter-bottom-right", "quarter-bottom-left", 787 "pie", "custom-half", "custom-quarter"]; 788 var shape_codes = ["h1", "h2", "h3", "h4", "q1", "q2", "q3", "q4", "3/4", "ch", "cq"]; 789 790 if (circel_shapes.indexOf(circleShape) == -1) { 791 var index = shape_codes.indexOf(circleShape); 792 if (index != -1) circleShape = circel_shapes[index]; 793 else if (circleShape == "half") circleShape = "half-top"; 794 else if (circleShape == "quarter") circleShape = "quarter-top-left"; 795 else circleShape = "full"; 796 } 797 this.options.circleShape = circleShape; 798 }, 799 _appendOverlay: function () { 800 var shape = this.options.circleShape; 801 if (shape == "pie") 802 this._checkOverlay(".rs-overlay", 270); 803 else if (shape == "custom-half" || shape == "custom-quarter") { 804 this._checkOverlay(".rs-overlay1", 180); 805 if (shape == "custom-quarter") 806 this._checkOverlay(".rs-overlay2", this._end); 807 } 808 }, 809 _checkOverlay: function (cls, angle) {
810 var overlay = this.container.children(cls); 811 if (overlay.length == 0) { 812 overlay = createElement("div" + cls + " rs-transition rs-bg-color"); 813 this.container.append(overlay); 814 } 815 overlay.rsRotate(this._start + angle); 816 }, 817 _checkDataType: function () { 818 var m = this.options, i, prop, value, props = this._props(); 819 // to check number datatype 820 for (i in props.numberType) { 821 prop = props.numberType[i], value = m[prop]; 822 if (!isNumber(value)) m[prop] = this.defaults[prop]; 823 else m[prop] = parseFloat(value); 824 } 825 // to check input string 826 for (i in props.booleanType) { 827 prop = props.booleanType[i], value = m[prop]; 828 m[prop] = (value == "false") ? false : !!value; 829 } 830 // to check boolean datatype 831 for (i in props.stringType) { 832 prop = props.stringType[i], value = m[prop]; 833 m[prop] = ("" + value).toLowerCase(); 834 } 835 }, 836 _validateSliderType: function () { 837 var type = this.options.sliderType.toLowerCase(); 838 this._rangeSlider = this._showRange = false; 839 if (type == "range") this._rangeSlider = this._showRange = true; 840 else if (type.indexOf("min") != -1) { 841 this._showRange = true; 842 type = "min-range"; 843 } 844 else type = "default"; 845 this.options.sliderType = type; 846 }, 847 _updateStartEnd: function () { 848 var circle = this.options.circleShape; 849 if (circle != "full") { 850 if (circle.indexOf("quarter") != -1) this.options.endAngle = "+90"; 851 else if (circle.indexOf("half") != -1) this.options.endAngle = "+180"; 852 else if (circle == "pie") this.options.endAngle = "+270"; 853 854 if (circle == "quarter-top-left" || circle == "half-top") this.options.startAngle = 0; 855 else if (circle == "quarter-top-right" || circle == "half-right") this.options.startAngle = 90; 856 else if (circle == "quarter-bottom-right" || circle == "half-bottom") this.options.startAngle = 180; 857 else if (circle == "quarter-bottom-left" || circle == "half-left") this.options.startAngle = 270; 858 } 859 }, 860 _validateStartEnd: function () { 861 this._start = this._validateStartAngle(); 862 this._end = this._validateEndAngle(); 863 864 var add = (this._start < this._end) ? 0 : 360; 865 this._end += add - this._start; 866 }, 867 _analyzeModelValue: function () { 868 var val = this.options.value, min = this.options.min, max = this.options.max, last, t; 869 if (val instanceof Array) val = val.toString(); 870 var parts = (typeof val == "string") ? val.split(",") : [val]; 871 872 if (this._rangeSlider) { 873 if (typeof val == "string") { 874 if (parts.length >= 2) t = (isNumber(parts[0]) ? parts[0] : min) + "," + 875 (isNumber(parts[1]) ? parts[1] : max); 876 else t = isNumber(parts[0]) ? min + "," + parts[0] : min + "," + min; 877 } 878 else t = isNumber(val) ? min + "," + val : min + "," + min; 879 } 880 else { 881 if (typeof val == "string") last = parts.pop(), t = isNumber(last) ? parseFloat(last) : min; 882 else t = isNumber(val) ? parseFloat(val) : min; 883 } 884 this.options.value = t; 885 }, 886 _validateModelValue: function () { 887 var val = this.options.value; 888 if (this._rangeSlider) { 889 var parts = val.split(","), val1 = parseFloat(parts[0]), val2 = parseFloat(parts[1]); 890 val1 = this._limitValue(val1); 891 val2 = this._limitValue(val2); 892 if (!this._invertRange) if (val1 > val2) val2 = val1; 893 894 this._handle1 = this._processStepByValue(val1); 895 this._handle2 = this._processStepByValue(val2); 896 this.options.value = this._handle1.value + "," + this._handle2.value; 897 } 898 else { 899 var index = this._showRange ? 2 : (this._active || 1); 900 this["_handle" + index] = this._processStepByValue(this._limitValue(val)); 901 if (this._showRange) this._handle1 = this._handleDefaults(); 902 this.options.value = this["_handle" + index].value; 903 } 904 }, 905 906 // common core methods 907 _isBrowserSupported: function () { 908 var properties = ["borderRadius", "WebkitBorderRadius", "MozBorderRadius", 909 "OBorderRadius", "msBorderRadius", "KhtmlBorderRadius"]; 910 for (var i = 0; i < properties.length; i++) { 911 if (document.body.style[properties[i]] !== undefined) return true; 912 } 913 }, 914 _throwError: function () { 915 return "This browser doesn't support the border-radious property."; 916 }, 917 _checkIE: function () { 918 var ua = window.navigator.userAgent; 919 if (ua.indexOf('MSIE ') >= 0 || ua.indexOf('Trident/') >= 0)
920 this.control.css({ "-ms-touch-action": "none", "touch-action": "none" }); 921 }, 922 _raise: function (event, args) { 923 var o = this.options, fn = o[event], val = true; 924 args = args || { value: o.value }; 925 args["options"] = o; 926 if (fn) { 927 args["type"] = event; 928 if (typeof fn === "string") fn = window[fn]; 929 if ($.isFunction(fn)) { 930 val = fn.call(this, args); 931 val = val === false ? false : val; 932 } 933 } 934 this.control.trigger($.Event ? $.Event(event, args) : event); 935 return val; 936 }, 937 _bind: function (element, _event, handler) { 938 $(element).bind(_event, $proxy(handler, this)); 939 }, 940 _unbind: function (element, _event, handler) { 941 if ($.proxy) $(element).unbind(_event, $.proxy(handler, this)); 942 else $(element).unbind(_event); 943 }, 944 _getInstance: function () { 945 return $data(this._dataElement()[0], pluginName); 946 }, 947 _removeData: function () { 948 var control = this._dataElement()[0]; 949 $.removeData && $.removeData(control, pluginName); 950 if (control.id) delete window[control.id]; 951 }, 952 _destroyControl: function () { 953 if (this._isInputType) this._dataElement().insertAfter(this.control).attr("type", "text"); 954 this.control.empty().removeClass("rs-control").height("").width(""); 955 this._removeAnimation(); 956 this._bindControlEvents("_unbind"); 957 }, 958 959 // methods to dynamic options updation (through option) 960 _updateWidth: function () { 961 this.lastBlock.css("padding", this.options.width); 962 this._refreshHandle(); 963 }, 964 _readOnly: function (bool) { 965 this._isReadOnly = bool; 966 this.container.removeClass("rs-readonly"); 967 if (bool) this.container.addClass("rs-readonly"); 968 }, 969 970 // get & set for the properties 971 _get: function (property) { 972 return this.options[property]; 973 }, 974 _set: function (property, value) { 975 var props = this._props(); 976 if ($.inArray(property, props.numberType) != -1) { // to check number datatype 977 if (!isNumber(value)) return; 978 value = parseFloat(value); 979 } 980 else if ($.inArray(property, props.booleanType) != -1) { // to check boolean datatype 981 value = (value == "false") ? false : !!value; 982 } 983 else if ($.inArray(property, props.stringType) != -1) { // to check input string 984 value = value.toLowerCase(); 985 } 986 987 if (this.options[property] == value) return; 988 this.options[property] = value; 989 switch (property) { 990 case "startAngle": 991 case "endAngle": 992 this._validateStartEnd(); 993 this._updateSeperator(); 994 this._appendOverlay(); 995 case "min": 996 case "max": 997 case "step": 998 case "value": 999 this._analyzeModelValue(); 1000 this._validateModelValue(); 1001 this._setValue(); 1002 this._updatePre(); 1003 this._updateHidden(); 1004 this._updateTooltip(); 1005 break; 1006 case "radius": 1007 this._setRadius(); 1008 this._updateTooltipPos(); 1009 break; 1010 case "width": 1011 this._removeAnimation(); 1012 this._updateWidth(); 1013 this._updateTooltipPos(); 1014 this._addAnimation(); 1015 this._refreshSeperator(); 1016 break; 1017 case "handleSize": 1018 this._refreshHandle(); 1019 break; 1020 case "handleShape": 1021 this._setHandleShape(); 1022 break; 1023 case "animation": 1024 this._setAnimation(); 1025 break; 1026 case "showTooltip": 1027 this.options.showTooltip ? this._appendTooltip() : this._removeTooltip(); 1028 break; 1029 case "editableTooltip": 1030 this._tooltipEditable(); 1031 this._updateTooltipPos(); 1032 break; 1033 case "disabled": 1034 this.options.disabled ? this.disable() : this.enable(); 1035 break; 1036 case "readOnly": 1037 this.options.readOnly ? this._readOnly(true) : (!this.options.disabled && this._readOnly(false)); 1038 break; 1039 case "mouseScrollAction": 1040 this._bindScrollEvents(this.options.mouseScrollAction ? "_bind" : "_unbind"); 1041 break; 1042 case "lineCap": 1043 this._refreshSeperator(); 1044 break; 1045 case "circleShape": 1046 this._refreshCircleShape(); 1047 if (this.options.circleShape == "full") { 1048 this.options.startAngle = 0; 1049 this.options.endAngle = "+360"; 1050 } 1051 case "sliderType": 1052 this._destroyControl(); 1053 this._onInit(); 1054 break; 1055 } 1056 return this; 1057 }, 1058 1059 // public methods 1060 option: function (property, value) { 1061 if (!this._getInstance() || !this._isBrowserSupport) return; 1062 if ($isPlainObject(property)) { 1063 if (property["min"] !== undefined || property["max"] !== undefined) { 1064 if (property["min"] !== undefined) { 1065 this.options.min = property["min"]; 1066 delete property["min"]; 1067 } 1068 if (property["max"] !== undefined) { 1069 this.options.max = property["max"]; 1070 delete property["max"]; 1071 } 1072 if (property["value"] == undefined) { 1073 this._set("value", this.options.value); 1074 } 1075 } 1076 for (var prop in property) { 1077 this._set(prop, property[prop]); 1078 } 1079 } 1080 else if (property && typeof property == "string") { 1081 if (value === undefined) return this._get(property); 1082 this._set(property, value); 1083 } 1084 return this; 1085 }, 1086 getValue: function (index) { 1087 if (this.options.sliderType == "range" && isNumber(index)) { 1088 var i = parseFloat(index); 1089 if (i == 1 || i == 2) 1090 return this["_handle" + i].value; 1091 } 1092 return this._get("value"); 1093 }, 1094 setValue: function (value, index) { 1095 if (isNumber(value)) { 1096 if (isNumber(index)) { 1097 if (this.options.sliderType == "range") { 1098 var i = parseFloat(index), val = parseFloat(value); 1099 if (i == 1) value = val + "," + this._handle2.value; 1100 else if (i == 2) value = this._handle1.value + "," + val; 1101 } 1102 else if (this.options.sliderType == "default") this._active = index; 1103 } 1104 this._set("value", value); 1105 } 1106 }, 1107 disable: function () { 1108 this.options.disabled = true; 1109 this.container.addClass("rs-disabled"); 1110 this._readOnly(true); 1111 }, 1112 enable: function () { 1113 this.options.disabled = false; 1114 this.container.removeClass("rs-disabled"); 1115 if (!this.options.readOnly) this._readOnly(false); 1116 }, 1117 destroy: function () { 1118 if (!this._getInstance()) return; 1119 this._destroyControl(); 1120 this._removeData(); 1121 if (this._isInputType) this.control.remove(); 1122 } 1123 }; 1124 1125 $.fn.rsRotate = function (degree) { 1126 return setTransform(this, degree); 1127 } 1128 1129 if (typeof $.fn.outerHeight == "undefined") { 1130 $.fn.outerHeight = function () { return this[0].offsetHeight; } 1131 $.fn.outerWidth = function () { return this[0].offsetWidth; } 1132 } 1133 if (typeof $.fn.hasClass === "undefined") { 1134 $.fn.hasClass = function (name) { 1135 return this[0].className.split(" ").indexOf(name) !== -1; 1136 } 1137 } 1138 if (typeof $.fn.offset === "undefined") { 1139 $.fn.offset = function () { 1140 return { left: this[0].offsetLeft, top: this[0].offsetTop }; 1141 } 1142 } 1143 1144 function $proxy(fn, that) { 1145 if (typeof $.proxy === "function") return $.proxy(fn, that); 1146 return function (e) { fn.call(that, e); }; 1147 } 1148 function $data(ele, name, data) { 1149 if (typeof $.data === "function") return $.data(ele, name, data); 1150 else if (!data) return $(ele).hasClass("rs-control"); 1151 } 1152 function $isPlainObject(obj) { 1153 if (typeof $.isPlainObject === "function") return $.isPlainObject(obj); 1154 else { 1155 var str = JSON.stringify(obj); 1156 return typeof obj === "object" && obj.length === undefined && 1157 str.length > 2 && str.substr(0, 1) === "{" && str.substr(str.length - 1) === "}"; 1158 } 1159 } 1160 1161 //core functions 1162 function isNumber(number) { 1163 number = parseFloat(number); 1164 return typeof number === "number" && !isNaN(number); 1165 } 1166 function createElement(tag) { 1167 var t = tag.split('.'); 1168 return $(document.createElement(t[0])).addClass(t[1] || ""); 1169 } 1170 function getdistance(p1, p2) { 1171 return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); 1172 } 1173 function setTransform(control, value) { 1174 control.css('-webkit-transform', "rotate(" + value + "deg)"); 1175 control.css('-moz-transform', "rotate(" + value + "deg)"); 1176 control.css('-ms-transform', "rotate(" + value + "deg)"); 1177 control.css('-o-transform', "rotate(" + value + "deg)"); 1178 control.css('transform', "rotate(" + value + "deg)"); 1179 return control; 1180 } 1181 1182 // The plugin constructor 1183 function RoundSlider(control, options) { 1184 if (control.id) window[control.id] = this; 1185 this.control = $(control); 1186 1187 // the options value holds the updated defaults value 1188 this.options = $.extend({}, this.defaults, options); 1189 if (this._raise("beforeCreate") !== false) { 1190 this._init(); 1191 this._raise("create"); 1192 } 1193 else this._removeData(); 1194 } 1195 1196 // The plugin wrapper, prevents multiple instantiations 1197 function CreateRoundSlider(options, args) { 1198 1199 for (var i = 0; i < this.length; i++) { 1200 var that = this[i], instance = $data(that, pluginName); 1201 if (!instance) { 1202 $data(that, pluginName, new RoundSlider(that, options)); 1203 } 1204 else if ($isPlainObject(options)) { 1205 if (typeof instance.option === "function") instance.option(options);
1206 else if (that.id && window[that.id] && typeof window[that.id].option === "function") { 1207 window[that.id].option(options); 1208 } 1209 } 1210 else if (typeof options === "string") { 1211 if (typeof instance[options] === "function") { 1212 if ((options === "option" || options.indexOf("get") === 0) && args[2] === undefined) { 1213 return instance[options](args[1]); 1214 } 1215 instance[options](args[1], args[2]); 1216 } 1217 } 1218 } 1219 return this; 1220 } 1221 1222 $.fn[pluginName].prototype = RoundSlider.prototype; 1223 1224})(jQuery, window);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.