1/* ======================================================================== 2 * bootstrap-switch - v3.3.2 3 * http://www.bootstrap-switch.org 4 * ======================================================================== 5 * Copyright 2012-2013 Mattia Larentis 6 * 7 * ======================================================================== 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * ======================================================================== 20 */ 21 22(function() { 23 var slice = [].slice; 24 25 (function($, window) { 26 "use strict"; 27 var BootstrapSwitch; 28 BootstrapSwitch = (function() { 29 function BootstrapSwitch(element, options) { 30 if (options == null) { 31 options = {}; 32 } 33 this.$element = $(element); 34 this.options = $.extend({}, $.fn.bootstrapSwitch.defaults, { 35 state: this.$element.is(":checked"), 36 size: this.$element.data("size"), 37 animate: this.$element.data("animate"), 38 disabled: this.$element.is(":disabled"), 39 readonly: this.$element.is("[readonly]"), 40 indeterminate: this.$element.data("indeterminate"), 41 inverse: this.$element.data("inverse"), 42 radioAllOff: this.$element.data("radio-all-off"), 43 onColor: this.$element.data("on-color"), 44 offColor: this.$element.data("off-color"), 45 onText: this.$element.data("on-text"), 46 offText: this.$element.data("off-text"), 47 labelText: this.$element.data("label-text"), 48 handleWidth: this.$element.data("handle-width"), 49 labelWidth: this.$element.data("label-width"), 50 baseClass: this.$element.data("base-class"), 51 wrapperClass: this.$element.data("wrapper-class") 52 }, options); 53 this.prevOptions = {}; 54 this.$wrapper = $("<div>", { 55 "class": (function(_this) { 56 return function() { 57 var classes; 58 classes = ["" + _this.options.baseClass].concat(_this._getClasses(_this.options.wrapperClass)); 59 classes.push(_this.options.state ? _this.options.baseClass + "-on" : _this.options.baseClass + "-off"); 60 if (_this.options.size != null) { 61 classes.push(_this.options.baseClass + "-" + _this.options.size); 62 } 63 if (_this.options.disabled) { 64 classes.push(_this.options.baseClass + "-disabled"); 65 } 66 if (_this.options.readonly) { 67 classes.push(_this.options.baseClass + "-readonly"); 68 } 69 if (_this.options.indeterminate) { 70 classes.push(_this.options.baseClass + "-indeterminate"); 71 } 72 if (_this.options.inverse) { 73 classes.push(_this.options.baseClass + "-inverse"); 74 } 75 if (_this.$element.attr("id")) { 76 classes.push(_this.options.baseClass + "-id-" + (_this.$element.attr("id"))); 77 } 78 return classes.join(" "); 79 }; 80 })(this)() 81 }); 82 this.$container = $("<div>", { 83 "class": this.options.baseClass + "-container" 84 }); 85 this.$on = $("<span>", { 86 html: this.options.onText, 87 "class": this.options.baseClass + "-handle-on " + this.options.baseClass + "-" + this.options.onColor 88 }); 89 this.$off = $("<span>", { 90 html: this.options.offText, 91 "class": this.options.baseClass + "-handle-off " + this.options.baseClass + "-" + this.options.offColor 92 }); 93 this.$label = $("<span>", { 94 html: this.options.labelText, 95 "class": this.options.baseClass + "-label" 96 }); 97 this.$element.on("init.bootstrapSwitch", (function(_this) { 98 return function() { 99 return _this.options.onInit.apply(element, arguments); 100 }; 101 })(this)); 102 this.$element.on("switchChange.bootstrapSwitch", (function(_this) { 103 return function(e) { 104 if (false === _this.options.onSwitchChange.apply(element, arguments)) { 105 if (_this.$element.is(":radio")) { 106 return $("[name='" + (_this.$element.attr('name')) + "']").trigger("previousState.bootstrapSwitch", true); 107 } else { 108 return _this.$element.trigger("previousState.bootstrapSwitch", true); 109 } 110 } 111 }; 112 })(this)); 113 this.$container = this.$element.wrap(this.$container).parent(); 114 this.$wrapper = this.$container.wrap(this.$wrapper).parent(); 115 this.$element.before(this.options.inverse ? this.$off : this.$on).before(this.$label).before(this.options.inverse ? this.$on : this.$off); 116 if (this.options.indeterminate) { 117 this.$element.prop("indeterminate", true); 118 } 119 this._init(); 120 this._elementHandlers(); 121 this._handleHandlers(); 122 this._labelHandlers(); 123 this._formHandler(); 124 this._externalLabelHandler(); 125 this.$element.trigger("init.bootstrapSwitch", this.options.state); 126 } 127 128 BootstrapSwitch.prototype._constructor = BootstrapSwitch; 129 130 BootstrapSwitch.prototype.setPrevOptions = function() { 131 return this.prevOptions = $.extend(true, {}, this.options); 132 }; 133 134 BootstrapSwitch.prototype.state = function(value, skip) { 135 if (typeof value === "undefined") { 136 return this.options.state; 137 } 138 if (this.options.disabled || this.options.readonly) { 139 return this.$element; 140 } 141 if (this.options.state && !this.options.radioAllOff && this.$element.is(":radio")) { 142 return this.$element; 143 } 144 if (this.$element.is(":radio")) { 145 $("[name='" + (this.$element.attr('name')) + "']").trigger("setPreviousOptions.bootstrapSwitch"); 146 } else { 147 this.$element.trigger("setPreviousOptions.bootstrapSwitch"); 148 } 149 if (this.options.indeterminate) { 150 this.indeterminate(false); 151 } 152 value = !!value; 153 this.$element.prop("checked", value).trigger("change.bootstrapSwitch", skip); 154 return this.$element; 155 }; 156 157 BootstrapSwitch.prototype.toggleState = function(skip) { 158 if (this.options.disabled || this.options.readonly) { 159 return this.$element; 160 } 161 if (this.options.indeterminate) { 162 this.indeterminate(false); 163 return this.state(true); 164 } else { 165 return this.$element.prop("checked", !this.options.state).trigger("change.bootstrapSwitch", skip); 166 } 167 }; 168 169 BootstrapSwitch.prototype.size = function(value) { 170 if (typeof value === "undefined") { 171 return this.options.size; 172 } 173 if (this.options.size != null) { 174 this.$wrapper.removeClass(this.options.baseClass + "-" + this.options.size); 175 } 176 if (value) { 177 this.$wrapper.addClass(this.options.baseClass + "-" + value); 178 } 179 this._width(); 180 this._containerPosition(); 181 this.options.size = value; 182 return this.$element; 183 }; 184 185 BootstrapSwitch.prototype.animate = function(value) { 186 if (typeof value === "undefined") { 187 return this.options.animate; 188 } 189 value = !!value; 190 if (value === this.options.animate) { 191 return this.$element; 192 } 193 return this.toggleAnimate(); 194 }; 195 196 BootstrapSwitch.prototype.toggleAnimate = function() { 197 this.options.animate = !this.options.animate; 198 this.$wrapper.toggleClass(this.options.baseClass + "-animate"); 199 return this.$element; 200 }; 201 202 BootstrapSwitch.prototype.disabled = function(value) { 203 if (typeof value === "undefined") { 204 return this.options.disabled; 205 } 206 value = !!value; 207 if (value === this.options.disabled) { 208 return this.$element; 209 } 210 return this.toggleDisabled(); 211 }; 212 213 BootstrapSwitch.prototype.toggleDisabled = function() { 214 this.options.disabled = !this.options.disabled; 215 this.$element.prop("disabled", this.options.disabled); 216 this.$wrapper.toggleClass(this.options.baseClass + "-disabled"); 217 return this.$element; 218 }; 219 220 BootstrapSwitch.prototype.readonly = function(value) { 221 if (typeof value === "undefined") { 222 return this.options.readonly; 223 } 224 value = !!value; 225 if (value === this.options.readonly) { 226 return this.$element; 227 } 228 return this.toggleReadonly(); 229 }; 230 231 BootstrapSwitch.prototype.toggleReadonly = function() { 232 this.options.readonly = !this.options.readonly; 233 this.$element.prop("readonly", this.options.readonly); 234 this.$wrapper.toggleClass(this.options.baseClass + "-readonly"); 235 return this.$element; 236 }; 237 238 BootstrapSwitch.prototype.indeterminate = function(value) { 239 if (typeof value === "undefined") { 240 return this.options.indeterminate; 241 } 242 value = !!value; 243 if (value === this.options.indeterminate) { 244 return this.$element; 245 } 246 return this.toggleIndeterminate(); 247 }; 248 249 BootstrapSwitch.prototype.toggleIndeterminate = function() { 250 this.options.indeterminate = !this.options.indeterminate; 251 this.$element.prop("indeterminate", this.options.indeterminate); 252 this.$wrapper.toggleClass(this.options.baseClass + "-indeterminate"); 253 this._containerPosition(); 254 return this.$element; 255 }; 256 257 BootstrapSwitch.prototype.inverse = function(value) { 258 if (typeof value === "undefined") { 259 return this.options.inverse; 260 } 261 value = !!value; 262 if (value === this.options.inverse) { 263 return this.$element; 264 } 265 return this.toggleInverse(); 266 }; 267 268 BootstrapSwitch.prototype.toggleInverse = function() { 269 var $off, $on; 270 this.$wrapper.toggleClass(this.options.baseClass + "-inverse"); 271 $on = this.$on.clone(true); 272 $off = this.$off.clone(true); 273 this.$on.replaceWith($off); 274 this.$off.replaceWith($on); 275 this.$on = $off; 276 this.$off = $on; 277 this.options.inverse = !this.options.inverse; 278 return this.$element; 279 }; 280 281 BootstrapSwitch.prototype.onColor = function(value) { 282 var color; 283 color = this.options.onColor; 284 if (typeof value === "undefined") { 285 return color; 286 } 287 if (color != null) { 288 this.$on.removeClass(this.options.baseClass + "-" + color); 289 } 290 this.$on.addClass(this.options.baseClass + "-" + value); 291 this.options.onColor = value; 292 return this.$element; 293 }; 294 295 BootstrapSwitch.prototype.offColor = function(value) { 296 var color; 297 color = this.options.offColor; 298 if (typeof value === "undefined") { 299 return color; 300 } 301 if (color != null) { 302 this.$off.removeClass(this.options.baseClass + "-" + color); 303 } 304 this.$off.addClass(this.options.baseClass + "-" + value); 305 this.options.offColor = value; 306 return this.$element; 307 }; 308 309 BootstrapSwitch.prototype.onText = function(value) { 310 if (typeof value === "undefined") { 311 return this.options.onText; 312 } 313 this.$on.html(value); 314 this._width(); 315 this._containerPosition(); 316 this.options.onText = value; 317 return this.$element; 318 }; 319 320 BootstrapSwitch.prototype.offText = function(value) { 321 if (typeof value === "undefined") { 322 return this.options.offText; 323 } 324 this.$off.html(value); 325 this._width(); 326 this._containerPosition(); 327 this.options.offText = value; 328 return this.$element; 329 }; 330 331 BootstrapSwitch.prototype.labelText = function(value) { 332 if (typeof value === "undefined") { 333 return this.options.labelText; 334 } 335 this.$label.html(value); 336 this._width(); 337 this.options.labelText = value; 338 return this.$element; 339 }; 340 341 BootstrapSwitch.prototype.handleWidth = function(value) { 342 if (typeof value === "undefined") { 343 return this.options.handleWidth; 344 } 345 this.options.handleWidth = value; 346 this._width(); 347 this._containerPosition(); 348 return this.$element; 349 }; 350 351 BootstrapSwitch.prototype.labelWidth = function(value) { 352 if (typeof value === "undefined") { 353 return this.options.labelWidth; 354 } 355 this.options.labelWidth = value; 356 this._width(); 357 this._containerPosition(); 358 return this.$element; 359 }; 360 361 BootstrapSwitch.prototype.baseClass = function(value) { 362 return this.options.baseClass; 363 }; 364 365 BootstrapSwitch.prototype.wrapperClass = function(value) { 366 if (typeof value === "undefined") { 367 return this.options.wrapperClass; 368 } 369 if (!value) { 370 value = $.fn.bootstrapSwitch.defaults.wrapperClass; 371 } 372 this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(" ")); 373 this.$wrapper.addClass(this._getClasses(value).join(" ")); 374 this.options.wrapperClass = value; 375 return this.$element; 376 }; 377 378 BootstrapSwitch.prototype.radioAllOff = function(value) { 379 if (typeof value === "undefined") { 380 return this.options.radioAllOff; 381 } 382 value = !!value; 383 if (value === this.options.radioAllOff) { 384 return this.$element; 385 } 386 this.options.radioAllOff = value; 387 return this.$element; 388 }; 389 390 BootstrapSwitch.prototype.onInit = function(value) { 391 if (typeof value === "undefined") { 392 return this.options.onInit; 393 } 394 if (!value) { 395 value = $.fn.bootstrapSwitch.defaults.onInit; 396 } 397 this.options.onInit = value; 398 return this.$element; 399 }; 400 401 BootstrapSwitch.prototype.onSwitchChange = function(value) { 402 if (typeof value === "undefined") { 403 return this.options.onSwitchChange; 404 } 405 if (!value) { 406 value = $.fn.bootstrapSwitch.defaults.onSwitchChange; 407 } 408 this.options.onSwitchChange = value; 409 return this.$element; 410 }; 411 412 BootstrapSwitch.prototype.destroy = function() { 413 var $form; 414 $form = this.$element.closest("form"); 415 if ($form.length) { 416 $form.off("reset.bootstrapSwitch").removeData("bootstrap-switch"); 417 } 418 this.$container.children().not(this.$element).remove(); 419 this.$element.unwrap().unwrap().off(".bootstrapSwitch").removeData("bootstrap-switch"); 420 return this.$element; 421 }; 422 423 BootstrapSwitch.prototype._width = function() { 424 var $handles, handleWidth; 425 $handles = this.$on.add(this.$off); 426 $handles.add(this.$label).css("width", ""); 427 handleWidth = this.options.handleWidth === "auto" ? Math.max(this.$on.width(), this.$off.width()) : this.options.handleWidth; 428 $handles.width(handleWidth);
429 this.$label.width((function(_this) { 430 return function(index, width) { 431 if (_this.options.labelWidth !== "auto") { 432 return _this.options.labelWidth; 433 } 434 if (width < handleWidth) { 435 return handleWidth; 436 } else { 437 return width; 438 } 439 }; 440 })(this)); 441 this._handleWidth = this.$on.outerWidth(); 442 this._labelWidth = this.$label.outerWidth(); 443 this.$container.width((this._handleWidth * 2) + this._labelWidth); 444 return this.$wrapper.width(this._handleWidth + this._labelWidth); 445 }; 446 447 BootstrapSwitch.prototype._containerPosition = function(state, callback) { 448 if (state == null) { 449 state = this.options.state; 450 } 451 this.$container.css("margin-left", (function(_this) { 452 return function() { 453 var values; 454 values = [0, "-" + _this._handleWidth + "px"]; 455 if (_this.options.indeterminate) { 456 return "-" + (_this._handleWidth / 2) + "px"; 457 } 458 if (state) { 459 if (_this.options.inverse) { 460 return values[1]; 461 } else { 462 return values[0]; 463 } 464 } else { 465 if (_this.options.inverse) { 466 return values[0]; 467 } else { 468 return values[1]; 469 } 470 } 471 }; 472 })(this)); 473 if (!callback) { 474 return; 475 } 476 return setTimeout(function() { 477 return callback(); 478 }, 50); 479 }; 480 481 BootstrapSwitch.prototype._init = function() { 482 var init, initInterval; 483 init = (function(_this) { 484 return function() { 485 _this.setPrevOptions(); 486 _this._width(); 487 return _this._containerPosition(null, function() { 488 if (_this.options.animate) { 489 return _this.$wrapper.addClass(_this.options.baseClass + "-animate"); 490 } 491 }); 492 }; 493 })(this); 494 if (this.$wrapper.is(":visible")) { 495 return init(); 496 } 497 return initInterval = window.setInterval((function(_this) { 498 return function() { 499 if (_this.$wrapper.is(":visible")) { 500 init(); 501 return window.clearInterval(initInterval); 502 } 503 }; 504 })(this), 50); 505 }; 506 507 BootstrapSwitch.prototype._elementHandlers = function() { 508 return this.$element.on({ 509 "setPreviousOptions.bootstrapSwitch": (function(_this) { 510 return function(e) { 511 return _this.setPrevOptions(); 512 }; 513 })(this), 514 "previousState.bootstrapSwitch": (function(_this) { 515 return function(e) { 516 _this.options = _this.prevOptions; 517 if (_this.options.indeterminate) { 518 _this.$wrapper.addClass(_this.options.baseClass + "-indeterminate"); 519 } 520 return _this.$element.prop("checked", _this.options.state).trigger("change.bootstrapSwitch", true); 521 }; 522 })(this), 523 "change.bootstrapSwitch": (function(_this) { 524 return function(e, skip) { 525 var state; 526 e.preventDefault(); 527 e.stopImmediatePropagation(); 528 state = _this.$element.is(":checked"); 529 _this._containerPosition(state); 530 if (state === _this.options.state) { 531 return; 532 } 533 _this.options.state = state; 534 _this.$wrapper.toggleClass(_this.options.baseClass + "-off").toggleClass(_this.options.baseClass + "-on"); 535 if (!skip) { 536 if (_this.$element.is(":radio")) { 537 $("[name='" + (_this.$element.attr('name')) + "']").not(_this.$element).prop("checked", false).trigger("change.bootstrapSwitch", true); 538 } 539 return _this.$element.trigger("switchChange.bootstrapSwitch", [state]); 540 } 541 }; 542 })(this), 543 "focus.bootstrapSwitch": (function(_this) { 544 return function(e) { 545 e.preventDefault(); 546 return _this.$wrapper.addClass(_this.options.baseClass + "-focused"); 547 }; 548 })(this), 549 "blur.bootstrapSwitch": (function(_this) { 550 return function(e) { 551 e.preventDefault(); 552 return _this.$wrapper.removeClass(_this.options.baseClass + "-focused"); 553 }; 554 })(this), 555 "keydown.bootstrapSwitch": (function(_this) { 556 return function(e) { 557 if (!e.which || _this.options.disabled || _this.options.readonly) { 558 return; 559 } 560 switch (e.which) { 561 case 37: 562 e.preventDefault(); 563 e.stopImmediatePropagation(); 564 return _this.state(false); 565 case 39: 566 e.preventDefault(); 567 e.stopImmediatePropagation(); 568 return _this.state(true); 569 } 570 }; 571 })(this) 572 }); 573 }; 574 575 BootstrapSwitch.prototype._handleHandlers = function() { 576 this.$on.on("click.bootstrapSwitch", (function(_this) { 577 return function(event) { 578 event.preventDefault(); 579 event.stopPropagation(); 580 _this.state(false); 581 return _this.$element.trigger("focus.bootstrapSwitch"); 582 }; 583 })(this)); 584 return this.$off.on("click.bootstrapSwitch", (function(_this) { 585 return function(event) { 586 event.preventDefault(); 587 event.stopPropagation(); 588 _this.state(true); 589 return _this.$element.trigger("focus.bootstrapSwitch"); 590 }; 591 })(this)); 592 }; 593 594 BootstrapSwitch.prototype._labelHandlers = function() { 595 return this.$label.on({ 596 "click": function(e) { 597 return e.stopPropagation(); 598 }, 599 "mousedown.bootstrapSwitch touchstart.bootstrapSwitch": (function(_this) { 600 return function(e) { 601 if (_this._dragStart || _this.options.disabled || _this.options.readonly) { 602 return; 603 } 604 e.preventDefault(); 605 e.stopPropagation(); 606 _this._dragStart = (e.pageX || e.originalEvent.touches[0].pageX) - parseInt(_this.$container.css("margin-left"), 10); 607 if (_this.options.animate) { 608 _this.$wrapper.removeClass(_this.options.baseClass + "-animate"); 609 } 610 return _this.$element.trigger("focus.bootstrapSwitch"); 611 }; 612 })(this), 613 "mousemove.bootstrapSwitch touchmove.bootstrapSwitch": (function(_this) { 614 return function(e) { 615 var difference; 616 if (_this._dragStart == null) { 617 return; 618 } 619 e.preventDefault(); 620 difference = (e.pageX || e.originalEvent.touches[0].pageX) - _this._dragStart; 621 if (difference < -_this._handleWidth || difference > 0) { 622 return; 623 } 624 _this._dragEnd = difference; 625 return _this.$container.css("margin-left", _this._dragEnd + "px"); 626 }; 627 })(this), 628 "mouseup.bootstrapSwitch touchend.bootstrapSwitch": (function(_this) { 629 return function(e) { 630 var state; 631 if (!_this._dragStart) { 632 return; 633 } 634 e.preventDefault(); 635 if (_this.options.animate) { 636 _this.$wrapper.addClass(_this.options.baseClass + "-animate"); 637 } 638 if (_this._dragEnd) { 639 state = _this._dragEnd > -(_this._handleWidth / 2); 640 _this._dragEnd = false;
641 _this.state(_this.options.inverse ? !state : state); 642 } else { 643 _this.state(!_this.options.state); 644 } 645 return _this._dragStart = false; 646 }; 647 })(this), 648 "mouseleave.bootstrapSwitch": (function(_this) { 649 return function(e) { 650 return _this.$label.trigger("mouseup.bootstrapSwitch"); 651 }; 652 })(this) 653 }); 654 }; 655 656 BootstrapSwitch.prototype._externalLabelHandler = function() { 657 var $externalLabel; 658 $externalLabel = this.$element.closest("label"); 659 return $externalLabel.on("click", (function(_this) { 660 return function(event) { 661 event.preventDefault(); 662 event.stopImmediatePropagation(); 663 if (event.target === $externalLabel[0]) { 664 return _this.toggleState(); 665 } 666 }; 667 })(this)); 668 }; 669 670 BootstrapSwitch.prototype._formHandler = function() { 671 var $form; 672 $form = this.$element.closest("form"); 673 if ($form.data("bootstrap-switch")) { 674 return; 675 } 676 return $form.on("reset.bootstrapSwitch", function() { 677 return window.setTimeout(function() { 678 return $form.find("input").filter(function() { 679 return $(this).data("bootstrap-switch"); 680 }).each(function() { 681 return $(this).bootstrapSwitch("state", this.checked); 682 }); 683 }, 1); 684 }).data("bootstrap-switch", true); 685 }; 686 687 BootstrapSwitch.prototype._getClasses = function(classes) { 688 var c, cls, i, len; 689 if (!$.isArray(classes)) { 690 return [this.options.baseClass + "-" + classes]; 691 } 692 cls = []; 693 for (i = 0, len = classes.length; i < len; i++) { 694 c = classes[i]; 695 cls.push(this.options.baseClass + "-" + c); 696 } 697 return cls; 698 }; 699 700 return BootstrapSwitch; 701 702 })(); 703 $.fn.bootstrapSwitch = function() { 704 var args, option, ret; 705 option = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; 706 ret = this; 707 this.each(function() { 708 var $this, data; 709 $this = $(this); 710 data = $this.data("bootstrap-switch"); 711 if (!data) { 712 $this.data("bootstrap-switch", data = new BootstrapSwitch(this, option)); 713 } 714 if (typeof option === "string") { 715 return ret = data[option].apply(data, args); 716 } 717 }); 718 return ret; 719 }; 720 $.fn.bootstrapSwitch.Constructor = BootstrapSwitch; 721 return $.fn.bootstrapSwitch.defaults = { 722 state: true, 723 size: null, 724 animate: true, 725 disabled: false, 726 readonly: false, 727 indeterminate: false, 728 inverse: false, 729 radioAllOff: false, 730 onColor: "primary", 731 offColor: "default", 732 onText: "ON", 733 offText: "OFF", 734 labelText: " ", 735 handleWidth: "auto", 736 labelWidth: "auto", 737 baseClass: "bootstrap-switch", 738 wrapperClass: "wrapper", 739 onInit: function() {}, 740 onSwitchChange: function() {} 741 }; 742 })(window.jQuery, window); 743 744}).call(this); 745 746 747 748 749// 750/* 751 * bootstrap-tagsinput v0.8.0 752 * 753 */ 754 755(function ($) { 756 "use strict"; 757 758 var defaultOptions = { 759 tagClass: function(item) { 760 return 'label label-info'; 761 }, 762 focusClass: 'focus', 763 itemValue: function(item) { 764 return item ? item.toString() : item; 765 }, 766 itemText: function(item) { 767 return this.itemValue(item); 768 }, 769 itemTitle: function(item) { 770 return null; 771 }, 772 freeInput: true, 773 addOnBlur: true, 774 maxTags: undefined, 775 maxChars: undefined, 776 confirmKeys: [13, 44], 777 delimiter: ',', 778 delimiterRegex: null, 779 cancelConfirmKeysOnEmpty: false, 780 onTagExists: function(item, $tag) { 781 $tag.hide().fadeIn(); 782 }, 783 trimValue: false, 784 allowDuplicates: false, 785 triggerChange: true 786 }; 787 788 /** 789 * Constructor function 790 */ 791 function TagsInput(element, options) { 792 this.isInit = true; 793 this.itemsArray = []; 794 795 this.$element = $(element); 796 this.$element.hide(); 797 798 this.isSelect = (element.tagName === 'SELECT'); 799 this.multiple = (this.isSelect && element.hasAttribute('multiple')); 800 this.objectItems = options && options.itemValue; 801 this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : ''; 802 this.inputSize = Math.max(1, this.placeholderText.length); 803
vendor: 4,192 bytes, lines 804-915
804 this.$container = $('<div class="bootstrap-tagsinput"></div>'); 805 this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container); 806 807 this.$element.before(this.$container); 808 809 this.build(options); 810 this.isInit = false; 811 } 812 813 TagsInput.prototype = { 814 constructor: TagsInput, 815 816 /** 817 * Adds the given item as a new tag. Pass true to dontPushVal to prevent 818 * updating the elements val() 819 */ 820 add: function(item, dontPushVal, options) { 821 var self = this; 822 823 if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags) 824 return; 825 826 // Ignore falsey values, except false 827 if (item !== false && !item) 828 return; 829 830 // Trim value 831 if (typeof item === "string" && self.options.trimValue) { 832 item = $.trim(item); 833 } 834 835 // Throw an error when trying to add an object while the itemValue option was not set 836 if (typeof item === "object" && !self.objectItems) 837 throw("Can't add objects when itemValue option is not set"); 838 839 // Ignore strings only containg whitespace 840 if (item.toString().match(/^\s*$/)) 841 return; 842 843 // If SELECT but not multiple, remove current tag 844 if (self.isSelect && !self.multiple && self.itemsArray.length > 0) 845 self.remove(self.itemsArray[0]); 846 847 if (typeof item === "string" && this.$element[0].tagName === 'INPUT') { 848 var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter; 849 var items = item.split(delimiter); 850 if (items.length > 1) { 851 for (var i = 0; i < items.length; i++) { 852 this.add(items[i], true); 853 } 854 855 if (!dontPushVal) 856 self.pushVal(self.options.triggerChange); 857 return; 858 } 859 } 860 861 var itemValue = self.options.itemValue(item), 862 itemText = self.options.itemText(item), 863 tagClass = self.options.tagClass(item), 864 itemTitle = self.options.itemTitle(item); 865 866 // Ignore items allready added 867 var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0]; 868 if (existing && !self.options.allowDuplicates) { 869 // Invoke onTagExists 870 if (self.options.onTagExists) { 871 var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; }); 872 self.options.onTagExists(item, $existingTag); 873 } 874 return; 875 } 876 877 // if length greater than limit 878 if (self.items().toString().length + item.length + 1 > self.options.maxInputLength) 879 return; 880 881 // raise beforeItemAdd arg 882 var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options}); 883 self.$element.trigger(beforeItemAddEvent); 884 if (beforeItemAddEvent.cancel) 885 return; 886 887 // register item in internal array and map 888 self.itemsArray.push(item); 889 890 // add a tag element 891 892 var $tag = $('<span class="tag ' + htmlEncode(tagClass) + (itemTitle !== null ? ('" title="' + itemTitle) : '') + '">' + htmlEncode(itemText) + '<span data-role="remove"></span></span>'); 893 $tag.data('item', item); 894 self.findInputWrapper().before($tag); 895 $tag.after(' '); 896 897 // Check to see if the tag exists in its raw or uri-encoded form 898 var optionExists = ( 899 $('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element).length || 900 $('option[value="' + htmlEncode(itemValue) + '"]', self.$element).length 901 ); 902 903 // add <option /> if item represents a value not present in one of the <select />'s options 904 if (self.isSelect && !optionExists) { 905 var $option = $('<option selected>' + htmlEncode(itemText) + '</option>'); 906 $option.data('item', item); 907 $option.attr('value', itemValue); 908 self.$element.append($option); 909 } 910 911 if (!dontPushVal) 912 self.pushVal(self.options.triggerChange); 913 914 // Add class when reached maxTags 915 if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInput
vendor: 8,294 bytes, lines 915-1160
915Length) 916 self.$container.addClass('bootstrap-tagsinput-max'); 917 918 // If using typeahead, once the tag has been added, clear the typeahead value so it does not stick around in the input. 919 if ($('.typeahead, .twitter-typeahead', self.$container).length) { 920 self.$input.typeahead('val', ''); 921 } 922 923 if (this.isInit) { 924 self.$element.trigger($.Event('itemAddedOnInit', { item: item, options: options })); 925 } else { 926 self.$element.trigger($.Event('itemAdded', { item: item, options: options })); 927 } 928 }, 929 930 /** 931 * Removes the given item. Pass true to dontPushVal to prevent updating the 932 * elements val() 933 */ 934 remove: function(item, dontPushVal, options) { 935 var self = this; 936 937 if (self.objectItems) { 938 if (typeof item === "object") 939 item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == self.options.itemValue(item); } ); 940 else 941 item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == item; } ); 942 943 item = item[item.length-1]; 944 } 945 946 if (item) { 947 var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options }); 948 self.$element.trigger(beforeItemRemoveEvent); 949 if (beforeItemRemoveEvent.cancel) 950 return; 951 952 $('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove(); 953 $('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove(); 954 if($.inArray(item, self.itemsArray) !== -1) 955 self.itemsArray.splice($.inArray(item, self.itemsArray), 1); 956 } 957 958 if (!dontPushVal) 959 self.pushVal(self.options.triggerChange); 960 961 // Remove class when reached maxTags 962 if (self.options.maxTags > self.itemsArray.length) 963 self.$container.removeClass('bootstrap-tagsinput-max'); 964 965 self.$element.trigger($.Event('itemRemoved', { item: item, options: options })); 966 }, 967 968 /** 969 * Removes all items 970 */ 971 removeAll: function() { 972 var self = this; 973 974 $('.tag', self.$container).remove(); 975 $('option', self.$element).remove(); 976 977 while(self.itemsArray.length > 0) 978 self.itemsArray.pop(); 979 980 self.pushVal(self.options.triggerChange); 981 }, 982 983 /** 984 * Refreshes the tags so they match the text/value of their corresponding 985 * item. 986 */ 987 refresh: function() { 988 var self = this; 989 $('.tag', self.$container).each(function() { 990 var $tag = $(this), 991 item = $tag.data('item'), 992 itemValue = self.options.itemValue(item), 993 itemText = self.options.itemText(item), 994 tagClass = self.options.tagClass(item); 995 996 // Update tag's class and inner text 997 $tag.attr('class', null); 998 $tag.addClass('tag ' + htmlEncode(tagClass)); 999 $tag.contents().filter(function() { 1000 return this.nodeType == 3; 1001 })[0].nodeValue = htmlEncode(itemText); 1002 1003 if (self.isSelect) { 1004 var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; }); 1005 option.attr('value', itemValue); 1006 } 1007 }); 1008 }, 1009 1010 /** 1011 * Returns the items added as tags 1012 */ 1013 items: function() { 1014 return this.itemsArray; 1015 }, 1016 1017 /** 1018 * Assembly value by retrieving the value of each item, and set it on the 1019 * element. 1020 */ 1021 pushVal: function() { 1022 var self = this, 1023 val = $.map(self.items(), function(item) { 1024 return self.options.itemValue(item).toString(); 1025 }); 1026 1027 self.$element.val(val, true); 1028 1029 if (self.options.triggerChange) 1030 self.$element.trigger('change'); 1031 }, 1032 1033 /** 1034 * Initializes the tags input behaviour on the element 1035 */ 1036 build: function(options) { 1037 var self = this; 1038 1039 self.options = $.extend({}, defaultOptions, options); 1040 // When itemValue is set, freeInput should always be false 1041 if (self.objectItems) 1042 self.options.freeInput = false; 1043 1044 makeOptionItemFunction(self.options, 'itemValue'); 1045 makeOptionItemFunction(self.options, 'itemText'); 1046 makeOptionFunction(self.options, 'tagClass'); 1047 1048 // Typeahead Bootstrap version 2.3.2 1049 if (self.options.typeahead) { 1050 var typeahead = self.options.typeahead || {}; 1051 1052 makeOptionFunction(typeahead, 'source'); 1053 1054 self.$input.typeahead($.extend({}, typeahead, { 1055 source: function (query, process) { 1056 function processItems(items) { 1057 var texts = []; 1058 1059 for (var i = 0; i < items.length; i++) { 1060 var text = self.options.itemText(items[i]); 1061 map[text] = items[i]; 1062 texts.push(text); 1063 } 1064 process(texts); 1065 } 1066 1067 this.map = {}; 1068 var map = this.map, 1069 data = typeahead.source(query); 1070 1071 if ($.isFunction(data.success)) { 1072 // support for Angular callbacks 1073 data.success(processItems); 1074 } else if ($.isFunction(data.then)) { 1075 // support for Angular promises 1076 data.then(processItems); 1077 } else { 1078 // support for functions and jquery promises 1079 $.when(data) 1080 .then(processItems); 1081 } 1082 }, 1083 updater: function (text) { 1084 self.add(this.map[text]); 1085 return this.map[text]; 1086 }, 1087 matcher: function (text) { 1088 return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1); 1089 }, 1090 sorter: function (texts) { 1091 return texts.sort(); 1092 }, 1093 highlighter: function (text) { 1094 var regex = new RegExp( '(' + this.query + ')', 'gi' ); 1095 return text.replace( regex, "<strong>$1</strong>" ); 1096 } 1097 })); 1098 } 1099 1100 // typeahead.js 1101 if (self.options.typeaheadjs) { 1102 1103 // Determine if main configurations were passed or simply a dataset 1104 var typeaheadjs = self.options.typeaheadjs; 1105 if (!$.isArray(typeaheadjs)) { 1106 typeaheadjs = [null, typeaheadjs]; 1107 } 1108 var valueKey = typeaheadjs[1].valueKey; // We should test typeaheadjs.size >= 1 1109 var f_datum = valueKey ? function (datum) { return datum[valueKey]; } 1110 : function (datum) { return datum; } 1111 $.fn.typeahead.apply(self.$input,typeaheadjs).on('typeahead:selected', $.proxy(function (obj, datum) { 1112 self.add( f_datum(datum) ); 1113 self.$input.typeahead('val', ''); 1114 }, self)); 1115 1116 } 1117 1118 self.$container.on('click', $.proxy(function(event) { 1119 if (! self.$element.attr('disabled')) { 1120 self.$input.removeAttr('disabled'); 1121 } 1122 self.$input.focus(); 1123 }, self)); 1124 1125 if (self.options.addOnBlur && self.options.freeInput) { 1126 self.$input.on('focusout', $.proxy(function(event) { 1127 // HACK: only process on focusout when no typeahead opened, to 1128 // avoid adding the typeahead text as tag 1129 if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) { 1130 self.add(self.$input.val()); 1131 self.$input.val(''); 1132 } 1133 }, self)); 1134 } 1135 1136 // Toggle the 'focus' css class on the container when it has focus 1137 self.$container.on({ 1138 focusin: function() { 1139 self.$container.addClass(self.options.focusClass); 1140 }, 1141 focusout: function() { 1142 self.$container.removeClass(self.options.focusClass); 1143 }, 1144 }); 1145 1146 self.$container.on('keydown', 'input', $.proxy(function(event) { 1147 var $input = $(event.target), 1148 $inputWrapper = self.findInputWrapper(); 1149 1150 if (self.$element.attr('disabled')) { 1151 self.$input.attr('disabled', 'disabled'); 1152 return; 1153 } 1154 1155 switch (event.which) { 1156 // BACKSPACE 1157 case 8: 1158 if (doGetCaretPosition($input[0]) === 0) { 1159 var prev = $inputWrapper.prev(); 1160 if (prev.length) {
vendor: 1,148 bytes, lines 1160-1199
1160 1161 self.remove(prev.data('item')); 1162 } 1163 } 1164 break; 1165 1166 // DELETE 1167 case 46: 1168 if (doGetCaretPosition($input[0]) === 0) { 1169 var next = $inputWrapper.next(); 1170 if (next.length) { 1171 self.remove(next.data('item')); 1172 } 1173 } 1174 break; 1175 1176 // LEFT ARROW 1177 case 37: 1178 // Try to move the input before the previous tag 1179 var $prevTag = $inputWrapper.prev(); 1180 if ($input.val().length === 0 && $prevTag[0]) { 1181 $prevTag.before($inputWrapper); 1182 $input.focus(); 1183 } 1184 break; 1185 // RIGHT ARROW 1186 case 39: 1187 // Try to move the input after the next tag 1188 var $nextTag = $inputWrapper.next(); 1189 if ($input.val().length === 0 && $nextTag[0]) { 1190 $nextTag.after($inputWrapper); 1191 $input.focus(); 1192 } 1193 break; 1194 default: 1195 // ignore 1196 } 1197 1198 // Reset internal input's size 1199 var textLength = $input.val().length,
vendor: 5,107 bytes, lines 1200-1363
1200 wordSpace = Math.ceil(textLength / 5), 1201 size = textLength + wordSpace + 1; 1202 $input.attr('size', Math.max(this.inputSize, $input.val().length)); 1203 }, self)); 1204 1205 self.$container.on('keypress', 'input', $.proxy(function(event) { 1206 var $input = $(event.target); 1207 1208 if (self.$element.attr('disabled')) { 1209 self.$input.attr('disabled', 'disabled'); 1210 return; 1211 } 1212 1213 var text = $input.val(), 1214 maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars; 1215 if (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) { 1216 // Only attempt to add a tag if there is data in the field 1217 if (text.length !== 0) { 1218 self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text); 1219 $input.val(''); 1220 } 1221 1222 // If the field is empty, let the event triggered fire as usual 1223 if (self.options.cancelConfirmKeysOnEmpty === false) { 1224 event.preventDefault(); 1225 } 1226 } 1227 1228 // Reset internal input's size 1229 var textLength = $input.val().length, 1230 wordSpace = Math.ceil(textLength / 5), 1231 size = textLength + wordSpace + 1; 1232 $input.attr('size', Math.max(this.inputSize, $input.val().length)); 1233 }, self)); 1234 1235 // Remove icon clicked 1236 self.$container.on('click', '[data-role=remove]', $.proxy(function(event) { 1237 if (self.$element.attr('disabled')) { 1238 return; 1239 } 1240 self.remove($(event.target).closest('.tag').data('item')); 1241 }, self)); 1242 1243 // Only add existing value as tags when using strings as tags 1244 if (self.options.itemValue === defaultOptions.itemValue) { 1245 if (self.$element[0].tagName === 'INPUT') { 1246 self.add(self.$element.val()); 1247 } else { 1248 $('option', self.$element).each(function() { 1249 self.add($(this).attr('value'), true); 1250 }); 1251 } 1252 } 1253 }, 1254 1255 /** 1256 * Removes all tagsinput behaviour and unregsiter all event handlers 1257 */ 1258 destroy: function() { 1259 var self = this; 1260 1261 // Unbind events 1262 self.$container.off('keypress', 'input'); 1263 self.$container.off('click', '[role=remove]'); 1264 1265 self.$container.remove(); 1266 self.$element.removeData('tagsinput'); 1267 self.$element.show(); 1268 }, 1269 1270 /** 1271 * Sets focus on the tagsinput 1272 */ 1273 focus: function() { 1274 this.$input.focus(); 1275 }, 1276 1277 /** 1278 * Returns the internal input element 1279 */ 1280 input: function() { 1281 return this.$input; 1282 }, 1283 1284 /** 1285 * Returns the element which is wrapped around the internal input. This 1286 * is normally the $container, but typeahead.js moves the $input element. 1287 */ 1288 findInputWrapper: function() { 1289 var elt = this.$input[0], 1290 container = this.$container[0]; 1291 while(elt && elt.parentNode !== container) 1292 elt = elt.parentNode; 1293 1294 return $(elt); 1295 } 1296 }; 1297 1298 /** 1299 * Register JQuery plugin 1300 */ 1301 $.fn.tagsinput = function(arg1, arg2, arg3) { 1302 var results = []; 1303 1304 this.each(function() { 1305 var tagsinput = $(this).data('tagsinput'); 1306 // Initialize a new tags input 1307 if (!tagsinput) { 1308 tagsinput = new TagsInput(this, arg1); 1309 $(this).data('tagsinput', tagsinput); 1310 results.push(tagsinput); 1311 1312 if (this.tagName === 'SELECT') { 1313 $('option', $(this)).attr('selected', 'selected'); 1314 } 1315 1316 // Init tags from $(this).val() 1317 $(this).val($(this).val()); 1318 } else if (!arg1 && !arg2) { 1319 // tagsinput already exists 1320 // no function, trying to init 1321 results.push(tagsinput); 1322 } else if(tagsinput[arg1] !== undefined) { 1323 // Invoke function on existing tags input 1324 if(tagsinput[arg1].length === 3 && arg3 !== undefined){ 1325 var retVal = tagsinput[arg1](arg2, null, arg3); 1326 }else{ 1327 var retVal = tagsinput[arg1](arg2); 1328 } 1329 if (retVal !== undefined) 1330 results.push(retVal); 1331 } 1332 }); 1333 1334 if ( typeof arg1 == 'string') { 1335 // Return the results from the invoked function calls 1336 return results.length > 1 ? results : results[0]; 1337 } else { 1338 return results; 1339 } 1340 }; 1341 1342 $.fn.tagsinput.Constructor = TagsInput; 1343 1344 /** 1345 * Most options support both a string or number as well as a function as 1346 * option value. This function makes sure that the option with the given 1347 * key in the given options is wrapped in a function 1348 */ 1349 function makeOptionItemFunction(options, key) { 1350 if (typeof options[key] !== 'function') { 1351 var propertyName = options[key]; 1352 options[key] = function(item) { return item[propertyName]; }; 1353 } 1354 } 1355 function makeOptionFunction(options, key) { 1356 if (typeof options[key] !== 'function') { 1357 var value = options[key]; 1358 options[key] = function() { return value; }; 1359 } 1360 } 1361 /** 1362 * HtmlEncodes the given value 1363 */
vendor: 2,241 bytes, lines 1364-1426
1364 var htmlEncodeContainer = $('<div />'); 1365 function htmlEncode(value) { 1366 if (value) { 1367 return htmlEncodeContainer.text(value).html(); 1368 } else { 1369 return ''; 1370 } 1371 } 1372 1373 /** 1374 * Returns the position of the caret in the given input field 1375 * http://flightschool.acylt.com/devnotes/caret-position-woes/ 1376 */ 1377 function doGetCaretPosition(oField) { 1378 var iCaretPos = 0; 1379 if (document.selection) { 1380 oField.focus (); 1381 var oSel = document.selection.createRange(); 1382 oSel.moveStart ('character', -oField.value.length); 1383 iCaretPos = oSel.text.length; 1384 } else if (oField.selectionStart || oField.selectionStart == '0') { 1385 iCaretPos = oField.selectionStart; 1386 } 1387 return (iCaretPos); 1388 } 1389 1390 /** 1391 * Returns boolean indicates whether user has pressed an expected key combination. 1392 * @param object keyPressEvent: JavaScript event object, refer 1393 * http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html 1394 * @param object lookupList: expected key combinations, as in: 1395 * [13, {which: 188, shiftKey: true}] 1396 */ 1397 function keyCombinationInList(keyPressEvent, lookupList) { 1398 var found = false; 1399 $.each(lookupList, function (index, keyCombination) { 1400 if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) { 1401 found = true; 1402 return false; 1403 } 1404 1405 if (keyPressEvent.which === keyCombination.which) { 1406 var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey, 1407 shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey, 1408 ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey; 1409 if (alt && shift && ctrl) { 1410 found = true; 1411 return false; 1412 } 1413 } 1414 }); 1415 1416 return found; 1417 } 1418 1419 /** 1420 * Initialize tagsinput behaviour on inputs and selects which have 1421 * data-role=tagsinput 1422 */ 1423 $(function() { 1424 $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput(); 1425 }); 1426})(window.jQuery);
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.