1/* 2* Kendo UI Web v2012.1.515 (http://kendoui.com) 3* Copyright 2012 Telerik AD. All rights reserved. 4* 5* Kendo UI Web commercial licenses may be obtained at http://kendoui.com/web-license 6* If you do not own a commercial license, this file shall be governed by the 7* GNU General Public License (GPL) version 3. 8* For GPL requirements, please review: http://www.gnu.org/copyleft/gpl.html 9*/ 10;(function($, undefined) { 11 /** 12 * @name kendo 13 * @namespace This object contains all code introduced by the Kendo project, plus helper functions that are used across all widgets. 14 */ 15 var kendo = window.kendo = window.kendo || {}, 16 extend = $.extend, 17 each = $.each, 18 proxy = $.proxy, 19 isArray = $.isArray, 20 noop = $.noop, 21 isFunction = $.isFunction, 22 math = Math, 23 Template, 24 JSON = window.JSON || {}, 25 support = {}, 26 formatRegExp = /\{(\d+)(:[^\}]+)?\}/g, 27 boxShadowRegExp = /(\d+?)px\s*(\d+?)px\s*(\d+?)px\s*(\d+?)?/i, 28 FUNCTION = "function", 29 STRING = "string", 30 NUMBER = "number", 31 OBJECT = "object", 32 NULL = "null", 33 BOOLEAN = "boolean", 34 UNDEFINED = "undefined", 35 getterCache = {}, 36 setterCache = {}, 37 slice = [].slice, 38 globalize = window.Globalize; 39 40 function Class() {} 41 42 Class.extend = function(proto) { 43 var base = function() {}, 44 member, 45 that = this, 46 subclass = proto && proto.init ? proto.init : function () { 47 that.apply(this, arguments); 48 }, 49 fn; 50 51 base.prototype = that.prototype; 52 fn = subclass.fn = subclass.prototype = new base(); 53 54 for (member in proto) { 55 if (typeof proto[member] === OBJECT && !(proto[member] instanceof Array) && proto[member] !== null) { 56 // Merge object members 57 fn[member] = extend(true, {}, base.prototype[member], proto[member]); 58 } else { 59 fn[member] = proto[member]; 60 } 61 } 62 63 fn.constructor = subclass; 64 subclass.extend = that.extend; 65 66 return subclass; 67 }; 68 69 var Observable = Class.extend(/** @lends kendo.Observable.prototype */{ 70 /** 71 * Creates an observable instance. 72 * @constructs 73 * @class Represents a class that can trigger events, along with methods that subscribe handlers to these events. 74 */ 75 init: function() { 76 this._events = {}; 77 }, 78 79 bind: function(eventName, handlers, one) { 80 var that = this, 81 idx, 82 eventNames = typeof eventName === STRING ? [eventName] : eventName, 83 length, 84 original, 85 handler, 86 handlersIsFunction = typeof handlers === FUNCTION, 87 events; 88 89 for (idx = 0, length = eventNames.length; idx < length; idx++) { 90 eventName = eventNames[idx]; 91 92 handler = handlersIsFunction ? handlers : handlers[eventName]; 93 94 if (handler) { 95 if (one) { 96 original = handler; 97 handler = function() { 98 that.unbind(eventName, handler); 99 original.apply(that, arguments); 100 }; 101 } 102 events = that._events[eventName] = that._events[eventName] || []; 103 events.push(handler); 104 } 105 } 106 107 return that; 108 }, 109 110 one: function(eventNames, handlers) { 111 return this.bind(eventNames, handlers, true); 112 }, 113 114 first: function(eventName, handlers) { 115 var that = this, 116 idx, 117 eventNames = typeof eventName === STRING ? [eventName] : eventName, 118 length, 119 handler, 120 handlersIsFunction = typeof handlers === FUNCTION, 121 events; 122 123 for (idx = 0, length = eventNames.length; idx < length; idx++) { 124 eventName = eventNames[idx]; 125 126 handler = handlersIsFunction ? handlers : handlers[eventName]; 127 128 if (handler) { 129 events = that._events[eventName] = that._events[eventName] || []; 130 events.unshift(handler); 131 } 132 } 133 134 return that; 135 }, 136 137 trigger: function(eventName, e) { 138 var that = this, 139 events = that._events[eventName], 140 idx, 141 isDefaultPrevented = false; 142 143 if (events) { 144 e = e || {}; 145 146 e.sender = that; 147 148 e.preventDefault = function () { 149 isDefaultPrevented = true; 150 }; 151 152 e.isDefaultPrevented = function() { 153 return isDefaultPrevented; 154 }; 155 156 //Do not cache the length of the events array as removing events attached through one will fail 157 for (idx = 0; idx < events.length; idx++) { 158 events[idx].call(that, e); 159 } 160 } 161 162 return isDefaultPrevented; 163 }, 164 165 unbind: function(eventName, handler) { 166 var that = this, 167 events = that._events[eventName], 168 idx, 169 length; 170 171 if (events) { 172 if (handler) { 173 for (idx = 0, length = events.length; idx < length; idx++) { 174 if (events[idx] === handler) { 175 events.splice(idx, 1); 176 } 177 } 178 } else { 179 that._events[eventName] = []; 180 } 181 } 182 183 return that; 184 } 185 });
186 187 /** 188 * @name kendo.Template.Description 189 * 190 * @section 191 * <p> 192 * Templates offer way of creating HTML chunks. Options such as HTML encoding and compilation for optimal 193 * performance are available. 194 * </p> 195 * 196 * @exampleTitle Basic template 197 * @example 198 * var inlineTemplate = kendo.template("Hello, #= firstName # #= lastName #"); 199 * var inlineData = { firstName: "John", lastName: "Doe" }; 200 * $("#inline").html(inlineTemplate(inlineData)); 201 * 202 * @exampleTitle Output: 203 * @example 204 * Hello, John Doe! 205 * 206 * @exampleTitle Encode HTML 207 * @example 208 * var encodingTemplate = kendo.template("HTML tags are encoded as follows: #:html#"); 209 * var encodingData = { html: "<strong>lorem ipsum</strong>" }; 210 * $("#encoding").html(encodingTemplate(encodingData)); 211 * 212 * @exampleTitle Output: 213 * @example 214 * HTML tags are encoded as follows: <strong>lorem ipsum</strong> 215 * 216 * @exampleTitle Use javascript in templates 217 * @example 218 * var encodingTemplate = kendo.template("#if (foo) {# bar #}#"); 219 * var data = { foo: true}; 220 * $("#encoding").html(encodingTemplate(data)); // outputs bar 221 * 222 * @exampleTitle Escape sharp symbols in JavaScript strings 223 * @example 224 * var encodingTemplate = kendo.template("<a href='\\#'>Link</a>"); 225 * 226 * @exampleTitle Escape sharp symbols in script templates 227 * @example 228 * <script type="text/x-kendo-template" id="template"> 229 * <a href="\#">Link</a> 230 * </script> 231 * 232 * <script> 233 * var encodingTemplate = kendo.template($("#template").text()); 234 * </script> 235 */ 236 237 function compilePart(part, stringPart) { 238 if (stringPart) { 239 return "'" + 240 part.split("'").join("\\'") 241 .replace(/\n/g, "\\n") 242 .replace(/\r/g, "\\r") 243 .replace(/\t/g, "\\t") + "'"; 244 } else { 245 var first = part.charAt(0), 246 rest = part.substring(1); 247 248 if (first === "=") { 249 return "+(" + rest + ")+"; 250 } else if (first === ":") { 251 return "+e(" + rest + ")+"; 252 } else { 253 return ";" + part + ";o+="; 254 } 255 } 256 } 257 258 var argumentNameRegExp = /^\w+/, 259 encodeRegExp = /\$\{([^}]*)\}/g, 260 escapedCurlyRegExp = /\\\}/g, 261 curlyRegExp = /__CURLY__/g, 262 escapedSharpRegExp = /\\#/g, 263 sharpRegExp = /__SHARP__/g; 264 265 /** 266 * @name kendo.Template 267 * @namespace 268 */ 269 Template = /** @lends kendo.Template */ { 270 paramName: "data", // name of the parameter of the generated template 271 useWithBlock: true, // whether to wrap the template in a with() block 272 /** 273 * Renders a template for each item of the data. 274 * @ignore 275 * @name kendo.Template.render 276 * @static 277 * @function 278 * @param {String} [template] The template that will be rendered 279 * @param {Array} [data] Data items 280 * @returns {String} The rendered template 281 */ 282 render: function(template, data) { 283 var idx, 284 length, 285 html = ""; 286 287 for (idx = 0, length = data.length; idx < length; idx++) { 288 html += template(data[idx]); 289 } 290 291 return html; 292 }, 293 /** 294 * Compiles a template to a function that builds HTML. Useful when a template will be used several times. 295 * @ignore 296 * @name kendo.Template.compile 297 * @static 298 * @function 299 * @param {String} [template] The template that will be compiled 300 * @param {Object} [options] Compilation options 301 * @returns {Function} The compiled template 302 */ 303 compile: function(template, options) { 304 var settings = extend({}, this, options), 305 paramName = settings.paramName, 306 argumentName = paramName.match(argumentNameRegExp)[0], 307 useWithBlock = settings.useWithBlock, 308 functionBody = "var o,e=kendo.htmlEncode;", 309 parts, 310 idx; 311 312 if (isFunction(template)) { 313 if (template.length === 2) { 314 //looks like jQuery.template 315 return function(d) { 316 return template($, { data: d }).join(""); 317 }; 318 } 319 return template; 320 } 321 322 functionBody += useWithBlock ? "with(" + paramName + "){" : ""; 323 324 functionBody += "o="; 325 326 parts = template 327 .replace(escapedCurlyRegExp, "__CURLY__") 328 .replace(encodeRegExp, "#=e($1)#") 329 .replace(curlyRegExp, "}") 330 .replace(escapedSharpRegExp, "__SHARP__") 331 .split("#"); 332 333 for (idx = 0; idx < parts.length; idx ++) { 334 functionBody += compilePart(parts[idx], idx % 2 === 0); 335 } 336 337 functionBody += useWithBlock ? ";}" : ";"; 338 339 functionBody += "return o;"; 340 341 functionBody = functionBody.replace(sharpRegExp, "#"); 342 343 try { 344 return new Function(argumentName, functionBody); 345 } catch(e) { 346 throw new Error(kendo.format("Invalid template:'{0}' Generated code:'{1}'", template, functionBody)); 347 } 348 } 349 }; 350 351function pad(number) { 352 return number < 10 ? "0" + number : number; 353} 354 355 //JSON stringify 356(function() { 357 var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, 358 gap, 359 indent, 360 meta = { 361 "\b": "\\b", 362 "\t": "\\t", 363 "\n": "\\n", 364 "\f": "\\f", 365 "\r": "\\r", 366 "\"" : '\\"', 367 "\\": "\\\\" 368 }, 369 rep, 370 toString = {}.toString; 371 372 if (typeof Date.prototype.toJSON !== FUNCTION) { 373 374 /** @ignore */ 375 Date.prototype.toJSON = function (key) { 376 var that = this; 377 378 return isFinite(that.valueOf()) ? 379 that.getUTCFullYear() + "-" + 380 pad(that.getUTCMonth() + 1) + "-" + 381 pad(that.getUTCDate()) + "T" + 382 pad(that.getUTCHours()) + ":" + 383 pad(that.getUTCMinutes()) + ":" + 384 pad(that.getUTCSeconds()) + "Z" : null; 385 }; 386 387 String.prototype.toJSON = Number.prototype.toJSON = /** @ignore */ Boolean.prototype.toJSON = function (key) { 388 return this.valueOf(); 389 }; 390 } 391 392 function quote(string) { 393 escapable.lastIndex = 0; 394 return escapable.test(string) ? "\"" + string.replace(escapable, function (a) { 395 var c = meta[a]; 396 return typeof c === STRING ? c : 397 "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); 398 }) + "\"" : "\"" + string + "\""; 399 } 400 401 function str(key, holder) { 402 var i, 403 k, 404 v, 405 length, 406 mind = gap, 407 partial, 408 value = holder[key], 409 type; 410 411 if (value && typeof value === OBJECT && typeof value.toJSON === FUNCTION) { 412 value = value.toJSON(key); 413 } 414 415 if (typeof rep === FUNCTION) { 416 value = rep.call(holder, key, value); 417 } 418 419 type = typeof value; 420 if (type === STRING) { 421 return quote(value); 422 } else if (type === NUMBER) { 423 return isFinite(value) ? String(value) : NULL; 424 } else if (type === BOOLEAN || type === NULL) { 425 return String(value); 426 } else if (type === OBJECT) { 427 if (!value) { 428 return NULL; 429 } 430 gap += indent; 431 partial = []; 432 if (toString.apply(value) === "[object Array]") { 433 length = value.length; 434 for (i = 0; i < length; i++) { 435 partial[i] = str(i, value) || NULL; 436 } 437 v = partial.length === 0 ? "[]" : gap ? 438 "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" : 439 "[" + partial.join(",") + "]"; 440 gap = mind; 441 return v; 442 } 443 if (rep && typeof rep === OBJECT) { 444 length = rep.length; 445 for (i = 0; i < length; i++) { 446 if (typeof rep[i] === STRING) { 447 k = rep[i]; 448 v = str(k, value); 449 if (v) { 450 partial.push(quote(k) + (gap ? ": " : ":") + v); 451 } 452 } 453 } 454 } else { 455 for (k in value) { 456 if (Object.hasOwnProperty.call(value, k)) { 457 v = str(k, value); 458 if (v) { 459 partial.push(quote(k) + (gap ? ": " : ":") + v); 460 } 461 } 462 } 463 } 464 465 v = partial.length === 0 ? "{}" : gap ? 466 "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" : 467 "{" + partial.join(",") + "}"; 468 gap = mind; 469 return v; 470 } 471 } 472 473 if (typeof JSON.stringify !== FUNCTION) { 474 JSON.stringify = function (value, replacer, space) { 475 var i; 476 gap = ""; 477 indent = ""; 478 479 if (typeof space === NUMBER) { 480 for (i = 0; i < space; i += 1) { 481 indent += " "; 482 } 483 484 } else if (typeof space === STRING) { 485 indent = space; 486 } 487 488 rep = replacer; 489 if (replacer && typeof replacer !== FUNCTION && (typeof replacer !== OBJECT || typeof replacer.length !== NUMBER)) { 490 throw new Error("JSON.stringify"); 491 } 492 493 return str("", {"": value}); 494 }; 495 } 496})(); 497 498// Date and Number formatting 499(function() { 500 var dateFormatRegExp = /dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|HH|H|hh|h|mm|m|fff|ff|f|tt|ss|s|"[^"]*"|'[^']*'/g, 501 standardFormatRegExp = /^(n|c|p|e)(\d*)$/i, 502 literalRegExp = /["'].*?["']/g, 503 commaRegExp = /\,/g, 504 EMPTY = "", 505 POINT = ".", 506 COMMA = ",", 507 SHARP = "#", 508 ZERO = "0", 509 PLACEHOLDER = "??", 510 EN = "en-US"; 511 512 //cultures 513 kendo.cultures = {"en-US" : { 514 name: EN, 515 numberFormat: { 516 pattern: ["-n"], 517 decimals: 2, 518 ",": ",", 519 ".": ".", 520 groupSize: [3], 521 percent: { 522 pattern: ["-n %", "n %"], 523 decimals: 2, 524 ",": ",", 525 ".": ".", 526 groupSize: [3], 527 symbol: "%" 528 }, 529 currency: { 530 pattern: ["($n)", "$n"], 531 decimals: 2, 532 ",": ",", 533 ".": ".", 534 groupSize: [3], 535 symbol: "$" 536 } 537 }, 538 calendars: { 539 standard: { 540 days: { 541 names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], 542 namesAbbr: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], 543 namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ] 544 }, 545 months: { 546 names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "
546November", "December"], 547 namesAbbr: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 548 }, 549 AM: [ "AM", "am", "AM" ], 550 PM: [ "PM", "pm", "PM" ], 551 patterns: { 552 d: "M/d/yyyy", 553 D: "dddd, MMMM dd, yyyy", 554 F: "dddd, MMMM dd, yyyy h:mm:ss tt", 555 g: "M/d/yyyy h:mm tt", 556 G: "M/d/yyyy h:mm:ss tt", 557 m: "MMMM dd", 558 M: "MMMM dd", 559 s: "yyyy'-'MM'-'ddTHH':'mm':'ss", 560 t: "h:mm tt", 561 T: "h:mm:ss tt", 562 u: "yyyy'-'MM'-'dd HH':'mm':'ss'Z'", 563 y: "MMMM, yyyy", 564 Y: "MMMM, yyyy" 565 }, 566 "/": "/", 567 ":": ":", 568 firstDay: 0 569 } 570 } 571 }}; 572 573 /** 574 * @name kendo.ui.Globalization 575 * @namespace 576 */ 577 /** 578 * @name kendo.ui.Globalization.Description 579 * 580 * @section Globalization is the process of designing and developing an 581 * application that works in multiple cultures. The culture defines specific information 582 * for the number formats, week and month names, date and time formats and etc. 583 * 584 * @section Kendo exposes <strong><em>culture(cultureName)</em></strong> method which allows to select the culture 585 * script coresponding to the "culture name". kendo.culture() method uses the passed culture name 586 * to select culture from the culture scripts that you have included and then sets the current culture. 587 * If there is no such culture, the default one is used. 588 * 589 * <h3>Define current culture settings</h3> 590 * 591 * @exampleTitle Include culture scripts and select culture 592 * @example 593 * 594 * <script src="jquery.js" ></script> 595 * <script src="kendo.all.min.js"></script> 596 * <script src="kendo.culture.en-GB.js"></script> 597 * <script type="text/javascript"> 598 * //set current culture to the "en-GB" culture script. 599 * kendo.culture("en-GB"); 600 * </script> 601 * 602 * @exampleTitle Get current culture 603 * @example 604 * var cultureInfo = kendo.culture(); 605 * 606 * @section 607 * <h3>Format number or date object</h3> 608 * 609 * Kendo exposes methods which can format number or date object using specific format string and the current specified culture: 610 * @section 611 * <h4><code>kendo.toString(object, format)</code> - returns a string representation of the current object using specific format.</h4> 612 * @exampleTitle Formats number and date objects 613 * @example 614 * //format number using standard number format 615 * kendo.toString(10.12, "n"); //10.12 616 * kendo.toString(10.12, "n0"); //10 617 * kendo.toString(10.12, "n5"); //10.12000 618 * kendo.toString(10.12, "c"); //$10.12 619 * kendo.toString(0.12, "p"); //12.00 % 620 * //format number using custom number format 621 * kendo.toString(19.12, "00##"); //0019 622 * //format date 623 * kendo.toString(new Date(2010, 9, 5), "yyyy/MM/dd" ); // "2010/10/05" 624 * kendo.toString(new Date(2010, 9, 5), "dddd MMMM d, yyyy" ); // "Tuesday October 5, 2010" 625 * kendo.toString(new Date(2010, 10, 10, 22, 12), "hh:mm tt" ); // "10:12 PM" 626 * 627 * @section 628 * <h4><code>kendo.format</code> - replaces each format item in a specified string with the text equivalent of a corresponding object's value.</h4> 629 * @exampleTitle String format 630 * @example 631 * kendo.format("{0} - {1}", 12, 24); //12 - 24 632 * kendo.format("{0:c} - {1:c}", 12, 24); //$12.00 - $24.00 633 * 634 * @section 635 * <h3>Parsing a string</h3> 636 * 637 * Kendo exposes methods which converts the specified string to date or number object: 638 * <ol> 639 * <li> 640 * <code>kendo.parseInt(string, [culture])</code> - converts a string to a whole number using the specified culture (current culture by default). 641 * @exampleTitle Parse string to integer 642 * @example 643 * 644 * //assumes that current culture defines decimal separator as "." 645 * kendo.parseInt("12.22"); //12 646 * 647 * //assumes that current culture defines decimal separator as ",", group separator as "." and currency symbol as "€" 648 * kendo.parseInt("1.212,22 €"); //1212 649 * </li> 650 * <li> 651 * <code>kendo.parseFloat(string, [culture])</code> - converts a string to a number with floating point using the specified culture (current culture by default). 652 * @exampleTitle Parse string to float 653 * @example 654 * 655 * //assumes that current culture defines decimal separator as "." 656 * kendo.parseFloat("12.22"); //12.22 657 * 658 * //assumes that current culture defines decimal separator as ",", group separator as "." and currency symbol as "€" 659 * kendo.parseFloat("1.212,22 €"); //1212.22 660 * </li> 661 * <li> 662 * <code>kendo.parseDate(string, [formats], [culture])</code> - converts a string to a JavaScript Date object, taking into account the given format/formats (or the given culture's set of default formats). 663 * Current culture is used if one is not specified. 664 * @exampleTitle Parse string to float 665 * @example 666 * 667 * //current culture is "en-US" 668 * kendo.parseDate("12/22/2000"); //Fri Dec 22 2000 669 * kendo.parseDate("2000/12/22", "yyyy/MM/dd"); //Fri Dec 22 2000 670 * </li> 671 * </ol> 672 * 673 * @section 674 * <h3>Number formatting</h3> 675 * The purpose of number formatting is to convert Number object to a human readable string using culture's specific settings. <code>kendo.format</code> and <code>kendo.toString</code> 676 * methods support standard and custom numeric formats: 677 * <h4>Standard numeric formats</h4> 678 *<strong>n</strong> for number 679 * @exampleTitle Formatting using "n" format 680 * @example 681 * kendo.culture("en-US"); 682 * kendo.toString(1234.567, "n"); //1,234.57 683 * 684 * kendo.culture("de-DE"); 685 * kendo.toString(1234.567, "n3"); //1.234,567 686 *@section 687 *<strong>c</strong> for currency 688 * @exampleTitle Formatting using "c" format 689 * @example 690 * kendo.culture("en-US"); 691 * kendo.toString(1234.567, "c"); //$1,234.57 692 * 693 * kendo.culture("de-DE"); 694 * kendo.toString(1234.567, "c3"); //1.234,567 € 695 *@section 696 *<strong>
696p</strong> for percentage (number is multiplied by 100) 697 * @exampleTitle Formatting using "p" format 698 * @example 699 * kendo.culture("en-US"); 700 * kendo.toString(0.222, "p"); //22.20 % 701 * 702 * kendo.culture("de-DE"); 703 * kendo.toString(0.22, "p3"); //22.000 % 704 *@section 705 *<strong>e</strong> for exponential 706 * @exampleTitle Formatting using "e" format 707 * @example 708 * kendo.toString(0.122, "e"); //1.22e-1 709 * kendo.toString(0.122, "e4"); //1.2200e-1 710 * 711 * @section 712 * <h4>Custom numeric formats</h4> 713 * You can create custom numeric format string using one or more custom numeric specifiers. Custom numeric format string is any tha is not a standard numeric format. 714 * <div class="details-list"> 715 * <h4 class="details-title">Format specifiers</h4> 716 * <dl> 717 * <dt> 718 * "0" - zero placeholder 719 * </dt> 720 * <dd>Replaces the zero with the corresponding digit if one is present; otherwise, zero appears in the result string - <code>kendo.toString(1234.5678, "00000") -> 01235</code></dd> 721 * <dt> 722 * "#" - digit placeholder 723 * </dt> 724 * <dd>Replaces the pound sign with the corresponding digit if one is present; otherwise, no digit appears in the result string - <code>kendo.toString(1234.5678, "#####") -> 1235</code></dd> 725 * <dt> 726 * "." - Decimal placeholder 727 * </dt> 728 * <dd>Determines the location of the decimal separator in the result string - <code>kendo.tostring(0.45678, "0.00") -> 0.46 </code>(en-us)</dd> 729 * <dt> 730 * "," - group separator placeholder 731 * </dt> 732 * <dd>Insert localized group separator between each group - <code>kendo.tostring(12345678, "##,#") -> 12,345,678</code>(en-us)</dd> 733 * <dt> 734 * "%" - percentage placeholder 735 * </dt> 736 * <dd>Multiplies a number by 100 and inserts a localized percentage symbol in the result string</dd> 737 * <dt> 738 * "e" - exponential notation 739 * </dt> 740 * <dd><code>kendo.toString(0.45678, "e0") -> 5e-1</code></dd> 741 * <dt> 742 * ";" - section separator 743 * </dt> 744 * <dd>Defines sections wih separate format strings for positive, negative, and zero numbers</dd> 745 * <dt> 746 * "string"/'string' - Literal string delimiter 747 * </dt> 748 * <dd>Indicates that the enclosed characters should be copied to the result string</dd> 749 * </dl> 750 * </div> 751 * 752 * @section 753 * <h3>Date formatting</h3> 754 * The purpose of date formatting is to convert Date object to a human readable string using culture's specific settings. <code>kendo.format</code> and <code>kendo.toString</code> 755 * methods support standard and custom date formats: 756 * <h4>Standard date formats</h4> 757 * <div class="details-list"> 758 * <h4 class="details-title">Format specifiers</h4> 759 * <dl> 760 * <dt> 761 * "d" - short date pattern 762 * </dt> 763 * <dd><code>kendo.toString(new Date(2000, 10, 6), "d") -> 11/6/2000</code></dd> 764 * <dt> 765 * "D" - long date pattern 766 * </dt> 767 * <dd><code>kendo.toString(new Date(2000, 10, 6), "D") -> Monday, November 06, 2000</code></dd> 768 * <dt> 769 * "F" - Full date/time pattern 770 * </dt> 771 * <dd><code>kendo.toString(new Date(2000, 10, 6), "D") -> Monday, November 06, 2000 12:00:00 AM</code></dd> 772 * <dt> 773 * "g" - General date/time pattern (short time) 774 * </dt> 775 * <dd><code>kendo.toString(new Date(2000, 10, 6), "g") -> 11/6/2000 12:00 AM</code></dd> 776 * <dt> 777 * "G" - General date/time pattern (long time) 778 * </dt> 779 * <dd><code>kendo.toString(new Date(2000, 10, 6), "G") -> 11/6/2000 12:00:00 AM</code></dd> 780 * <dt> 781 * "M/m" - Month/day pattern 782 * </dt> 783 * <dd><code>kendo.toString(new Date(2000, 10, 6), "m") -> November 06</code></dd> 784 * <dt> 785 * "u" - Universal sortable date/time pattern
786 * </dt> 787 * <dd><code>kendo.toString(new Date(2000, 10, 6), "u") -> 2000-11-06 00:00:00Z</code></dd> 788 * <dt> 789 * "Y/y" - Year/month pattern 790 * </dt> 791 * <dd><code>kendo.toString(new Date(2000, 10, 6), "y") -> November, 2000</code></dd> 792 * </dl> 793 * </div> 794 * 795 *@section 796 * <h4>Custom date formats</h4> 797 * <div class="details-list"> 798 * <h4 class="details-title">Format specifiers</h4> 799 * <dl> 800 * <dt> 801 * "d" 802 * </dt> 803 * <dd>The day of the month, from 1 through 31</dd> 804 * <dt> 805 * "dd" 806 * </dt> 807 * <dd>The day of the month, from 01 through 31.</dd> 808 * <dt> 809 * "ddd" 810 * </dt> 811 * <dd>iThe abbreviated name of the day of the week</dd> 812 * <dt> 813 * "dddd" 814 * </dt> 815 * <dd>The full name of the day of the week</dd> 816 * <dt> 817 * "f" 818 * </dt> 819 * <dd>The tenths of a second in a date and time value</dd> 820 * <dt> 821 * "ff" 822 * </dt> 823 * <dd>The hundredths of a second in a date and time value</dd> 824 * <dt> 825 * "fff" 826 * </dt> 827 * <dd>The milliseconds in a date and time value</dd> 828 * <dt> 829 * "M" 830 * </dt> 831 * <dd>The month, from 1 through 12</dd> 832 * <dt> 833 * "MM" 834 * </dt> 835 * <dd>The month, from 01 through 12</dd> 836 * <dt> 837 * "MMM" 838 * </dt> 839 * <dd>The abbreviated name of the month</dd> 840 * <dt> 841 * "MMMM" 842 * </dt> 843 * <dd>The full name of the month</dd> 844 * <dt> 845 * "h" 846 * </dt> 847 * <dd>The hour, using a 12-hour clock from 1 to 12</dd> 848 * <dt> 849 * "hh" 850 * </dt> 851 * <dd>The hour, using a 12-hour clock from 01 to 12</dd> 852 * <dt> 853 * "H" 854 * </dt> 855 * <dd>The hour, using a 24-hour clock from 1 to 23</dd> 856 * <dt> 857 * "HH" 858 * </dt> 859 * <dd>The hour, using a 24-hour clock from 01 to 23</dd> 860 * <dt> 861 * "m" 862 * </dt> 863 * <dd>The minute, from 0 through 59</dd> 864 * <dt> 865 * "mm" 866 * </dt> 867 * <dd>The minute, from 00 through 59</dd> 868 * <dt> 869 * "s" 870 * </dt> 871 * <dd>The second, from 0 through 59</dd> 872 * <dt> 873 * "ss" 874 * </dt> 875 * <dd>The second, from 00 through 59</dd> 876 * <dt> 877 * "tt" 878 * </dt> 879 * <dd>The AM/PM designator</dd> 880 * </dl> 881 * </div> 882 * 883 * @section 884 * <p><h3>Widgets that depend on current culture are:</h3> 885 * <ul> 886 * <li> Calendar </li> 887 * <li> DatePicker </li> 888 * <li> TimePicker </li> 889 * <li> NumericTextBox </li> 890 * </ul> 891 * </p> 892 */ 893 894 kendo.culture = function(cultureName) { 895 if (cultureName !== undefined) { 896 var cultures = kendo.cultures, 897 culture = cultures[cultureName] || cultures[EN]; 898 899 culture.calendar = culture.calendars.standard; 900 cultures.current = culture; 901 } else { 902 return kendo.cultures.current; 903 } 904 }; 905 906 //set current culture to en-US. 907 kendo.culture(EN); 908 909 function formatDate(date, format) { 910 var calendar = kendo.cultures.current.calendar, 911 days = calendar.days, 912 months = calendar.months; 913 914 format = calendar.patterns[format] || format; 915 916 return format.replace(dateFormatRegExp, function (match) { 917 var result; 918 919 if (match === "d") { 920 result = date.getDate(); 921 } else if (match === "dd") { 922 result = pad(date.getDate()); 923 } else if (match === "ddd") { 924 result = days.namesAbbr[date.getDay()]; 925 } else if (match === "dddd") { 926 result = days.names[date.getDay()]; 927 } else if (match === "M") { 928 result = date.getMonth() + 1; 929 } else if (match === "MM") { 930 result = pad(date.getMonth() + 1); 931 } else if (match === "MMM") { 932 result = months.namesAbbr[date.getMonth()];
933 } else if (match === "MMMM") { 934 result = months.names[date.getMonth()]; 935 } else if (match === "yy") { 936 result = pad(date.getFullYear() % 100); 937 } else if (match === "yyyy") { 938 result = date.getFullYear(); 939 } else if (match === "h" ) { 940 result = date.getHours() % 12 || 12; 941 } else if (match === "hh") { 942 result = pad(date.getHours() % 12 || 12); 943 } else if (match === "H") { 944 result = date.getHours(); 945 } else if (match === "HH") { 946 result = pad(date.getHours()); 947 } else if (match === "m") { 948 result = date.getMinutes(); 949 } else if (match === "mm") { 950 result = pad(date.getMinutes()); 951 } else if (match === "s") { 952 result = date.getSeconds(); 953 } else if (match === "ss") { 954 result = pad(date.getSeconds()); 955 } else if (match === "f") { 956 result = math.floor(date.getMilliseconds() / 100); 957 } else if (match === "ff") { 958 result = math.floor(date.getMilliseconds() / 10); 959 } else if (match === "fff") { 960 result = date.getMilliseconds(); 961 } else if (match === "tt") { 962 result = date.getHours() < 12 ? calendar.AM[0] : calendar.PM[0]; 963 } 964 965 return result !== undefined ? result : match.slice(1, match.length - 1); 966 }); 967 } 968 969 //number formatting 970 function formatNumber(number, format) { 971 var culture = kendo.cultures.current, 972 numberFormat = culture.numberFormat, 973 groupSize = numberFormat.groupSize[0], 974 groupSeparator = numberFormat[COMMA], 975 decimal = numberFormat[POINT], 976 precision = numberFormat.decimals, 977 pattern = numberFormat.pattern[0], 978 literals = [], 979 symbol, 980 isCurrency, isPercent, 981 customPrecision, 982 formatAndPrecision, 983 negative = number < 0, 984 integer, 985 fraction, 986 integerLength, 987 fractionLength, 988 replacement = EMPTY, 989 value = EMPTY, 990 idx, 991 length, 992 ch, 993 hasGroup, 994 decimalIndex, 995 sharpIndex, 996 zeroIndex, 997 startZeroIndex, 998 start = -1, 999 end; 1000 1001 //return empty string if no number 1002 if (number === undefined) { 1003 return EMPTY; 1004 } 1005 1006 if (!isFinite(number)) { 1007 return number; 1008 } 1009 1010 //if no format then return number.toString() or number.toLocaleString() if culture.name is not defined 1011 if (!format) { 1012 return culture.name.length ? number.toLocaleString() : number.toString(); 1013 } 1014 1015 formatAndPrecision = standardFormatRegExp.exec(format); 1016 1017 /* standard formatting */ 1018 if (formatAndPrecision) { 1019 format = formatAndPrecision[1].toLowerCase(); 1020 1021 isCurrency = format === "c"; 1022 isPercent = format === "p"; 1023 1024 if (isCurrency || isPercent) { 1025 //get specific number format information if format is currency or percent 1026 numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent; 1027 groupSize = numberFormat.groupSize[0]; 1028 groupSeparator = numberFormat[COMMA]; 1029 decimal = numberFormat[POINT]; 1030 precision = numberFormat.decimals; 1031 symbol = numberFormat.symbol; 1032 pattern = numberFormat.pattern[negative ? 0 : 1]; 1033 } 1034 1035 customPrecision = formatAndPrecision[2]; 1036 1037 if (customPrecision) { 1038 precision = +customPrecision; 1039 } 1040 1041 //return number in exponential format 1042 if (format === "e") { 1043 return customPrecision ? number.toExponential(precision) : number.toExponential(); // toExponential() and toExponential(undefined) differ in FF #653438. 1044 } 1045 1046 // multiply if format is percent 1047 if (isPercent) { 1048 number *= 100; 1049 } 1050 1051 number = number.toFixed(precision); 1052 number = number.split(POINT); 1053 1054 integer = number[0]; 1055 fraction = number[1]; 1056 1057 //exclude "-" if number is negative. 1058 if (negative) { 1059 integer = integer.substring(1); 1060 } 1061 1062 value = integer; 1063 integerLength = integer.length; 1064 1065 //add group separator to the number if it is longer enough 1066 if (integerLength >= groupSize) { 1067 value = EMPTY; 1068 for (idx = 0; idx < integerLength; idx++) { 1069 if (idx > 0 && (integerLength - idx) % groupSize === 0) { 1070 value += groupSeparator; 1071 } 1072 value += integer.charAt(idx); 1073 } 1074 } 1075 1076 if (fraction) { 1077 value += decimal + fraction; 1078 } 1079 1080 if (format === "n" && !negative) { 1081 return value; 1082 } 1083 1084 number = EMPTY; 1085 1086 for (idx = 0, length = pattern.length; idx < length; idx++) { 1087 ch = pattern.charAt(idx); 1088 1089 if (ch === "n") { 1090 number += value; 1091 } else if (ch === "$" || ch === "%") { 1092 number += symbol; 1093 } else { 1094 number += ch; 1095 } 1096 } 1097 1098 return number; 1099 } 1100 1101 //custom formatting 1102 // 1103 //separate format by sections. 1104 format = format.split(";"); 1105 if (negative && format[1]) { 1106 //make number positive and get negative format 1107 number = -number; 1108 format = format[1]; 1109 } else if (number === 0) { 1110 //format for zeros 1111 format = format[2] || format[0]; 1112 if (format.indexOf(SHARP) == -1 && format.indexOf(ZERO) == -1) { 1113 //return format if it is string constant. 1114 return format; 1115 } 1116 } else { 1117 format = format[0]; 1118 } 1119 1120 if (format.indexOf("'") > -1 || format.indexOf("\"") > -1) { 1121 format = format.replace(literalRegExp, function(match) { 1122 literals.push(match); 1123 return PLACEHOLDER; 1124 }); 1125 } 1126 1127 isCurrency = format.indexOf("$") != -1; 1128 isPercent = format.indexOf("%") != -1; 1129 1130 //multiply number if the format has percent 1131 if (isPercent) { 1132 number *= 100; 1133 } 1134 1135 if (isCurrency || isPercent) { 1136 //get specific number format information if format is currency or percent 1137 numberFormat = isCurrency ? numberFormat.currency : numberFormat.percent; 1138 groupSize = numberFormat.groupSize[0]; 1139 groupSeparator = numberFormat[COMMA]; 1140 decimal = numberFormat[POINT]; 1141 precision = numberFormat.decimals; 1142 symbol = numberFormat.symbol; 1143 } 1144 1145 hasGroup = format.indexOf(COMMA) > -1; 1146 if (hasGroup) { 1147 format = format.replace(commaRegExp, EMPTY); 1148 } 1149 1150 decimalIndex = format.indexOf(POINT); 1151 length = format.length; 1152 1153 if (decimalIndex != -1) { 1154 zeroIndex = format.lastIndexOf(ZERO); 1155 sharpIndex = format.lastIndexOf(SHARP); 1156 fraction = number.toString().split(POINT)[1] || EMPTY; 1157 1158 if (sharpIndex > zeroIndex && fraction.length > (sharpIndex - zeroIndex)) { 1159 idx = sharpIndex; 1160 } else if (zeroIndex != -1 && zeroIndex >= decimalIndex) { 1161 idx = zeroIndex; 1162 } 1163 1164 if (idx) { 1165 number = number.toFixed(idx - decimalIndex); 1166 } 1167 1168 } else { 1169 number = number.toFixed(0); 1170 } 1171 1172 sharpIndex = format.indexOf(SHARP); 1173 startZeroIndex = zeroIndex = format.indexOf(ZERO); 1174 1175 //define the index of the first digit placeholder 1176 if (sharpIndex == -1 && zeroIndex != -1) { 1177 start = zeroIndex; 1178 } else if (sharpIndex != -1 && zeroIndex == -1) { 1179 start = sharpIndex; 1180 } else { 1181 start = sharpIndex > zeroIndex ? zeroIndex : sharpIndex; 1182 } 1183 1184 sharpIndex = format.lastIndexOf(SHARP); 1185 zeroIndex = format.lastIndexOf(ZERO); 1186 1187 //define the index of the last digit placeholder 1188 if (sharpIndex == -1 && zeroIndex != -1) { 1189 end = zeroIndex; 1190 } else if (sharpIndex != -1 && zeroIndex == -1) { 1191 end = sharpIndex; 1192 } else { 1193 end = sharpIndex > zeroIndex ? sharpIndex : zeroIndex; 1194 } 1195 1196 if (start == length) { 1197 end = start; 1198 } 1199 1200 if (start != -1) { 1201 value = number.toString().split(POINT); 1202 integer = value[0]; 1203 fraction = value[1] || EMPTY; 1204 1205 integerLength = integer.length; 1206 fractionLength = fraction.length; 1207 1208 //add group separator to the number if it is longer enough 1209 if (hasGroup) { 1210 if (integerLength === groupSize && integerLength < decimalIndex - startZeroIndex) { 1211 integer = groupSeparator + integer; 1212 } else if (integerLength > groupSize) { 1213 value = EMPTY; 1214 for (idx = 0; idx < integerLength; idx++) { 1215 if (idx > 0 && (integerLength - idx) % groupSize === 0) { 1216 value += groupSeparator; 1217 } 1218 value += integer.charAt(idx); 1219 } 1220 1221 integer = value; 1222 } 1223 } 1224 1225 number = format.substring(0, start); 1226 1227 for (idx = start; idx < length; idx++) { 1228 ch = format.charAt(idx); 1229 1230 if (decimalIndex == -1) { 1231 if (end - idx < integerLength) { 1232 number += integer; 1233 break; 1234 } 1235 } else {
1236 if (zeroIndex != -1 && zeroIndex < idx) { 1237 replacement = EMPTY; 1238 } 1239 1240 if ((decimalIndex - idx) <= integerLength && decimalIndex - idx > -1) { 1241 number += integer; 1242 idx = decimalIndex; 1243 } 1244 1245 if (decimalIndex === idx) { 1246 number += (fraction ? decimal : EMPTY) + fraction; 1247 idx += end - decimalIndex + 1; 1248 continue; 1249 } 1250 } 1251 1252 if (ch === ZERO) { 1253 number += ch; 1254 replacement = ch; 1255 } else if (ch === SHARP) { 1256 number += replacement; 1257 } 1258 } 1259 1260 if (end >= start) { 1261 number += format.substring(end + 1); 1262 } 1263 1264 //replace symbol placeholders 1265 if (isCurrency || isPercent) { 1266 value = EMPTY; 1267 for (idx = 0, length = number.length; idx < length; idx++) { 1268 ch = number.charAt(idx); 1269 value += (ch === "$" || ch === "%") ? symbol : ch; 1270 } 1271 number = value; 1272 } 1273 1274 if (literals[0]) { 1275 length = literals.length; 1276 for (idx = 0; idx < length; idx++) { 1277 number = number.replace(PLACEHOLDER, literals[idx]); 1278 } 1279 } 1280 } 1281 1282 return number; 1283 } 1284 1285 var toString = function(value, fmt) { 1286 if (fmt) { 1287 if (value instanceof Date) { 1288 return formatDate(value, fmt); 1289 } else if (typeof value === NUMBER) { 1290 return formatNumber(value, fmt); 1291 } 1292 } 1293 1294 return value !== undefined ? value : ""; 1295 }; 1296 1297 if (globalize) { 1298 toString = proxy(globalize.format, globalize); 1299 } 1300 1301 kendo.format = function(fmt) { 1302 var values = arguments; 1303 1304 return fmt.replace(formatRegExp, function(match, index, placeholderFormat) { 1305 var value = values[parseInt(index, 10) + 1]; 1306 1307 return toString(value, placeholderFormat ? placeholderFormat.substring(1) : ""); 1308 }); 1309 }; 1310 1311 kendo.toString = toString; 1312 })(); 1313 1314 1315(function() { 1316 1317 var nonBreakingSpaceRegExp = /\u00A0/g, 1318 exponentRegExp = /[eE][\-+]?[0-9]+/, 1319 formatsSequence = ["G", "g", "d", "F", "D", "y", "m", "T", "t"]; 1320 1321 function outOfRange(value, start, end) { 1322 return !(value >= start && value <= end); 1323 } 1324 1325 function parseExact(value, format, culture) { 1326 if (!value) { 1327 return null; 1328 } 1329 1330 var lookAhead = function (match) { 1331 var i = 0; 1332 while (format[idx] === match) { 1333 i++; 1334 idx++; 1335 } 1336 if (i > 0) { 1337 idx -= 1; 1338 } 1339 return i; 1340 }, 1341 getNumber = function(size) { 1342 var rg = new RegExp('^\\d{1,' + size + '}'), 1343 match = value.substr(valueIdx, size).match(rg); 1344 1345 if (match) { 1346 match = match[0]; 1347 valueIdx += match.length; 1348 return parseInt(match, 10); 1349 } 1350 return null; 1351 }, 1352 getIndexByName = function (names) { 1353 var i = 0, 1354 length = names.length, 1355 name, nameLength; 1356 1357 for (; i < length; i++) { 1358 name = names[i]; 1359 nameLength = name.length; 1360 1361 if (value.substr(valueIdx, nameLength) == name) { 1362 valueIdx += nameLength; 1363 return i + 1; 1364 } 1365 } 1366 return null; 1367 }, 1368 checkLiteral = function() { 1369 if (value.charAt(valueIdx) == format[idx]) { 1370 valueIdx++; 1371 } 1372 }, 1373 calendar = culture.calendar, 1374 year = null,
1375 month = null, 1376 day = null, 1377 hours = null, 1378 minutes = null, 1379 seconds = null, 1380 milliseconds = null, 1381 idx = 0, 1382 valueIdx = 0, 1383 literal = false, 1384 date = new Date(), 1385 defaultYear = date.getFullYear(), 1386 shortYearCutOff = 30, 1387 ch, count, pmHour, length, pattern; 1388 1389 if (!format) { 1390 format = "d"; //shord date format 1391 } 1392 1393 //if format is part of the patterns get real format 1394 pattern = calendar.patterns[format]; 1395 if (pattern) { 1396 format = pattern; 1397 } 1398 1399 format = format.split(""); 1400 length = format.length; 1401 1402 for (; idx < length; idx++) { 1403 ch = format[idx]; 1404 1405 if (literal) { 1406 if (ch === "'") { 1407 literal = false; 1408 } else { 1409 checkLiteral(); 1410 } 1411 } else { 1412 if (ch === "d") { 1413 count = lookAhead("d"); 1414 day = count < 3 ? getNumber(2) : getIndexByName(calendar.days[count == 3 ? "namesAbbr" : "names"]); 1415 1416 if (day === null || outOfRange(day, 1, 31)) { 1417 return null; 1418 } 1419 } else if (ch === "M") { 1420 count = lookAhead("M"); 1421 month = count < 3 ? getNumber(2) : getIndexByName(calendar.months[count == 3 ? 'namesAbbr' : 'names']);
1422 1423 if (month === null || outOfRange(month, 1, 12)) { 1424 return null; 1425 } 1426 month -= 1; //because month is zero based 1427 } else if (ch === "y") { 1428 count = lookAhead("y"); 1429 year = getNumber(count < 3 ? 2 : 4); 1430 if (year === null) { 1431 year = defaultYear; 1432 } 1433 if (year < shortYearCutOff) { 1434 year = (defaultYear - defaultYear % 100) + year; 1435 } 1436 } else if (ch === "h" ) { 1437 lookAhead("h"); 1438 hours = getNumber(2); 1439 if (hours == 12) { 1440 hours = 0; 1441 } 1442 if (hours === null || outOfRange(hours, 0, 11)) { 1443 return null; 1444 } 1445 } else if (ch === "H") { 1446 lookAhead("H"); 1447 hours = getNumber(2); 1448 if (hours === null || outOfRange(hours, 0, 23)) { 1449 return null; 1450 } 1451 } else if (ch === "m") { 1452 lookAhead("m"); 1453 minutes = getNumber(2); 1454 if (minutes === null || outOfRange(minutes, 0, 59)) { 1455 return null; 1456 } 1457 } else if (ch === "s") { 1458 lookAhead("s"); 1459 seconds = getNumber(2); 1460 if (seconds === null || outOfRange(seconds, 0, 59)) { 1461 return null; 1462 } 1463 } else if (ch === "f") { 1464 count = lookAhead("f"); 1465 milliseconds = getNumber(count); 1466 if (milliseconds === null || outOfRange(milliseconds, 0, 999)) { 1467 return null; 1468 } 1469 } else if (ch === "t") { 1470 count = lookAhead("t"); 1471 pmHour = getIndexByName(calendar.PM); 1472 } else if (ch === "'") { 1473 checkLiteral(); 1474 literal = true; 1475 } else { 1476 checkLiteral(); 1477 } 1478 } 1479 } 1480 1481 if (pmHour && hours < 12) { 1482 hours += 12; 1483 } 1484 1485 if (day === null) { 1486 day = 1; 1487 } 1488 1489 return new Date(year, month, day, hours, minutes, seconds, milliseconds); 1490 } 1491 1492 kendo.parseDate = function(value, formats, culture) { 1493 if (value instanceof Date) { 1494 return value; 1495 } 1496 1497 var idx = 0, 1498 date = null, 1499 length, patterns; 1500 1501 if (!culture) { 1502 culture = kendo.culture(); 1503 } else if (typeof culture === STRING) { 1504 kendo.culture(culture); 1505 culture = kendo.culture(); 1506 } 1507 1508 if (!formats) { 1509 formats = []; 1510 patterns = culture.calendar.patterns; 1511 length = formatsSequence.length; 1512 1513 for (; idx < length; idx++) { 1514 formats[idx] = patterns[formatsSequence[idx]]; 1515 } 1516 formats[idx] = "ddd MMM dd yyyy HH:mm:ss"; 1517 1518 idx = 0; 1519 } 1520 1521 formats = isArray(formats) ? formats: [formats]; 1522 length = formats.length; 1523 1524 for (; idx < length; idx++) { 1525 date = parseExact(value, formats[idx], culture); 1526 if (date) { 1527 return date; 1528 } 1529 } 1530 1531 return date; 1532 }; 1533 1534 kendo.parseInt = function(value, culture) { 1535 var result = kendo.parseFloat(value, culture); 1536 if (result) { 1537 result = result | 0; 1538 } 1539 return result; 1540 }; 1541 1542 kendo.parseFloat = function(value, culture) { 1543 if (!value && value !== 0) { 1544 return null; 1545 } 1546 1547 if (typeof value === NUMBER) { 1548 return value; 1549 } 1550 1551 value = value.toString(); 1552 culture = kendo.cultures[culture] || kendo.cultures.current; 1553 1554 var number = culture.numberFormat, 1555 percent = number.percent, 1556 currency = number.currency, 1557 symbol = currency.symbol, 1558 percentSymbol = percent.symbol, 1559 negative = value.indexOf("-") > -1, 1560 parts, isPercent; 1561 1562 //handle exponential number 1563 if (exponentRegExp.test(value)) { 1564 value = parseFloat(value); 1565 if (isNaN(value)) { 1566 value = null; 1567 } 1568 return value; 1569 } 1570 1571 if (value.indexOf(symbol) > -1) { 1572 number = currency; 1573 parts = number.pattern[0].replace("$", symbol).split("n"); 1574 if (value.indexOf(parts[0]) > -1 && value.indexOf(parts[1]) > -1) { 1575 value = value.replace(parts[0], "").replace(parts[1], ""); 1576 negative = true; 1577 } 1578 } else if (value.indexOf(percentSymbol) > -1) { 1579 isPercent = true; 1580 number = percent; 1581 symbol = percentSymbol; 1582 } 1583 1584 value = value.replace("-", "") 1585 .replace(symbol, "") 1586 .replace(nonBreakingSpaceRegExp, " ") 1587 .split(number[","].replace(nonBreakingSpaceRegExp, " ")).join("") 1588 .replace(number["."], "."); 1589 1590 value = parseFloat(value); 1591 1592 if (isNaN(value)) { 1593 value = null; 1594 } else if (negative) { 1595 value *= -1; 1596 } 1597 1598 if (value && isPercent) { 1599 value /= 100; 1600 } 1601 1602 return value; 1603 }; 1604 1605 if (globalize) { 1606 kendo.parseDate = proxy(globalize.parseDate, globalize); 1607 kendo.parseFloat = proxy(globalize.parseFloat, globalize); 1608 } 1609})(); 1610 1611 function wrap(element) { 1612 var browser = $.browser; 1613 1614 if (!element.parent().hasClass("k-animation-container")) { 1615 var shadow = element.css(kendo.support.transitions.css + "box-shadow") || element.css("box-shadow"), 1616 radius = shadow ? shadow.match(boxShadowRegExp) || [ 0, 0, 0, 0, 0 ] : [ 0, 0, 0, 0, 0 ], 1617 blur = math.max((+radius[3]), +(radius[4] || 0)), 1618 left = (-radius[1]) + blur, 1619 right = (+radius[1]) + blur, 1620 bottom = (+radius[2]) + blur; 1621 1622 if (browser.opera) { // Box shadow can't be retrieved
1622in Opera 1623 left = right = bottom = 5; 1624 } 1625 1626 element.wrap( 1627 $("<div/>") 1628 .addClass("k-animation-container") 1629 .css({ 1630 width: element.outerWidth(), 1631 height: element.outerHeight(), 1632 marginLeft: -left, 1633 paddingLeft: left, 1634 paddingRight: right, 1635 paddingBottom: bottom 1636 })); 1637 } else { 1638 var wrapper = element.parent(".k-animation-container"); 1639 1640 if (wrapper.is(":hidden")) { 1641 wrapper.show(); 1642 } 1643 1644 wrapper.css({ 1645 width: element.outerWidth(), 1646 height: element.outerHeight() 1647 }); 1648 } 1649 1650 if (browser.msie && math.floor(browser.version) <= 7) { 1651 element.css({ 1652 zoom: 1 1653 }); 1654 } 1655 1656 return element.parent(); 1657 } 1658 1659 function deepExtend(destination) { 1660 var i = 1, 1661 length = arguments.length; 1662 1663 for (i = 1; i < length; i++) { 1664 deepExtendOne(destination, arguments[i]); 1665 } 1666 1667 return destination; 1668 } 1669 1670 function deepExtendOne(destination, source) { 1671 var property, 1672 propValue, 1673 propType, 1674 destProp; 1675 1676 for (property in source) { 1677 propValue = source[property]; 1678 propType = typeof propValue; 1679 if (propType === OBJECT && propValue !== null && propValue.constructor !== Array) { 1680 destProp = destination[property]; 1681 if (typeof (destProp) === OBJECT) { 1682 destination[property] = destProp || {}; 1683 } else { 1684 destination[property] = {}; 1685 } 1686 deepExtendOne(destination[property], propValue); 1687 } else if (propType !== UNDEFINED) { 1688 destination[property] = propValue; 1689 } 1690 } 1691 1692 return destination; 1693 } 1694 1695 /** 1696 * Contains results from feature detection. 1697 * @name kendo.support 1698 * @namespace Contains results from feature detection. 1699 */ 1700 (function() { 1701 /** 1702 * Indicates the width of the browser scrollbar. A value of zero means that the browser does not show a visual representation of a scrollbar (i.e. mobile browsers). 1703 * @name kendo.support.scrollbar 1704 * @property {Boolean} 1705 */ 1706 support.scrollbar = function() { 1707 var div = document.createElement("div"), 1708 result; 1709 1710 div.style.cssText = "overflow:scroll;overflow-x:hidden;zoom:1;clear:both"; 1711 div.innerHTML = " "; 1712 document.body.appendChild(div); 1713 1714 result = div.offsetWidth - div.scrollWidth; 1715 1716 document.body.removeChild(div); 1717 return result; 1718 }; 1719 1720 var table = document.createElement("table"); 1721 1722 // Internet Explorer does not support setting the innerHTML of TBODY and TABLE elements 1723 try { 1724 table.innerHTML = "<tr><td></td></tr>"; 1725 1726 /** 1727 * Indicates whether the browser supports setting of the <tbody> innerHtml. 1728 * @name kendo.support.tbodyInnerHtml 1729 * @property {Boolean} 1730 */ 1731 support.tbodyInnerHtml = true; 1732 } catch (e) { 1733 support.tbodyInnerHtml = false; 1734 } 1735 1736 /** 1737 * Indicates whether the browser supports touch events. 1738 * @name kendo.support.touch 1739 * @property {Boolean} 1740 */ 1741 support.touch = "ontouchstart" in window; 1742 support.pointers = navigator.msPointerEnabled; 1743 1744 /** 1745 * Indicates whether the browser supports CSS transitions. 1746 * @name kendo.support.transitions 1747 * @property {Boolean} 1748 */ 1749 var transitions = support.transitions = false; 1750 var transforms = support.transforms = false; 1751 1752 /** 1753 * Indicates whether the browser supports hardware 3d transitions. 1754 * @name kendo.support.hasHW3D 1755 * @property {Boolean} 1756 */ 1757 support.hasHW3D = ("WebKitCSSMatrix" in window && "m11" in new window.WebKitCSSMatrix()) || "MozPerspective" in document.documentElement.style; 1758 support.hasNativeScrolling = typeof document.documentElement.style.webkitOverflowScrolling == "string"; 1759 1760 each([ "Moz", "webkit", "O", "ms" ], function () { 1761 var prefix = this.toString(), 1762 hasTransitions = typeof table.style[prefix + "Transition"] === STRING; 1763 1764 if (hasTransitions || typeof table.style[prefix + "Transform"] === STRING) { 1765 var lowPrefix = prefix.toLowerCase(); 1766 1767 transforms = { 1768 css: "-" + lowPrefix + "-", 1769 prefix: prefix, 1770 event: (lowPrefix === "o" || lowPrefix === "webkit") ? lowPrefix : "" 1771 }; 1772 1773 if (hasTransitions) { 1774 transitions = transforms;
1775 transitions.event = transitions.event ? transitions.event + "TransitionEnd" : "transitionend"; 1776 } 1777 1778 return false; 1779 } 1780 }); 1781 1782 support.transforms = transforms; 1783 support.transitions = transitions; 1784 1785 support.detectOS = function (ua) { 1786 var os = false, minorVersion, match = [], 1787 agentRxs = { 1788 fire: /(Silk)\/(\d+)\.(\d+(\.\d+)?)/, 1789 android: /(Android|Android.*(?:Opera|Firefox).*?\/)\s*(\d+)\.(\d+(\.\d+)?)/, 1790 iphone: /(iPhone|iPod).*OS\s+(\d+)[\._]([\d\._]+)/, 1791 ipad: /(iPad).*OS\s+(\d+)[\._]([\d_]+)/, 1792 meego: /(MeeGo).+NokiaBrowser\/(\d+)\.([\d\._]+)/, 1793 webos: /(webOS)\/(\d+)\.(\d+(\.\d+)?)/, 1794 blackberry: /(BlackBerry).*?Version\/(\d+)\.(\d+(\.\d+)?)/, 1795 playbook: /(PlayBook).*?Tablet\s*OS\s*(\d+)\.(\d+(\.\d+)?)/, 1796 winphone: /(IEMobile)\/(\d+)\.(\d+(\.\d+)?)/, 1797 windows: /(MSIE)\s+(\d+)\.(\d+(\.\d+)?)/ 1798 }, 1799 osRxs = { 1800 ios: /^i(phone|pad|pod)$/i, 1801 android: /^android|fire$/i, 1802 blackberry: /^blackberry|playbook/i, 1803 windows: /windows|winphone/ 1804 }, 1805 browserRxs = { 1806 omini: /Opera\sMini/i, 1807 omobile: /Opera\sMobi/i, 1808 firefox: /Firefox|Fennec/i, 1809 webkit: /webkit/i, 1810 ie: /MSIE|Windows\sPhone/i 1811 }, 1812 testOs = function (agent) { 1813 for (var os in osRxs) { 1814 if (osRxs.hasOwnProperty(os) && osRxs[os].test(agent)) { 1815 return os; 1816 } 1817 } 1818 return agent; 1819 }, 1820 testBrowser = function (ua) { 1821 for (var browser in browserRxs) { 1822 if (browserRxs.hasOwnProperty(browser) && browserRxs[browser].test(ua)) { 1823 return browser; 1824 } 1825 } 1826 return "default"; 1827 }; 1828 1829 for (var agent in agentRxs) { 1830 if (agentRxs.hasOwnProperty(agent)) { 1831 match = ua.match(agentRxs[agent]);
1832 if (match) { 1833 if (agent == "windows" && "plugins" in navigator) { return false; } // Break if not Metro/Mobile Windows 1834 1835 os = {}; 1836 os.device = agent; 1837 os.browser = testBrowser(ua); 1838 os.name = testOs(agent); 1839 os[os.name] = true; 1840 os.majorVersion = match[2]; 1841 os.minorVersion = match[3].replace("_", "."); 1842 minorVersion = os.minorVersion.replace(".", "").substr(0, 2); 1843 os.flatVersion = os.majorVersion + minorVersion + (new Array(3 - (minorVersion.length < 3 ? minorVersion.length : 2)).join("0")); 1844 os.appMode = window.navigator.standalone || (/file|local/).test(window.location.protocol) || typeof window.PhoneGap !== UNDEFINED; // Use file protocol to detect appModes. 1845 1846 break; 1847 } 1848 } 1849 } 1850 return os; 1851 }; 1852 1853 /** 1854 * Parses the mobile OS type and version from the browser user agent. 1855 * @name kendo.support.mobileOS 1856 */ 1857 support.mobileOS = support.detectOS(navigator.userAgent); 1858 1859 support.zoomLevel = function() { 1860 return support.touch ? (document.documentElement.clientWidth / window.innerWidth) : 1; 1861 }; 1862 1863 /** 1864 * Indicates the browser device pixel ratio. 1865 * @name kendo.support.devicePixelRatio 1866 * @property {Float} 1867 */ 1868 support.devicePixelRatio = window.devicePixelRatio === undefined ? 1 : window.devicePixelRatio; 1869 1870 /** 1871 * Indicates the browser support for event capturing 1872 * @name kendo.support.eventCapture 1873 * @property {Boolean} 1874 */ 1875 support.eventCapture = document.documentElement.addEventListener; 1876 1877 /** 1878 * Indicates whether the browser supports input placeholder. 1879 * @name kendo.support.placeholder 1880 * @property {Boolean} 1881 */ 1882 support.placeholder = "placeholder" in document.createElement("input"); 1883 support.stableSort = (function() { 1884 var sorted = [0,1,2,3,4,5,6,7,8,9,10,11,12].sort(function() { return 0; } ); 1885 return sorted[0] === 0 && sorted[1] === 1 && sorted[2] === 2 && sorted[3] === 3 && sorted[4] === 4 && 1886 sorted[5] === 5 && sorted[6] === 6 && sorted[7] === 7 && sorted[8] === 8 && 1887 sorted[9] === 9 && sorted[10] === 10 && sorted[11] === 11 && sorted[12] === 12; 1888 })(); 1889 1890 })(); 1891 1892 /** 1893 * Exposed by jQuery. 1894 * @ignore 1895 * @name jQuery.fn 1896 * @namespace Handy jQuery plug-ins that are used by all Kendo widgets. 1897 */ 1898 1899 function size(obj) { 1900 var result = 0, key; 1901 for (key in obj) { 1902 if (obj.hasOwnProperty(key)) { 1903 result++; 1904 } 1905 } 1906 1907 return result; 1908 } 1909 1910 function isNodeEmpty(element) { 1911 return $.trim($(element).contents().filter(function () { return this.nodeType != 8; }).html()) === ""; 1912 } 1913 1914 function getOffset(element, type) { 1915 if (!type) { 1916 type = "offset"; 1917 } 1918 1919 var result = element[type](), 1920 mobileOS = support.mobileOS; 1921 1922 if (support.touch && mobileOS.ios && mobileOS.flatVersion < 410) { // Extra processing only in broken iOS' 1923 var offset = type == "offset" ? result : element.offset(), 1924 positioned = (result.left == offset.left && result.top == offset.top); 1925 1926 if (positioned) { 1927 return { 1928 top: result.top - window.scrollY, 1929 left: result.left - window.scrollX 1930 }; 1931 } 1932 } 1933 1934 return result; 1935 } 1936 1937 var directions = { 1938 left: { reverse: "right" }, 1939 right: { reverse: "left" }, 1940 down: { reverse: "up" }, 1941 up: { reverse: "down" }, 1942 top: { reverse: "bottom" }, 1943 bottom: { reverse: "top" }, 1944 "in": { reverse: "out" }, 1945 out: { reverse: "in" } 1946 }; 1947 1948 function parseEffects(input) { 1949 var effects = {}; 1950 1951 each((typeof input === "string" ? input.split(" ") : input), function(idx) { 1952 effects[idx] = this; 1953 }); 1954 1955 return effects; 1956 } 1957 1958 var fx = { 1959 promise: function (element, options) { 1960 if (options.show) { 1961 element.css({ display: element.data("olddisplay") || "block" }).css("display"); 1962 } 1963 1964 if (options.hide) { 1965 element.data("olddisplay", element.css("display")).hide(); 1966 } 1967 1968 if (options.init) { 1969 options.init(); 1970 } 1971 1972 if (options.completeCallback) { 1973 options.completeCallback(element);
1973 // call the external complete callback with the element 1974 } 1975 1976 element.dequeue(); 1977 }, 1978 1979 transitionPromise: function(element, destination, options) { 1980 var container = kendo.wrap(element); 1981 container.append(destination); 1982 1983 element.hide(); 1984 destination.show(); 1985 1986 if (options.completeCallback) { 1987 options.completeCallback(element); // call the external complete callback with the element 1988 } 1989 1990 return element; 1991 } 1992 }; 1993 1994 function prepareAnimationOptions(options, duration, reverse, complete) { 1995 if (typeof options === STRING) { 1996 // options is the list of effect names separated by space e.g. animate(element, "fadeIn slideDown") 1997 1998 // only callback is provided e.g. animate(element, options, function() {}); 1999 if (isFunction(duration)) { 2000 complete = duration; 2001 duration = 400; 2002 reverse = false; 2003 } 2004 2005 if (isFunction(reverse)) { 2006 complete = reverse; 2007 reverse = false; 2008 } 2009 2010 if (typeof duration === BOOLEAN){ 2011 reverse = duration; 2012 duration = 400; 2013 } 2014 2015 options = { 2016 effects: options, 2017 duration: duration, 2018 reverse: reverse, 2019 complete: complete 2020 }; 2021 } 2022 2023 return extend({ 2024 //default options 2025 effects: {}, 2026 duration: 400, //jQuery default duration 2027 reverse: false, 2028 init: noop, 2029 teardown: noop, 2030 hide: false, 2031 show: false 2032 }, options, { completeCallback: options.complete, complete: noop }); // Move external complete callback, so deferred.resolve can be always executed. 2033 2034 } 2035 2036 function animate(element, options, duration, reverse, complete) { 2037 element.each(function (idx, el) { // fire separate queues on every element to separate the callback elements 2038 el = $(el); 2039 el.queue(function () { 2040 fx.promise(el, prepareAnimationOptions(options, duration, reverse, complete)); 2041 }); 2042 }); 2043 2044 return element; 2045 } 2046 2047 function animateTo(element, destination, options, duration, reverse, complete) { 2048 return fx.transitionPromise(element, destination, prepareAnimationOptions(options, duration, reverse, complete)); 2049 } 2050 2051 extend($.fn, /** @lends jQuery.fn */{ 2052 kendoStop: function(clearQueue, gotoEnd) { 2053 return this.stop(clearQueue, gotoEnd); 2054 }, 2055 2056 kendoAnimate: function(options, duration, reverse, complete) { 2057 return animate(this, options, duration, reverse, complete); 2058 }, 2059 2060 kendoAnimateTo: function(destination, options, duration, reverse, complete) { 2061 return animateTo(this, destination, options, duration, reverse, complete); 2062 } 2063 }); 2064 2065 function toggleClass(element, classes, options, add) { 2066 if (classes) { 2067 classes = classes.split(" "); 2068 2069 each(classes, function(idx, value) { 2070 element.toggleClass(value, add); 2071 }); 2072 } 2073 2074 return element; 2075 } 2076 2077 extend($.fn, /** @lends jQuery.fn */{ 2078 kendoAddClass: function(classes, options){ 2079 return toggleClass(this, classes, options, true); 2080 }, 2081 kendoRemoveClass: function(classes, options){ 2082 return toggleClass(this, classes, options, false); 2083 }, 2084 kendoToggleClass: function(classes, options, toggle){ 2085 return toggleClass(this, classes, options, toggle); 2086 } 2087 }); 2088 2089 var ampRegExp = /&/g, 2090 ltRegExp = /</g, 2091 gtRegExp = />/g; 2092 /** 2093 * Encodes HTML characters to entities. 2094 * @name kendo.htmlEncode 2095 * @function 2096 * @param {String} value The string that needs to be HTML encoded. 2097 * @returns {String} The encoded string. 2098 */ 2099 function htmlEncode(value) { 2100 return ("" + value).replace(ampRegExp, "&").replace(ltRegExp, "<").replace(gtRegExp, ">"); 2101 } 2102 2103 var touchLocation = function(e) { 2104 return { 2105 idx: 0, 2106 x: e.pageX, 2107 y: e.pageY 2108 }; 2109 }; 2110 2111 var eventTarget = function (e) { 2112 return e.target; 2113 }; 2114 2115 if (support.touch) { 2116 /** @ignore */ 2117 touchLocation = function(e, id) { 2118 var changedTouches = e.changedTouches || e.originalEvent.changedTouches; 2119 2120 if (id) { 2121 var output = null; 2122 each(changedTouches, function(idx, value) { 2123 if (id == value.identifier) { 2124 output = { 2125 idx: value.identifier, 2126 x: value.pageX, 2127 y: value.pageY 2128 }; 2129 } 2130 }); 2131 return output; 2132 } else { 2133 return { 2134 idx: changedTouches[0].identifier, 2135 x: changedTouches[0].pageX, 2136 y: changedTouches[0].pageY 2137 }; 2138 } 2139 }; 2140 2141 eventTarget = function(e) { 2142 var touches = "originalEvent" in e ? e.originalEvent.changedTouches : "changedTouches" in e ? e.changedTouches : null; 2143 2144 return touches ? document.elementFromPoint(touches[0].clientX, touches[0].clientY) : null; 2145 }; 2146
2147 each(["swipe", "swipeLeft", "swipeRight", "swipeUp", "swipeDown", "doubleTap", "tap"], function(m, value) { 2148 $.fn[value] = function(callback) { 2149 return this.bind(value, callback); 2150 }; 2151 }); 2152 } 2153 2154 if (support.touch) { 2155 support.mousedown = "touchstart"; 2156 support.mouseup = "touchend"; 2157 support.mousemove = "touchmove"; 2158 support.mousecancel = "touchcancel"; 2159 } else { 2160 support.mousemove = "mousemove"; 2161 support.mousedown = "mousedown"; 2162 support.mouseup = "mouseup"; 2163 support.mousecancel = "mouseleave"; 2164 } 2165 2166 var wrapExpression = function(members) { 2167 var result = "d", 2168 index, 2169 idx, 2170 length, 2171 member, 2172 count = 1; 2173 2174 for (idx = 0, length = members.length; idx < length; idx++) { 2175 member = members[idx]; 2176 if (member !== "") { 2177 index = member.indexOf("["); 2178 2179 if (index !== 0) { 2180 if (index == -1) { 2181 member = "." + member; 2182 } else { 2183 count++; 2184 member = "." + member.substring(0, index) + " || {})" + member.substring(index); 2185 } 2186 } 2187 2188 count++; 2189 result += member + ((idx < length - 1) ? " || {})" : ")"); 2190 } 2191 } 2192 return new Array(count).join("(") + result; 2193 }, 2194 localUrlRe = /^([a-z]+:)?\/\//i; 2195 2196 extend(kendo, /** @lends kendo */ { 2197 /** 2198 * @name kendo.ui 2199 * @namespace Contains all classes for the Kendo UI widgets. 2200 */ 2201 ui: kendo.ui || {}, 2202 fx: kendo.fx || fx, 2203 data: kendo.data || {}, 2204 mobile: kendo.mobile || {}, 2205 keys: { 2206 INSERT: 45, 2207 DELETE: 46, 2208 BACKSPACE: 8, 2209 TAB: 9, 2210 ENTER: 13, 2211 ESC: 27, 2212 LEFT: 37, 2213 UP: 38, 2214 RIGHT: 39, 2215 DOWN: 40, 2216 END: 35, 2217 HOME: 36, 2218 SPACEBAR: 32, 2219 PAGEUP: 33, 2220 PAGEDOWN: 34, 2221 F10: 121, 2222 F12: 123 2223 }, 2224 support: support, 2225 animate: animate, 2226 ns: "", 2227 attr: function(value) { 2228 return "data-" + kendo.ns + value; 2229 }, 2230 wrap: wrap, 2231 deepExtend: deepExtend, 2232 size: size, 2233 isNodeEmpty: isNodeEmpty, 2234 getOffset: getOffset, 2235 parseEffects: parseEffects, 2236 toggleClass: toggleClass, 2237 directions: directions, 2238 Observable: Observable, 2239 Class: Class, 2240 Template: Template, 2241 /** 2242 * Shorthand for {@link kendo.Template.compile}. 2243 * @name kendo.template 2244 * @function 2245 */ 2246 template: proxy(Template.compile, Template), 2247 /** 2248 * Shorthand for {@link kendo.Template.render}. 2249 * @name kendo.render 2250 * @function 2251 */ 2252 render: proxy(Template.render, Template), 2253 stringify: proxy(JSON.stringify, JSON), 2254 touchLocation: touchLocation, 2255 eventTarget: eventTarget, 2256 htmlEncode: htmlEncode, 2257 isLocalUrl: function(url) { 2258 return url && !localUrlRe.test(url); 2259 }, 2260 /** @ignore */ 2261 expr: function(expression, safe) { 2262 expression = expression || ""; 2263 2264 if (expression && expression.charAt(0) !== "[") { 2265 expression = "." + expression; 2266 } 2267 2268 if (safe) { 2269 expression = wrapExpression(expression.split(".")); 2270 } else { 2271 expression = "d" + expression; 2272 } 2273 2274 return expression; 2275 }, 2276 /** @ignore */ 2277 getter: function(expression, safe) { 2278 return getterCache[expression] = getterCache[expression] || new Function("d", "return " + kendo.expr(expression, safe)); 2279 }, 2280 /** @ignore */ 2281 setter: function(expression) { 2282 return setterCache[expression] = setterCache[expression] || new Function("d,value", "d." + expression + "=value"); 2283 }, 2284 /** @ignore */ 2285 accessor: function(expression) { 2286 return {
2287 get: kendo.getter(expression), 2288 set: kendo.setter(expression) 2289 }; 2290 }, 2291 /** @ignore */ 2292 guid: function() { 2293 var id = "", i, random; 2294 2295 for (i = 0; i < 32; i++) { 2296 random = math.random() * 16 | 0; 2297 2298 if (i == 8 || i == 12 || i == 16 || i == 20) { 2299 id += "-"; 2300 } 2301 id += (i == 12 ? 4 : (i == 16 ? (random & 3 | 8) : random)).toString(16); 2302 } 2303 2304 return id; 2305 }, 2306 2307 roleSelector: function(role) { 2308 return role.replace(/(\S+)/g, "[" + kendo.attr("role") + "=$1],").slice(0, -1); 2309 }, 2310 2311 /** @ignore */ 2312 logToConsole: function(message) { 2313 var console = window.console; 2314 2315 if (typeof(console) != "undefined" && console.log) { 2316 console.log(message); 2317 } 2318 } 2319 }); 2320 2321 var Widget = Observable.extend( /** @lends kendo.ui.Widget.prototype */ { 2322 /** 2323 * Initializes widget. Sets `element` and `options` properties. 2324 * @constructs 2325 * @class Represents a UI widget. Base class for all Kendo widgets 2326 * @extends kendo.Observable 2327 */ 2328 init: function(element, options) { 2329 var that = this; 2330 2331 that.element = $(element); 2332 2333 Observable.fn.init.call(that); 2334 2335 that.options = extend(true, {}, that.options, options); 2336 2337 if (!that.element.attr(kendo.attr("role"))) { 2338 that.element.attr(kendo.attr("role"), (that.options.name || "").toLowerCase()); 2339 } 2340 2341 that.element.data("kendo" + that.options.prefix + that.options.name, that); 2342 2343 that.bind(that.events, that.options); 2344 }, 2345 2346 events: [], 2347 2348 options: { 2349 prefix: "" 2350 }, 2351 2352 setOptions: function(options) { 2353 $.extend(this.options, options); 2354 2355 this.bind(this.events, options); 2356 } 2357 }); 2358 2359 kendo.notify = noop; 2360 2361 var templateRegExp = /template$/i, 2362 jsonRegExp = /^(?:\{(?:.|\n)*\}|\[(?:.|\n)*\])$/, 2363 jsonFormatRegExp = /^\{(\d+)(:[^\}]+)?\}/, 2364 dashRegExp = /([A-Z])/g; 2365 2366 function parseOption(element, option) { 2367 var value; 2368 2369 if (option.indexOf("data") === 0) { 2370 option = option.substring(4); 2371 option = option.charAt(0).toLowerCase() + option.substring(1); 2372 } 2373 2374 option = option.replace(dashRegExp, "-$1"); 2375 value = element.getAttribute("data-" + kendo.ns + option); 2376 2377 if (value === null) { 2378 value = undefined; 2379 } else if (value === "null") { 2380 value = null; 2381 } else if (value === "true") { 2382 value = true; 2383 } else if (value === "false") { 2384 value = false; 2385 } else if (!isNaN(parseFloat(value))) { 2386 value = parseFloat(value); 2387 } else if (jsonRegExp.test(value) && !jsonFormatRegExp.test(value)) { 2388 value = $.parseJSON(value); 2389 } 2390 2391 return value; 2392 } 2393 2394 function parseOptions(element, options) { 2395 var result = {}, 2396 option, 2397 value; 2398 2399 for (option in options) { 2400 value = parseOption(element, option); 2401 2402 if (value !== undefined) { 2403 2404 if (templateRegExp.test(option)) { 2405 value = kendo.template($("#" + value).html()); 2406 } 2407 2408 result[option] = value; 2409 } 2410 } 2411 2412 return result; 2413 } 2414 2415 kendo.initWidget = function(element, options, namespace) { 2416 var result, 2417 option, 2418 widget, 2419 idx, 2420 length, 2421 role, 2422 value, 2423 dataSource; 2424 2425 element = element.nodeType ? element : element[0]; 2426 2427 role = element.getAttribute("data-" + kendo.ns + "role"); 2428 2429 if (!role) { 2430 return; 2431 } 2432 2433 dataSource = parseOption(element, "dataSource"); 2434 2435 widget = (namespace || kendo.ui).roles[role]; 2436 2437 if (!widget) { 2438 return; 2439 } 2440 2441 options = $.extend({}, parseOptions(element, widget.fn.options), options); 2442 2443 if (dataSource) { 2444 if (typeof dataSource === STRING) { 2445 options.dataSource = kendo.getter(dataSource)(window); 2446 } else { 2447 options.dataSource = dataSource; 2448 } 2449 } 2450 2451 for (idx = 0, length = widget.fn.events.length; idx < length; idx++) { 2452 option = widget.fn.events[idx]; 2453 2454 value = parseOption(element, option); 2455 2456 if (value !== undefined) { 2457 options[option] = kendo.getter(value)(window); 2458 } 2459 } 2460 2461 result = $(element).data("kendo" + widget.fn.options.name); 2462 2463 if (!result) { 2464 result = new widget(element, options); 2465 } else { 2466 result.setOptions(options); 2467 } 2468 2469 return result; 2470 }; 2471
2472 kendo.init = function(element, namespace) { 2473 $(element).find("[data-" + kendo.ns + "role]").andSelf().each(function(){ 2474 kendo.initWidget(this, {}, namespace); 2475 }); 2476 }; 2477 2478 kendo.parseOptions = parseOptions; 2479 2480 extend(kendo.ui, /** @lends kendo.ui */{ 2481 Widget: Widget, 2482 roles: {}, 2483 /** 2484 * Shows an overlay with a loading message, indicating that an action is in progress. 2485 * @name kendo.ui.progress 2486 * @function 2487 * @param {jQueryObject} container The container that will hold the overlay 2488 * @param {Boolean} toggle Whether the overlay should be shown or hidden 2489 */ 2490 progress: function(container, toggle) { 2491 var mask = container.find(".k-loading-mask"); 2492 2493 if (toggle) { 2494 if (!mask.length) { 2495 mask = $("<div class='k-loading-mask'><span class='k-loading-text'>Loading...</span><div class='k-loading-image'/><div class='k-loading-color'/></div>") 2496 .width("100%").height("100%") 2497 .prependTo(container) 2498 .css({ top: container.scrollTop(), left: container.scrollLeft() }); 2499 } 2500 } else if (mask) { 2501 mask.remove(); 2502 } 2503 }, 2504 /** 2505 * Helper method for writing new widgets. 2506 * Exposes a jQuery plug-in that will handle the widget creation and attach its client-side object in the appropriate data-* attribute. 2507 * @name kendo.ui.plugin 2508 * @function 2509 * @param {kendo.ui.Widget} widget The widget function. 2510 * @param {Object} register <kendo.ui> The object where the reference to the widget is recorded. 2511 * @param {Object} prefix <""> The plugin function prefix, e.g. "Mobile" will register "kendoMobileFoo". 2512 * @example 2513 * function TextBox(element, options); 2514 * kendo.ui.plugin(TextBox); 2515 * 2516 * // initialize a new TextBox for each input, with the given options object. 2517 * $("input").kendoTextBox({ }); 2518 * // get the TextBox object and call the value API method 2519 * $("input").data("kendoTextBox").value(); 2520 */ 2521 plugin: function(widget, register, prefix) { 2522 var name = widget.fn.options.name; 2523 2524 register = register || kendo.ui; 2525 prefix = prefix || ""; 2526 2527 register[name] = widget; 2528 2529 register.roles[name.toLowerCase()] = widget; 2530 2531 name = "kendo" + prefix + name; 2532 $.fn[name] = function(options) { 2533 var value = this, 2534 args; 2535 2536 if (typeof options === STRING) { 2537 args = slice.call(arguments, 1); 2538 2539 this.each(function(){ 2540 var widget = $.data(this, name), 2541 method, 2542 result; 2543 2544 if (!widget) { 2545 throw new Error(kendo.format("Cannot call method '{0}' of {1} before it is initialized", options, name)); 2546 } 2547 2548 method = widget[options]; 2549 2550 if (typeof method !== FUNCTION) { 2551 throw new Error(kendo.format("Cannot find method '{0}' of {1}", options, name)); 2552 } 2553 2554 result = method.apply(widget, args); 2555 2556 if (result !== undefined) { 2557 value = result; 2558 return false; 2559 } 2560 }); 2561 } else { 2562 this.each(function() { 2563 new widget(this, options); 2564 }); 2565 } 2566 2567 return value; 2568 }; 2569 } 2570 }); 2571 2572 var MobileWidget = Widget.extend(/** @lends kendo.mobile.ui.Widget.prototype */{ 2573 /** 2574 * Initializes mobile widget. Sets `element` and `options` properties. 2575 * @constructs 2576 * @class Represents a mobile UI widget. Base class for all Kendo mobile widgets. 2577 * @extends kendo.ui.Widget 2578 */ 2579 init: function(element, options) { 2580 Widget.fn.init.call(this, element, options); 2581 this.wrapper = this.element; 2582 }, 2583 2584 options: { 2585 prefix: "Mobile" 2586 }, 2587 2588 events: [], 2589 2590 viewShow: $.noop, 2591 viewInit: $.noop, 2592 2593 view: function() { 2594 return this.element.closest(kendo.roleSelector("view")).data("kendoMobileView"); 2595 } 2596 });
2597 2598 /** 2599 * @name kendo.mobile 2600 * @namespace This object contains all code introduced by the Kendo mobile suite, plus helper functions that are used across all mobile widgets. 2601 */ 2602 extend(kendo.mobile, { 2603 init: function(element) { 2604 kendo.init(element, kendo.mobile.ui); 2605 }, 2606 2607 /** 2608 * @name kendo.mobile.ui 2609 * @namespace Contains all classes for the Kendo Mobile UI widgets. 2610 */ 2611 ui: { 2612 Widget: MobileWidget, 2613 roles: {}, 2614 plugin: function(widget) { 2615 kendo.ui.plugin(widget, kendo.mobile.ui, "Mobile"); 2616 } 2617 } 2618 }); 2619 2620 /** 2621 * Enables kinetic scrolling on touch devices 2622 * @name kendo.touchScroller 2623 * @function 2624 * @param {Selector} element The container element to enable scrolling for. 2625 */ 2626 kendo.touchScroller = function(element, options) { 2627 if (support.touch && kendo.mobile.ui.Scroller) { 2628 element.kendoMobileScroller(options); 2629 return element.data("kendoMobileScroller"); 2630 } else { 2631 return false; 2632 } 2633 }; 2634 2635 /** 2636 * Prevents the default event action. Should be supplied as an event callback 2637 * @name kendo.preventDefault 2638 * @function 2639 */ 2640 kendo.preventDefault = function(e) { 2641 e.preventDefault(); 2642 }; 2643 2644 /** 2645 * Retrieves the widget for a given element (if any) 2646 */ 2647 kendo.widgetInstance = function(element, suite) { 2648 var widget = suite.roles[element.data(kendo.ns + "role")]; 2649 2650 if (widget) { 2651 return element.data("kendo" + widget.fn.options.prefix + widget.fn.options.name); 2652 } 2653 }; 2654})(jQuery); 2655(function($, undefined) { 2656 /** 2657 * @name kendo.fx
2658 * @namespace This object contains the fx library that is used by all widgets using animation. 2659 * If this file is not included, all animations will be disabled but the basic functionality preserved. 2660 */ 2661 var kendo = window.kendo, 2662 fx = kendo.fx, 2663 each = $.each, 2664 extend = $.extend, 2665 proxy = $.proxy, 2666 size = kendo.size, 2667 browser = $.browser, 2668 support = kendo.support, 2669 transforms = support.transforms, 2670 transitions = support.transitions, 2671 scaleProperties = { scale: 0, scaleX: 0, scaleY: 0, scale3d: 0 }, 2672 translateProperties = { translate: 0, translateX: 0, translateY: 0, translate3d: 0 }, 2673 hasZoom = (typeof document.documentElement.style.zoom !== "undefined") && !transitions, 2674 matrix3dRegExp = /matrix3?d?\s*\(.*,\s*([\d\w\.\-]+),\s*([\d\w\.\-]+),\s*([\d\w\.\-]+)/, 2675 cssParamsRegExp = /^(-?[\d\.\-]+)?[\w\s]*,?\s*(-?[\d\.\-]+)?[\w\s]*/i, 2676 translateXRegExp = /translatex?$/i, 2677 oldEffectsRegExp = /(zoom|fade|expand)(\w+)/, 2678 singleEffectRegExp = /(zoom|fade|expand)/, 2679 transformProps = ["perspective", "rotate", "rotateX", "rotateY", "rotateZ", "rotate3d", "scale", "scaleX", "scaleY", "scaleZ", "scale3d", "skew", "skewX", "skewY", "translate", "translateX", "translateY", "translateZ", "translate3d", "matrix", "matrix3d"], 2680 cssPrefix = transforms.css, 2681 round = Math.round, 2682 BLANK = "", 2683 PX = "px", 2684 NONE = "none", 2685 AUTO = "auto", 2686 WIDTH = "width", 2687 HEIGHT = "height", 2688 HIDDEN = "hidden", 2689 ORIGIN = "origin", 2690 ABORT_ID = "abortId", 2691 OVERFLOW = "overflow", 2692 TRANSLATE = "translate", 2693 TRANSITION = cssPrefix + "transition", 2694 TRANSFORM = cssPrefix + "transform"; 2695 2696 kendo.directions = { 2697 left: { 2698 reverse: "right", 2699 property: "left", 2700 transition: "translateX", 2701 vertical: false, 2702 modifier: -1 2703 }, 2704 right: { 2705 reverse: "left", 2706 property: "left", 2707 transition: "translateX", 2708 vertical: false, 2709 modifier: 1 2710 }, 2711 down: { 2712 reverse: "up", 2713 property: "top", 2714 transition: "translateY", 2715 vertical: true, 2716 modifier: 1 2717 }, 2718 up: { 2719 reverse: "down", 2720 property: "top", 2721 transition: "translateY", 2722 vertical: true, 2723 modifier: -1 2724 }, 2725 top: { 2726 reverse: "bottom" 2727 }, 2728 bottom: { 2729 reverse: "top" 2730 }, 2731 "in": { 2732 reverse: "out", 2733 modifier: -1 2734 }, 2735 out: { 2736 reverse: "in", 2737 modifier: 1 2738 } 2739 }; 2740 2741 extend($.fn, { 2742 kendoStop: function(clearQueue, gotoEnd) { 2743 if (transitions) { 2744 return kendo.fx.stopQueue(this, clearQueue || false, gotoEnd || false); 2745 } else { 2746 return this.stop(clearQueue, gotoEnd); 2747 } 2748 } 2749 }); 2750 2751 /* jQuery support for scale transform animation (FF 3.5/3.6, Opera 10.x */ 2752 $.fn.scale = function (val) { 2753 if (typeof val == "undefined") { 2754 return animationProperty(this, "scale"); 2755 } else { 2756 $(this).css(TRANSFORM, "scale(" + val + ")"); 2757 } 2758 return this; 2759 }; 2760 2761 var curProxy = $.fx.prototype.cur; 2762 $.fx.prototype.cur = function () { 2763 if (this.prop == "scale") { 2764 return parseFloat($(this.elem).scale()); 2765 } 2766 2767 return curProxy.apply(this, arguments); 2768 }; 2769 2770 $.fx.step.scale = function (fx) { 2771 $(fx.elem).scale(fx.now); 2772 }; 2773 2774 kendo.toggleClass = function(element, classes, options, add) { 2775 if (classes) { 2776 classes = classes.split(" "); 2777 2778 if (transitions) { 2779 options = extend({ 2780 exclusive: "all", 2781 duration: 400, 2782 ease: "ease-out" 2783 }, options); 2784 2785 element.css(TRANSITION, options.exclusive + " " + options.duration + "ms " + options.ease); 2786 setTimeout(function() { 2787 element.css(TRANSITION, NONE).css(HEIGHT); 2788 }, options.duration); // TODO: this should fire a kendoAnimate session instead. 2789 } 2790 2791 each(classes, function(idx, value) { 2792 element.toggleClass(value, add); 2793 }); 2794 } 2795 2796 return element; 2797 }; 2798
2799 kendo.parseEffects = function(input, mirror) { 2800 var effects = {}; 2801 2802 if (typeof input === "string") { 2803 each(input.split(" "), function(idx, value) { 2804 var redirectedEffect = !singleEffectRegExp.test(value), 2805 resolved = value.replace(oldEffectsRegExp, function(match, $1, $2) { 2806 return $1 + ":" + $2.toLowerCase(); 2807 }), // Support for old zoomIn/fadeOut style, now deprecated. 2808 effect = resolved.split(":"), 2809 direction = effect[1], 2810 effectBody = {}; 2811 2812 if (effect.length > 1) { 2813 effectBody.direction = (mirror && redirectedEffect ? kendo.directions[direction].reverse : direction); 2814 } 2815 2816 effects[effect[0]] = effectBody; 2817 }); 2818 } else { 2819 each(input, function(idx) { 2820 var direction = this.direction; 2821 2822 if (direction && mirror && !singleEffectRegExp.test(idx)) { 2823 this.direction = kendo.directions[direction].reverse; 2824 } 2825 2826 effects[idx] = this; 2827 }); 2828 } 2829 2830 return effects; 2831 }; 2832 2833 function parseInteger(value) { 2834 return parseInt(value, 10); 2835 } 2836 2837 function parseCSS(element, property) { 2838 return parseInteger(element.css(property)); 2839 } 2840 2841 function getComputedStyles(element, properties) { 2842 var styles = {}; 2843 2844 if (properties) { 2845 if (document.defaultView && document.defaultView.getComputedStyle) { 2846 var computedStyle = document.defaultView.getComputedStyle(element, ""); 2847 2848 each(properties, function(idx, value) { 2849 styles[value] = computedStyle.getPropertyValue(value); 2850 }); 2851 } else 2852 if (element.currentStyle) { // Not really needed 2853 var style = element.currentStyle; 2854 2855 each(properties, function(idx, value) { 2856 styles[value] = style[value.replace(/\-(\w)/g, function (strMatch, g1) { return g1.toUpperCase(); })]; 2857 }); 2858 } 2859 } else { 2860 styles = document.defaultView.getComputedStyle(element, ""); 2861 } 2862 2863 return styles; 2864 } 2865 2866 function slideToSlideIn(options) { 2867 options.effects.slideIn = options.effects.slide; 2868 delete options.effects.slide; 2869 return options; 2870 } 2871 2872 function parseTransitionEffects(options) { 2873 var effects = options.effects, 2874 mirror; 2875 2876 if (effects === "zoom") { 2877 effects = "zoomIn fadeIn"; 2878 } 2879 if (effects === "slide") { 2880 effects = "slide:left"; 2881 } 2882 if (effects === "fade") { 2883 effects = "fadeIn"; 2884 } 2885 if (effects === "overlay") { 2886 effects = "slideIn:left"; 2887 } 2888 if (/^overlay:(.+)$/.test(effects)) { 2889 effects = "slideIn:" + RegExp.$1; 2890 } 2891 2892 mirror = options.reverse && /^(slide:)/.test(effects); 2893 2894 if (mirror) { 2895 delete options.reverse; 2896 } 2897 2898 options.effects = $.extend(kendo.parseEffects(effects, mirror), {show: true}); 2899 2900 return options; 2901 } 2902 2903 function keys(obj) { 2904 var acc = []; 2905 for (var propertyName in obj) { 2906 acc.push(propertyName); 2907 } 2908 return acc; 2909 } 2910 2911 function removeTransitionStyles(element) { 2912 element.css(TRANSITION, NONE); 2913 2914 if (!browser.safari) { 2915 element.css(HEIGHT); 2916 } 2917 } 2918 2919 function activateTask(currentTransition) { 2920 var element = currentTransition.object; 2921 2922 if (!currentTransition) { 2923 return; 2924 } 2925 2926 element.css(currentTransition.setup); 2927 element.css(TRANSITION); 2928 element.data(ABORT_ID, setTimeout(function() { 2929 2930 removeTransitionStyles(element); 2931 element.dequeue(); 2932 currentTransition.complete.call(element); 2933 2934 }, currentTransition.duration)); 2935 2936 element.css(currentTransition.CSS); 2937 } 2938 2939 if (transitions) { 2940 2941 extend(kendo.fx, { 2942 transition: function(element, properties, options) { 2943 2944 options = extend({ 2945 duration: 200, 2946 ease: "ease-out", 2947 complete: null, 2948 exclusive: "all" 2949 }, 2950 options 2951 ); 2952 2953 options.duration = $.fx ? $.fx.speeds[options.duration] || options.duration : options.duration; 2954 2955 var transforms = [], 2956 cssValues = {}, 2957 key; 2958 2959 for (key in properties) { 2960 if (transformProps.indexOf(key) != -1) { 2961 transforms.push(key + "(" + properties[key] + ")"); 2962 } else { 2963 cssValues[key] = properties[key]; 2964 } 2965 } 2966 2967 if (transforms.length) { 2968 cssValues[TRANSFORM] = transforms.join(" "); 2969 } 2970 2971 var currentTask = { 2972 keys: keys(cssValues), 2973 CSS: cssValues, 2974 object: element, 2975 setup: {}, 2976 duration: options.duration, 2977 complete: options.complete 2978 }; 2979 currentTask.setup[TRANSITION] = options.exclusive + " " + options.duration + "ms " + options.ease; 2980 2981 var oldKeys = element.data("keys") || []; 2982 $.merge(oldKeys, currentTask.keys); 2983 element.data("keys", $.unique(oldKeys)); 2984 2985 activateTask(currentTask); 2986 }, 2987 2988 stopQueue: function(element, clearQueue, gotoEnd) { 2989 2990 if (element.data(ABORT_ID)) { 2991 clearTimeout(element.data(ABORT_ID)); 2992 element.removeData(ABORT_ID); 2993 } 2994 2995 var that = this, cssValues, 2996 taskKeys = element.data("keys"), 2997 retainPosition = (gotoEnd === false && taskKeys); 2998 2999 if (retainPosition) { 3000 cssValues = getComputedStyles(element[0], taskKeys); 3001 } 3002 3003 removeTransitionStyles(element); 3004 3005 if (retainPosition) { 3006 element.css(cssValues); 3007 } 3008 3009 element.removeData("keys"); 3010 3011 if (that.complete) { 3012 that.complete.call(element); 3013 } 3014 3015 element.stop(clearQueue); 3016 return element; 3017 } 3018 3019 }); 3020 } 3021 3022 function animationProperty(element, property) { 3023 if (transforms) { 3024 var transform = element.css(TRANSFORM); 3025 if (transform == "none") { 3026 return property == "scale" ? 1 : 0; 3027 } 3028 3029 var match = transform.match(new RegExp(property + "\\s*\\(([\\d\\w\\.]+)")), 3030 computed = 0; 3031 3032 if (match) { 3033 computed = parseInteger(match[1]); 3034 } else { 3035 match = transform.match(matrix3dRegExp) || [0, 0, 0, 0]; 3036 3037 if (translateXRegExp.test(property)) { 3038 computed = parseInteger(match[2]); 3039 } else if (property.toLowerCase() == "translatey") { 3040 computed = parseInteger(match[3]); 3041 } else if (property.toLowerCase() == "scale") { 3042 computed = parseFloat(match[1]); 3043 } 3044 } 3045 3046 return computed; 3047 } else { 3048 return element.css(property); 3049 } 3050 } 3051 3052 kendo.fx.promise = function(element, options) { 3053 var promises = [], effects; 3054 3055 effects = kendo.parseEffects(options.effects); 3056 options.effects = effects; 3057 3058 element.data("animating", true); 3059 3060 var props = { keep: [], restore: [] }, css = {}, 3061 methods = { setup: [], teardown: [] }, properties = {}, 3062 3063 // create a promise for each effect 3064 promise = $.Deferred(function(deferred) { 3065 if (size(effects)) { 3066 var opts = extend({}, options, { complete: deferred.resolve });
3067 3068 each(effects, function(effectName, settings) { 3069 var effect = kendo.fx[effectName]; 3070 3071 if (effect) { 3072 var dir = kendo.directions[settings.direction]; 3073 if (settings.direction && dir) { 3074 settings.direction = (options.reverse ? dir.reverse : settings.direction); 3075 element.data(effectName, settings); 3076 } 3077 3078 opts = extend(true, opts, settings); 3079 3080 each(methods, function (idx) { 3081 if (effect[idx]) { 3082 methods[idx].push(effect[idx]); 3083 } 3084 }); 3085 3086 each(props, function(idx) { 3087 if (effect[idx]) { 3088 $.merge(props[idx], effect[idx]); 3089 } 3090 }); 3091 3092 if (effect.css) { 3093 css = extend(css, effect.css); 3094 } 3095 } 3096 }); 3097 3098 if (methods.setup.length) { 3099 each ($.unique(props.keep), function(idx, value) { 3100 if (!element.data(value)) { 3101 element.data(value, element.css(value)); 3102 } 3103 }); 3104 3105 if (options.show) { 3106 css = extend(css, { display: element.data("olddisplay") || "block" }); // Add show to the set 3107 } 3108 3109 var buffer = css.transform; 3110 delete css.transform; 3111 if (transforms) { 3112 css[TRANSFORM] = buffer; 3113 } 3114 3115 element.css(css); 3116 element.css("overflow"); // Nudge Chrome 3117 3118 each (methods.setup, function() { properties = extend(properties, this(element, opts)); }); 3119 3120 if (kendo.fx.animate) { 3121 options.init(); 3122 kendo.fx.animate(element, properties, opts); 3123 } 3124 3125 return; 3126 } 3127 } else if (options.show) { 3128 element.css({ display: element.data("olddisplay") || "block" }).css("display"); 3129 options.init(); 3130 } 3131 3132 deferred.resolve(); 3133 }).promise(); 3134 3135 promises.push(promise); 3136 3137 //wait for all effects to complete 3138 $.when.apply(null, promises).then(function() { 3139 element 3140 .removeData("animating") 3141 .dequeue(); // call next animation from the queue 3142 3143 if (options.hide) { 3144 element.data("olddisplay", element.css("display")).hide(); 3145 } 3146 3147 if (size(effects)) { 3148 var restore = function() { 3149 each ($.unique(props.restore), function(idx, value) { 3150 element.css(value, element.data(value)); 3151 }); 3152 }; 3153 3154 if ($.browser.msie) { 3155 setTimeout(restore, 0); // Again jQuery callback in IE. 3156 } 3157 else { 3158 restore(); 3159 } 3160 3161 each(methods.teardown, function() { this(element, options); }); // call the internal completion callbacks 3162 } 3163 3164 if (options.completeCallback) { 3165 options.completeCallback(element); // call the external complete callback with the element 3166 } 3167 }); 3168 }; 3169 3170 kendo.fx.transitionPromise = function(element, destination, options) { 3171 kendo.fx.animateTo(element, destination, options); 3172 return element; 3173 }; 3174 3175 extend(kendo.fx, { 3176 animate: function(elements, properties, options) { 3177 var useTransition = options.transition !== false; 3178 delete options.transition; 3179 3180 if (transitions && "transition" in fx && useTransition) { 3181 fx.transition(elements, properties, options); 3182 } else { 3183 elements.each(function() { 3184 var element = $(this), 3185 multiple = {}; 3186 3187 each(transformProps, function(idx, value) { // remove transforms to avoid IE and older browsers confusion 3188 var params, 3189 currentValue = properties ? properties[value]+ " " : null; // We need to match 3190 3191 if (currentValue) { 3192 var single = properties; 3193 3194 if (value in scaleProperties && properties[value] !== undefined) { 3195 params = currentValue.match(cssParamsRegExp); 3196 if (hasZoom) { 3197 var half = (1 - params[1]) / 2; 3198 extend(single, { 3199 zoom: +params[1], 3200 marginLeft: element.width() * half, 3201 marginTop: element.height() * half 3202 }); 3203 } else if (transforms) { 3204 extend(single, { 3205 scale: +params[0] 3206 }); 3207 } 3208 } else 3209 if (value in translateProperties && properties[value] !== undefined) { 3210 var position = element.css("position"), 3211 isFixed = (position == "absolute" || position == "fixed"); 3212 3213 if (!element.data(TRANSLATE)) { 3214 if (isFixed) { 3215 element.data(TRANSLATE, { 3216 top: parseCSS(element, "top") || 0, 3217 left: parseCSS(element, "left") || 0, 3218 bottom: parseCSS(element, "bottom"), 3219 right: parseCSS(element, "right") 3220 }); 3221 } else { 3222 element.data(TRANSLATE, { 3223 top: parseCSS(element, "marginTop") || 0, 3224 left: parseCSS(element, "marginLeft") || 0 3225 }); 3226 } 3227 } 3228 3229 var originalPosition = element.data(TRANSLATE); 3230 3231 params = currentValue.match(cssParamsRegExp); 3232 if (params) { 3233 3234 var dX = value == TRANSLATE + "Y" ? +null : +params[1], 3235 dY = value == TRANSLATE + "Y" ? +params[1] : +params[2]; 3236 3237 if (isFixed) { 3238 if (!isNaN(originalPosition.right)) { 3239 if (!isNaN(dX)) { extend(single, { right: originalPosition.right - dX }); } 3240 } else { 3241 if (!isNaN(dX)) { extend(single, { left: originalPosition.left + dX }); } 3242 } 3243 3244 if (!isNaN(originalPosition.bottom)) { 3245 if (!isNaN(dY)) { extend(single, { bottom: originalPosition.bottom - dY }); } 3246 } else { 3247 if (!isNaN(dY)) { extend(single, { top: originalPosition.top + dY }); } 3248 } 3249 } else { 3250 if (!isNaN(dX)) { extend(single, { marginLeft: originalPosition.left + dX }); } 3251 if (!isNaN(dY)) { extend(single, { marginTop: originalPosition.top + dY }); } 3252 } 3253 } 3254 } 3255 3256 if (!transforms && value != "scale" && value in single) { 3257 delete single[value]; 3258 } 3259 3260 if (single) { 3261 extend(multiple, single); 3262 } 3263 } 3264 }); 3265 3266 if ($.browser.msie) { 3267 delete multiple.scale; 3268 } 3269 3270 element.animate(multiple, { queue: false, show: false, hide: false, duration: options.duration, complete: options.complete }); // Stop animate from showing/hiding the element to be able to hide it later on. 3271 }); 3272 } 3273 }, 3274 3275 animateTo: function(element, destination, options) { 3276 var direction, 3277 commonParent = element.parents().filter(destination.parents()).first(), 3278 originalOverflow = commonParent.css(OVERFLOW); 3279 3280 options = parseTransitionEffects(options); 3281 commonParent.css(OVERFLOW, "hidden"); 3282 3283 $.each(options.effects, function(name, definition) { 3284 direction = direction || definition.direction; 3285 }); 3286 3287 function complete() { 3288 destination[0].style.cssText = ""; 3289 element[0].style.cssText = ""; // Removing the whole style attribute breaks Android. 3290 commonParent.css(OVERFLOW, originalOverflow); 3291 if (options.completeCallback) { 3292 options.completeCallback(); 3293 } 3294 } 3295 3296 options.complete = $.browser.msie ? function() { setTimeout(complete); } : complete; 3297 3298 if ("slide" in options.effects) { 3299 element.kendoAnimate(options); 3300 destination.kendoAnimate(slideToSlideIn(options)); 3301 } else { 3302 (options.reverse ? element : destination).kendoAnimate(options); 3303 } 3304 }, 3305 3306 fade: { 3307 css: { 3308 opacity: function() { 3309 var fade = $(this).data("fade"), 3310 inDirection = fade ? fade.direction == "in" : false; 3311 3312 return inDirection && !this.style.opacity ? 0 : undefined; 3313 } 3314 }, 3315 setup: function(element, options) { 3316 return extend({ opacity: options.effects.fade.direction == "out" ? 0 : 1 }, options.properties); 3317 } 3318 }, 3319 zoom: { 3320 css: { 3321 transform: function() { 3322 var zoom = $(this).data("zoom"), 3323 inDirection = zoom ? zoom.direction == "in" : false; 3324 3325 return inDirection && transitions ? "scale(.01)" : undefined; 3326 }, 3327 zoom: function() { 3328 var zoom = $(this).data("zoom"), 3329 inDirection = zoom ? zoom.direction == "in" : false; 3330 3331 return inDirection && hasZoom ? ".01" : undefined; 3332 } 3333 }, 3334 setup: function(element, options) { 3335 var reverse = options.effects.zoom.direction == "out"; 3336 3337 if (hasZoom) { 3338 var version = $.browser.version, 3339 style = element[0].currentStyle, 3340 width = style.width.indexOf("%") != -1 ? element.parent().width() : element.width(), 3341 height = style.height.indexOf("%") != -1 ? element.parent().height() : parseInt(style.height, 10), 3342 half = version < 9 && options.effects.fade ? 0 : (1 - (parseInt(element.css("zoom"), 10) / 100)) / 2; // Kill margins in IE7/8 if using fade 3343 3344 element.css({ 3345 marginLeft: width * (version < 8 ? 0 : half), 3346 marginTop: height * half 3347 }); 3348 } 3349 3350 return extend({ scale: reverse ? 0.01 : 1 }, options.properties); 3351 } 3352 }, 3353 slide: { 3354 setup: function(element, options) { 3355 var direction = kendo.directions[options.effects.slide.direction], 3356 extender = {}, offset, reverse = options.reverse, 3357 property = transitions && options.transition !== false ? direction.transition : direction.property, 3358 divisor = options.divisor || 1; 3359 3360 if (!reverse) { 3361 var origin = element.data(ORIGIN); 3362 offset = (direction.modifier * (direction.vertical ? element.outerHeight() : element.outerWidth()) / divisor); 3363 if (!origin && origin !== 0) { 3364 element.data(ORIGIN, parseFloat(animationProperty(element, property))); 3365 } 3366 } 3367 3368 extender[property] = reverse ? (element.data(ORIGIN) || 0) : (element.data(ORIGIN) || 0) + offset + PX; 3369 3370 return extend(extender, options.properties); 3371 } 3372 }, 3373 slideMargin: { 3374 setup: function(element, options) { 3375 var origin = element.data(ORIGIN), 3376 offset = options.offset, margin, 3377 extender = {}, reverse = options.reverse; 3378
3379 if (!reverse && !origin && origin !== 0) { 3380 element.data(ORIGIN, parseInt(element.css("margin-" + options.axis), 10)); 3381 } 3382 3383 margin = (element.data(ORIGIN) || 0); 3384 extender["margin-" + options.axis] = !reverse ? margin + offset : margin; 3385 return extend(extender, options.properties); 3386 } 3387 }, 3388 slideTo: { 3389 setup: function(element, options) { 3390 var offset = (options.offset+"").split(","), 3391 extender = {}, reverse = options.reverse; 3392 3393 if (transitions && options.transition !== false) { 3394 extender.translate = !reverse ? offset + PX : 0; 3395 } else { 3396 extender.left = !reverse ? offset[0] : 0; 3397 extender.top = !reverse ? offset[1] : 0; 3398 } 3399 element.css("left"); 3400 3401 return extend(extender, options.properties); 3402 } 3403 }, 3404 slideIn: { 3405 setup: function(element, options) { 3406 var dir = kendo.directions[options.effects.slideIn.direction], 3407 reverse = options.reverse, 3408 direction = reverse ? kendo.directions[dir.reverse] : dir, 3409 offset = -direction.modifier * (direction.vertical ? element.outerHeight() : element.outerWidth()), 3410 extender = {}; 3411 3412 if (transitions && options.transition !== false) { 3413 element.css(TRANSFORM, direction.transition + "(" + (!reverse ? offset : 0) + "px)"); 3414 extender[direction.transition] = reverse ? offset + PX : 0; 3415 element.css(TRANSFORM); 3416 } else { 3417 if (!reverse) { 3418 element.css(direction.property, offset + PX); 3419 } 3420 extender[direction.property] = reverse ? offset + PX : 0; 3421 element.css(direction.property); // Read a style to force Chrome to apply the change. 3422 } 3423 3424 return extend(extender, options.properties); 3425 } 3426 }, 3427 expand: { 3428 keep: [ OVERFLOW ], 3429 css: { overflow: HIDDEN }, 3430 restore: [ OVERFLOW ], 3431 setup: function(element, options) { 3432 var reverse = options.reverse, 3433 direction = options.effects.expand.direction, 3434 property = (direction ? direction == "vertical" : true) ? HEIGHT : WIDTH, 3435 setLength = element[0].style[property], 3436 oldLength = element.data(property), 3437 length = parseInteger(oldLength || setLength) || round(element.css(property, AUTO )[property]()), 3438 completion = {}; 3439 3440 completion[property] = (reverse ? 0 : length) + PX; 3441 element.css(property, reverse ? length : 0).css(property); 3442 if (oldLength === undefined) { 3443 element.data(property, setLength); 3444 } 3445 3446 return extend(completion, options.properties); 3447 }, 3448 teardown: function(element, options) { 3449 var direction = options.effects.expand.direction, 3450 property = (direction ? direction == "vertical" : true) ? HEIGHT : WIDTH, 3451 length = element.data(property); 3452 if (length == AUTO || length === BLANK) { 3453 setTimeout(function() { element.css(property, AUTO).css(property); }, 0); // jQuery animate complete callback in IE is called before the last animation step! 3454 } 3455 } 3456 }, 3457 simple: { 3458 setup: function(element, options) { 3459 return options.properties; 3460 } 3461 } 3462 }); 3463 3464 kendo.fx.expandVertical = kendo.fx.expand; // expandVertical is deprecated. 3465 3466 var animationFrame = window.requestAnimationFrame || 3467 window.webkitRequestAnimationFrame || 3468 window.mozRequestAnimationFrame || 3469 window.oRequestAnimationFrame || 3470 window.msRequestAnimationFrame || 3471 function(callback){ setTimeout(callback, 1000 / 60); }; 3472 3473 var Animation = kendo.Class.extend({ 3474 init: function() { 3475 var that = this; 3476 that._tickProxy = proxy(that._tick, that); 3477 that._started = false; 3478 }, 3479 3480 tick: $.noop, 3481 done: $.noop, 3482 onEnd: $.noop, 3483 onCancel: $.noop, 3484 3485 start: function() { 3486 this._started = true; 3487 animationFrame(this._tickProxy); 3488 }, 3489 3490 cancel: function() { 3491 this._started = false; 3492 this.onCancel(); 3493 }, 3494 3495 _tick: function() { 3496 var that = this; 3497 if (!that._started) { return; } 3498 3499 that.tick(); 3500 3501 if (!that.done()) { 3502 animationFrame(that._tickProxy); 3503 } else { 3504 that._started = false; 3505 that.onEnd(); 3506 } 3507 } 3508 });
3509 3510 var Transition = Animation.extend({ 3511 init: function(options) { 3512 var that = this; 3513 extend(that, options); 3514 Animation.fn.init.call(that); 3515 }, 3516 3517 done: function() { 3518 return this.timePassed() >= this.duration; 3519 }, 3520 3521 timePassed: function() { 3522 return Math.min(this.duration, (+new Date()) - this.startDate); 3523 }, 3524 3525 moveTo: function(options) { 3526 var that = this, 3527 movable = that.movable; 3528 3529 that.initial = movable[that.axis]; 3530 that.delta = options.location - that.initial; 3531 3532 that.duration = options.duration || 300; 3533 3534 that.tick = that._easeProxy(options.ease); 3535 3536 that.startDate = +new Date(); 3537 that.start(); 3538 }, 3539 3540 _easeProxy: function(ease) { 3541 var that = this; 3542 3543 return function() { 3544 that.movable.moveAxis(that.axis, ease(that.timePassed(), that.initial, that.delta, that.duration)); 3545 }; 3546 } 3547 }); 3548 3549 extend(Transition, { 3550 easeOutExpo: function (t, b, c, d) { 3551 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; 3552 }, 3553 3554 easeOutBack: function (t, b, c, d, s) { 3555 s = 1.70158; 3556 return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; 3557 } 3558 }); 3559 3560 fx.Animation = Animation; 3561 fx.Transition = Transition; 3562})(jQuery); 3563(function($, undefined) { 3564 var kendo = window.kendo, 3565 extend = $.extend, 3566 odataFilters = { 3567 eq: "eq", 3568 neq: "ne", 3569 gt: "gt", 3570 gte: "ge", 3571 lt: "lt", 3572 lte: "le", 3573 contains : "substringof", 3574 endswith: "endswith", 3575 startswith: "startswith" 3576 }, 3577 mappers = { 3578 pageSize: $.noop, 3579 page: $.noop, 3580 filter: function(params, filter) { 3581 if (filter) { 3582 params.$filter = toOdataFilter(filter); 3583 } 3584 }, 3585 sort: function(params, orderby) { 3586 var expr = $.map(orderby, function(value) { 3587 var order = value.field.replace(/\./g, "/"); 3588 3589 if (value.dir === "desc") { 3590 order += " desc"; 3591 } 3592 3593 return order; 3594 }).join(","); 3595 3596 if (expr) { 3597 params.$orderby = expr; 3598 } 3599 }, 3600 skip: function(params, skip) { 3601 if (skip) { 3602 params.$skip = skip; 3603 } 3604 }, 3605 take: function(params, take) { 3606 if (take) { 3607 params.$top = take; 3608 } 3609 } 3610 }, 3611 defaultDataType = { 3612 read: { 3613 dataType: "jsonp" 3614 } 3615 }; 3616 3617 function toOdataFilter(filter) { 3618 var result = [], 3619 logic = filter.logic || "and", 3620 idx, 3621 length, 3622 field, 3623 type, 3624 format, 3625 operator, 3626 value, 3627 ignoreCase, 3628 filters = filter.filters; 3629 3630 for (idx = 0, length = filters.length; idx < length; idx++) { 3631 filter = filters[idx]; 3632 field = filter.field; 3633 value = filter.value; 3634 operator = filter.operator; 3635 3636 if (filter.filters) { 3637 filter = toOdataFilter(filter); 3638 } else { 3639 ignoreCase = filter.ignoreCase; 3640 field = field.replace(/\./g, "/"); 3641 filter = odataFilters[operator]; 3642 3643 if (filter && value !== undefined) { 3644 type = $.type(value); 3645 if (type === "string") { 3646 format = "'{1}'"; 3647 value = value.replace(/'/g, "''"); 3648 3649 if (ignoreCase === true) { 3650 field = "tolower(" + field + ")"; 3651 } 3652 3653 } else if (type === "date") { 3654 format = "datetime'{1:yyyy-MM-ddTHH:mm:ss}'"; 3655 } else { 3656 format = "{1}"; 3657 } 3658 3659 if (filter.length > 3) { 3660 if (filter !== "substringof") { 3661 format = "{0}({2}," + format + ")"; 3662 } else { 3663 format = "{0}(" + format + ",{2})"; 3664 } 3665 } else { 3666 format = "{2} {0} " + format; 3667 } 3668 3669 filter = kendo.format(format, filter, value, field); 3670 } 3671 } 3672 3673 result.push(filter); 3674 } 3675 3676 filter = result.join(" " + logic + " "); 3677 3678 if (result.length > 1) { 3679 filter = "(" + filter + ")"; 3680 } 3681 3682 return filter; 3683 } 3684 3685 extend(true, kendo.data, { 3686 schemas: { 3687 odata: { 3688 type: "json", 3689 data: function(data) { 3690 return data.d.results || [data.d]; 3691 }, 3692 total: "d.__count" 3693 } 3694 }, 3695 transports: { 3696 odata: { 3697 read: { 3698 cache: true, // to prevent jQuery from adding cache buster 3699 dataType: "jsonp", 3700 jsonp: "$callback" 3701 }, 3702 update: { 3703 cache: true, 3704 dataType: "json", 3705 contentType: "application/json", // to inform the server the the request body is JSON encoded 3706 type: "PUT" // can be PUT or MERGE 3707 }, 3708 create: { 3709 cache: true, 3710 dataType: "json", 3711 contentType: "application/json", 3712 type: "POST" // must be POST to create new entity 3713 }, 3714 destroy: { 3715 cache: true, 3716 dataType: "json", 3717 type: "DELETE" 3718 }, 3719 parameterMap: function(options, type) { 3720 var params, 3721 value, 3722 option, 3723 dataType; 3724 3725 options = options || {}; 3726 type = type || "read"; 3727 dataType = (this.options || defaultDataType)[type]; 3728 dataType = dataType ? dataType.dataType : "json"; 3729 3730 if (type === "read") { 3731 params = { 3732 $inlinecount: "allpages" 3733 }; 3734 3735 if (dataType != "json") { 3736 params.$format = "json"; 3737 } 3738 3739 for (option in options) { 3740 if (mappers[option]) { 3741 mappers[option](params, options[option]); 3742 } else { 3743 params[option] = options[option]; 3744 } 3745 } 3746 } else { 3747 if (dataType !== "json") { 3748 throw new Error("Only json dataType can be used for " + type + " operation."); 3749 } 3750 3751 if (type !== "destroy") { 3752 for (option in options) { 3753 value = options[option]; 3754 if (typeof value === "number") { 3755 options[option] = value + ""; 3756 } 3757 } 3758 3759 params = kendo.stringify(options); 3760 } 3761 } 3762 3763 return params; 3764 } 3765 } 3766 } 3767 });
3768})(jQuery); 3769(function($, undefined) { 3770 var kendo = window.kendo, 3771 isArray = $.isArray, 3772 isPlainObject = $.isPlainObject, 3773 map = $.map, 3774 each = $.each, 3775 extend = $.extend, 3776 getter = kendo.getter, 3777 Class = kendo.Class; 3778 3779 var XmlDataReader = Class.extend({ 3780 init: function(options) { 3781 var that = this, 3782 total = options.total, 3783 model = options.model, 3784 data = options.data; 3785 3786 if (model) { 3787 if (isPlainObject(model)) { 3788 if (model.fields) { 3789 each(model.fields, function(field, value) { 3790 if (isPlainObject(value) && value.field) { 3791 value = extend(value, { field: that.getter(value.field) }); 3792 } else { 3793 value = { field: that.getter(value) }; 3794 } 3795 model.fields[field] = value; 3796 }); 3797 } 3798 var id = model.id; 3799 if (id) { 3800 var idField = {}; 3801 3802 idField[that.xpathToMember(id, true)] = { field : that.getter(id) }; 3803 model.fields = extend(idField, model.fields); 3804 model.id = that.xpathToMember(id); 3805 } 3806 model = kendo.data.Model.define(model); 3807 } 3808 3809 that.model = model; 3810 } 3811 3812 if (total) { 3813 total = that.getter(total); 3814 that.total = function(data) { 3815 return parseInt(total(data), 10); 3816 }; 3817 } 3818 3819 if (data) { 3820 data = that.xpathToMember(data); 3821 that.data = function(value) { 3822 var result = that.evaluate(value, data), 3823 modelInstance; 3824 3825 result = isArray(result) ? result : [result]; 3826 3827 if (that.model && model.fields) { 3828 modelInstance = new that.model(); 3829 3830 return map(result, function(value) { 3831 if (value) { 3832 var record = {}, field; 3833 3834 for (field in model.fields) { 3835 record[field] = modelInstance._parse(field, model.fields[field].field(value)); 3836 } 3837 3838 return record; 3839 } 3840 }); 3841 } 3842 3843 return result; 3844 }; 3845 } 3846 }, 3847 total: function(result) { 3848 return this.data(result).length; 3849 }, 3850 parseDOM: function(element) { 3851 var result = {}, 3852 parsedNode, 3853 node, 3854 nodeType, 3855 nodeName, 3856 member, 3857 attribute, 3858 attributes = element.attributes, 3859 attributeCount = attributes.length, 3860 idx; 3861 3862 for (idx = 0; idx < attributeCount; idx++) { 3863 attribute = attributes[idx]; 3864 result["@" + attribute.nodeName] = attribute.nodeValue; 3865 } 3866 3867 for (node = element.firstChild; node; node = node.nextSibling) { 3868 nodeType = node.nodeType; 3869 3870 if (nodeType === 3 || nodeType === 4) { 3871 // text nodes or CDATA are stored as #text field 3872 result["#text"] = node.nodeValue; 3873 } else if (nodeType === 1) { 3874 // elements are stored as fields 3875 parsedNode = this.parseDOM(node); 3876 3877 nodeName = node.nodeName; 3878 3879 member = result[nodeName]; 3880 3881 if (isArray(member)) { 3882 // elements of same nodeName are stored as array 3883 member.push(parsedNode); 3884 } else if (member !== undefined) { 3885 member = [member, parsedNode]; 3886 } else { 3887 member = parsedNode; 3888 } 3889 3890 result[nodeName] = member; 3891 } 3892 } 3893 return result; 3894 }, 3895 3896 evaluate: function(value, expression) { 3897 var members = expression.split("."), 3898 member, 3899 result, 3900 length, 3901 intermediateResult, 3902 idx; 3903 3904 while (member = members.shift()) { 3905 value = value[member]; 3906 3907 if (isArray(value)) { 3908 result = []; 3909 expression = members.join("."); 3910 3911 for (idx = 0, length = value.length; idx < length; idx++) { 3912 intermediateResult = this.evaluate(value[idx], expression); 3913 3914 intermediateResult = isArray(intermediateResult) ? intermediateResult : [intermediateResult]; 3915 3916 result.push.apply(result, intermediateResult); 3917 } 3918 3919 return result; 3920 } 3921 } 3922 3923 return value; 3924 }, 3925 3926 parse: function(xml) { 3927 var documentElement, 3928 tree, 3929 result = {}; 3930 3931 documentElement = xml.documentElement || $.parseXML(xml).documentElement; 3932 3933 tree = this.parseDOM(documentElement); 3934 3935 result[documentElement.nodeName] = tree; 3936 3937 return result; 3938 }, 3939 3940 xpathToMember: function(member, raw) { 3941 if (!member) { 3942 return ""; 3943 } 3944 3945 member = member.replace(/^\//, "") // remove the first "/" 3946 .replace(/\//g, "."); // replace all "/" with "." 3947 3948 if (member.indexOf("@") >= 0) { 3949 // replace @attribute with '["@attribute"]' 3950 return member.replace(/\.?(@.*)/, raw? '$1':'["$1"]'); 3951 } 3952 3953 if (member.indexOf("text()") >= 0) { 3954 // replace ".text()" with '["#text"]' 3955 return member.replace(/(\.?text\(\))/, raw? '#text':'["#text"]'); 3956 } 3957 3958 return member; 3959 }, 3960 getter: function(member) { 3961 return getter(this.xpathToMember(member), true); 3962 } 3963 });
3964 3965 $.extend(true, kendo.data, { 3966 XmlDataReader: XmlDataReader, 3967 readers: { 3968 xml: XmlDataReader 3969 } 3970 }); 3971})(jQuery); 3972(function($, undefined) { 3973 var extend = $.extend, 3974 proxy = $.proxy, 3975 isFunction = $.isFunction, 3976 isPlainObject = $.isPlainObject, 3977 isEmptyObject = $.isEmptyObject, 3978 isArray = $.isArray, 3979 grep = $.grep, 3980 ajax = $.ajax, 3981 map, 3982 each = $.each, 3983 noop = $.noop, 3984 kendo = window.kendo, 3985 Observable = kendo.Observable, 3986 Class = kendo.Class, 3987 STRING = "string", 3988 FUNCTION = "function", 3989 CREATE = "create", 3990 READ = "read", 3991 UPDATE = "update", 3992 DESTROY = "destroy", 3993 CHANGE = "change", 3994 GET = "get", 3995 ERROR = "error", 3996 REQUESTSTART = "requestStart", 3997 crud = [CREATE, READ, UPDATE, DESTROY], 3998 identity = function(o) { return o; }, 3999 getter = kendo.getter, 4000 stringify = kendo.stringify, 4001 math = Math, 4002 push = [].push, 4003 join = [].join, 4004 pop = [].pop, 4005 splice = [].splice, 4006 shift = [].shift, 4007 slice = [].slice, 4008 unshift = [].unshift, 4009 toString = {}.toString, 4010 stableSort = kendo.support.stableSort, 4011 dateRegExp = /^\/Date\((.*?)\)\/$/, 4012 quoteRegExp = /(?=['\\])/g; 4013 4014 var ObservableArray = Observable.extend({ 4015 init: function(array, type) { 4016 var that = this; 4017 4018 that.type = type || ObservableObject; 4019 4020 Observable.fn.init.call(that); 4021 4022 that.length = array.length; 4023 4024 that.wrapAll(array, that); 4025 }, 4026 4027 toJSON: function() { 4028 return slice.call(this); 4029 }, 4030 4031 parent: noop, 4032 4033 wrapAll: function(source, target) { 4034 var that = this, 4035 idx, 4036 length, 4037 parent = function() { 4038 return that; 4039 }; 4040 4041 target = target || []; 4042 4043 for (idx = 0, length = source.length; idx < length; idx++) { 4044 target[idx] = that.wrap(source[idx], parent); 4045 } 4046 4047 return target; 4048 }, 4049 4050 wrap: function(object, parent) { 4051 var that = this, 4052 observable; 4053 4054 if (object !== null && toString.call(object) === "[object Object]") { 4055 observable = object instanceof that.type || object instanceof Model; 4056 4057 if (!observable) { 4058 object = object instanceof ObservableObject ? object.toJSON() : object; 4059 object = new that.type(object); 4060 } 4061 4062 object.parent = parent; 4063 4064 object.bind(CHANGE, function(e) { 4065 that.trigger(CHANGE, { 4066 field: e.field, 4067 items: [this], 4068 action: "itemchange" 4069 }); 4070 }); 4071 } 4072 4073 return object; 4074 }, 4075 4076 push: function() { 4077 var index = this.length, 4078 items = this.wrapAll(arguments), 4079 result; 4080 4081 result = push.apply(this, items); 4082 4083 this.trigger(CHANGE, { 4084 action: "add", 4085 index: index, 4086 items: items 4087 }); 4088 4089 return result; 4090 }, 4091 4092 slice: slice, 4093 4094 join: join, 4095 4096 pop: function() { 4097 var length = this.length, result = pop.apply(this); 4098 4099 if (length) { 4100 this.trigger(CHANGE, { 4101 action: "remove", 4102 index: length - 1, 4103 items:[result] 4104 }); 4105 } 4106 4107 return result; 4108 }, 4109 4110 splice: function(index, howMany, item) { 4111 var items = this.wrapAll(slice.call(arguments, 2)), 4112 result; 4113 4114 result = splice.apply(this, [index, howMany].concat(items)); 4115 4116 if (result.length) { 4117 this.trigger(CHANGE, { 4118 action: "remove", 4119 index: index, 4120 items: result 4121 }); 4122 } 4123 4124 if (item) { 4125 this.trigger(CHANGE, { 4126 action: "add", 4127 index: index, 4128 items: items 4129 }); 4130 } 4131 return result; 4132 }, 4133 4134 shift: function() { 4135 var length = this.length, result = shift.apply(this); 4136 4137 if (length) { 4138 this.trigger(CHANGE, { 4139 action: "remove", 4140 index: 0, 4141 items:[result] 4142 }); 4143 } 4144 4145 return result; 4146 }, 4147 4148 unshift: function() { 4149 var items = this.wrapAll(arguments), 4150 result; 4151 4152 result = unshift.apply(this, items); 4153 4154 this.trigger(CHANGE, { 4155 action: "add", 4156 index: 0, 4157 items: items 4158 }); 4159 4160 return result; 4161 }, 4162 4163 indexOf: function(item) { 4164 var that = this, 4165 idx, 4166 length; 4167 4168 for (idx = 0, length = that.length; idx < length; idx++) { 4169 if (that[idx] === item) { 4170 return idx; 4171 } 4172 } 4173 return -1; 4174 } 4175 });
4176 4177 var ObservableObject = Observable.extend({ 4178 init: function(value) { 4179 var that = this, 4180 member, 4181 field, 4182 parent = function() { 4183 return that; 4184 }, 4185 type; 4186 4187 Observable.fn.init.call(this); 4188 4189 for (field in value) { 4190 member = value[field]; 4191 if (field.charAt(0) != "_") { 4192 type = toString.call(member); 4193 4194 member = that.wrap(member, field, parent); 4195 } 4196 that[field] = member; 4197 } 4198 4199 that.uid = kendo.guid(); 4200 }, 4201 4202 shouldSerialize: function(field) { 4203 return this.hasOwnProperty(field) && field !== "_events" && typeof this[field] !== FUNCTION && field !== "uid"; 4204 }, 4205 4206 toJSON: function() { 4207 var result = {}, field; 4208 4209 for (field in this) { 4210 if (this.shouldSerialize(field)) { 4211 result[field] = this[field]; 4212 } 4213 } 4214 4215 return result; 4216 }, 4217 4218 get: function(field) { 4219 var that = this, result; 4220 4221 that.trigger(GET, { field: field }); 4222 4223 if (field === "this") { 4224 result = that; 4225 } else { 4226 result = kendo.getter(field, true)(that); 4227 } 4228 4229 return result; 4230 }, 4231 4232 _set: function(field, value) { 4233 var that = this; 4234 if (field.indexOf(".")) { 4235 var paths = field.split("."), 4236 path = ""; 4237 4238 while (paths.length > 1) { 4239 path += paths.shift(); 4240 var obj = kendo.getter(path, true)(that); 4241 if (obj instanceof ObservableObject) { 4242 obj.set(paths.join("."), value); 4243 return; 4244 } 4245 path += "."; 4246 } 4247 } 4248 4249 kendo.setter(field)(that, value); 4250 }, 4251 4252 set: function(field, value) { 4253 var that = this, 4254 current = that[field], 4255 parent = function() { return that; }; 4256 4257 if (current !== value) { 4258 if (!that.trigger("set", { field: field, value: value })) { 4259 4260 that._set(field, that.wrap(value, field, parent)); 4261 4262 that.trigger(CHANGE, { field: field }); 4263 } 4264 } 4265 }, 4266 4267 parent: noop, 4268 4269 wrap: function(object, field, parent) { 4270 var that = this, 4271 type = toString.call(object), 4272 isObservableArray = object instanceof ObservableArray; 4273 4274 if (object !== null && type === "[object Object]" && !(object instanceof DataSource) && !isObservableArray) { 4275 if (!(object instanceof ObservableObject)) { 4276 object = new ObservableObject(object); 4277 } 4278 4279 object.parent = parent; 4280 4281 (function(field) { 4282 object.bind(GET, function(e) { 4283 e.field = field + "." + e.field; 4284 that.trigger(GET, e); 4285 }); 4286 4287 object.bind(CHANGE, function(e) { 4288 e.field = field + "." + e.field; 4289 that.trigger(CHANGE, e); 4290 }); 4291 })(field); 4292 } else if (object !== null && (type === "[object Array]" || isObservableArray)) { 4293 if (!isObservableArray) { 4294 object = new ObservableArray(object); 4295 } 4296 object.parent = parent; 4297 4298 (function(field) { 4299 object.bind(CHANGE, function(e) { 4300 that.trigger(CHANGE, { field: field, index: e.index, items: e.items, action: e.action}); 4301 }); 4302 })(field); 4303 } else if (object !== null && object instanceof DataSource) { 4304 object._parent = parent; // assign parent to the DataSource if part of observable object 4305 } 4306 4307 4308 return object; 4309 } 4310 });
4311 4312 function equal(x, y) { 4313 if (x === y) { 4314 return true; 4315 } 4316 4317 var xtype = $.type(x), ytype = $.type(y), field; 4318 4319 if (xtype !== ytype) { 4320 return false; 4321 } 4322 4323 if (xtype === "date") { 4324 return x.getTime() === y.getTime(); 4325 } 4326 4327 if (xtype !== "object" && xtype !== "array") { 4328 return false; 4329 } 4330 4331 for (field in x) { 4332 if (!equal(x[field], y[field])) { 4333 return false; 4334 } 4335 } 4336 4337 return true; 4338 } 4339 4340 var parsers = { 4341 "number": function(value) { 4342 return kendo.parseFloat(value); 4343 }, 4344 4345 "date": function(value) { 4346 if (typeof value === STRING) { 4347 var date = dateRegExp.exec(value); 4348 if (date) { 4349 return new Date(parseInt(date[1], 10)); 4350 } 4351 } 4352 return kendo.parseDate(value); 4353 }, 4354 4355 "boolean": function(value) { 4356 if (typeof value === STRING) { 4357 return value.toLowerCase() === "true"; 4358 } 4359 return !!value; 4360 }, 4361 4362 "string": function(value) { 4363 return value + ""; 4364 }, 4365 4366 "default": function(value) { 4367 return value; 4368 } 4369 }; 4370 4371 var defaultValues = { 4372 "string": "", 4373 "number": 0, 4374 "date": new Date(), 4375 "boolean": false, 4376 "default": "" 4377 }; 4378 4379 var Model = ObservableObject.extend({ 4380 init: function(data) { 4381 var that = this; 4382 4383 if (!data || $.isEmptyObject(data)) { 4384 data = $.extend({}, that.defaults, data); 4385 } 4386 4387 ObservableObject.fn.init.call(that, data); 4388 4389 that.dirty = false; 4390 4391 if (that.idField) { 4392 that.id = that.get(that.idField); 4393 4394 if (that.id === undefined) { 4395 that.id = that._defaultId; 4396 } 4397 } 4398 }, 4399 4400 shouldSerialize: function(field) { 4401 return ObservableObject.fn.shouldSerialize.call(this, field) && field !== "uid" && !(this.idField !== "id" && field === "id") && field !== "dirty" && field !== "_accessors"; 4402 }, 4403 4404 _parse: function(field, value) { 4405 var that = this, 4406 parse; 4407 4408 field = (that.fields || {})[field]; 4409 if (field) { 4410 parse = field.parse; 4411 if (!parse && field.type) { 4412 parse = parsers[field.type.toLowerCase()]; 4413 } 4414 } 4415 4416 return parse ? parse(value) : value; 4417 }, 4418 4419 editable: function(field) { 4420 field = (this.fields || {})[field]; 4421 return field ? field.editable !== false : true; 4422 }, 4423 4424 set: function(field, value, initiator) { 4425 var that = this; 4426 4427 if (that.editable(field)) { 4428 value = that._parse(field, value); 4429 4430 if (!equal(value, that.get(field))) { 4431 that.dirty = true; 4432 ObservableObject.fn.set.call(that, field, value, initiator); 4433 } 4434 } 4435 }, 4436 4437 accept: function(data) { 4438 var that = this; 4439 4440 extend(that, data); 4441 4442 if (that.idField) { 4443 that.id = that.get(that.idField); 4444 } 4445 that.dirty = false; 4446 }, 4447 4448 isNew: function() { 4449 return this.id === this._defaultId; 4450 } 4451 }); 4452 4453 Model.define = function(options) { 4454 var model, 4455 proto = extend({}, { defaults: {} }, options), 4456 id = proto.id; 4457 4458 if (id) { 4459 proto.idField = id; 4460 } 4461 4462 if (proto.id) { 4463 delete proto.id; 4464 } 4465 4466 if (id) { 4467 proto.defaults[id] = proto._defaultId = ""; 4468 } 4469 4470 for (var name in proto.fields) { 4471 var field = proto.fields[name], 4472 type = field.type || "default", 4473 value = null; 4474 4475 name = field.field || name; 4476 4477 if (!field.nullable) { 4478 value = proto.defaults[name] = field.defaultValue !== undefined ? field.defaultValue : defaultValues[type.toLowerCase()]; 4479 } 4480 4481 if (options.id === name) { 4482 proto._defaultId = value; 4483 } 4484 4485 proto.defaults[name] = value; 4486 4487 field.parse = field.parse || parsers[type]; 4488 } 4489 4490 model = Model.extend(proto); 4491 4492 if (proto.fields) { 4493 model.fields = proto.fields; 4494 model.idField = proto.idField; 4495 } 4496 4497 return model; 4498 }; 4499 4500 var Comparer = { 4501 selector: function(field) { 4502 return isFunction(field) ? field : getter(field); 4503 }, 4504 4505 asc: function(field) { 4506 var selector = this.selector(field); 4507 return function (a, b) { 4508 a = selector(a); 4509 b = selector(b); 4510 4511 return a > b ? 1 : (a < b ? -1 : 0); 4512 }; 4513 }, 4514 4515 desc: function(field) { 4516 var selector = this.selector(field); 4517 return function (a, b) { 4518 a = selector(a); 4519 b = selector(b); 4520 4521 return a < b ? 1 : (a > b ? -1 : 0); 4522 }; 4523 }, 4524 4525 create: function(descriptor) { 4526 return this[descriptor.dir.toLowerCase()](descriptor.field); 4527 }, 4528 4529 combine: function(comparers) { 4530 return function(a, b) { 4531 var result = comparers[0](a, b), 4532 idx, 4533 length; 4534 4535 for (idx = 1, length = comparers.length; idx < length; idx ++) { 4536 result = result || comparers[idx](a, b); 4537 } 4538 4539 return result; 4540 }; 4541 } 4542 }; 4543 4544 var PositionComparer = extend({}, Comparer, { 4545 asc: function(field) { 4546 var selector = this.selector(field); 4547 return function (a, b) { 4548 var valueA = selector(a); 4549 var valueB = selector(b); 4550 4551 if (valueA === valueB) { 4552 return a.__position - b.__position; 4553 } 4554 return valueA > valueB ? 1 : (valueA < valueB ? -1 : 0); 4555 }; 4556 }, 4557 4558 desc: function(field) { 4559 var selector = this.selector(field); 4560 return function (a, b) { 4561 var valueA = selector(a); 4562 var valueB = selector(b); 4563 4564 if (valueA === valueB) { 4565 return a.__position - b.__position; 4566 } 4567 4568 return valueA < valueB ? 1 : (valueA > valueB ? -1 : 0); 4569 }; 4570 } 4571 });
4572 4573 map = function (array, callback) { 4574 var idx, length = array.length, result = new Array(length); 4575 4576 for (idx = 0; idx < length; idx++) { 4577 result[idx] = callback(array[idx], idx, array); 4578 } 4579 4580 return result; 4581 }; 4582 4583 var operators = (function(){ 4584 4585 function quote(value) { 4586 return value.replace(quoteRegExp, "\\"); 4587 } 4588 4589 function operator(op, a, b, ignore) { 4590 var date; 4591 4592 if (b != null) { 4593 if (typeof b === STRING) { 4594 b = quote(b); 4595 date = dateRegExp.exec(b); 4596 if (date) { 4597 b = new Date(+date[1]); 4598 } else if (ignore) { 4599 b = "'" + b.toLowerCase() + "'"; 4600 a = a + ".toLowerCase()"; 4601 } else { 4602 b = "'" + b + "'"; 4603 } 4604 } 4605 4606 if (b.getTime) { 4607 //b looks like a Date 4608 a = "(" + a + "?" + a + ".getTime():" + a + ")"; 4609 b = b.getTime(); 4610 } 4611 } 4612 4613 return a + " " + op + " " + b; 4614 } 4615 4616 return { 4617 eq: function(a, b, ignore) { 4618 return operator("==", a, b, ignore); 4619 }, 4620 neq: function(a, b, ignore) { 4621 return operator("!=", a, b, ignore); 4622 }, 4623 gt: function(a, b, ignore) { 4624 return operator(">", a, b, ignore); 4625 }, 4626 gte: function(a, b, ignore) { 4627 return operator(">=", a, b, ignore); 4628 }, 4629 lt: function(a, b, ignore) { 4630 return operator("<", a, b, ignore); 4631 }, 4632 lte: function(a, b, ignore) { 4633 return operator("<=", a, b, ignore); 4634 }, 4635 startswith: function(a, b, ignore) { 4636 if (ignore) { 4637 a = a + ".toLowerCase()"; 4638 if (b) { 4639 b = b.toLowerCase(); 4640 } 4641 } 4642 4643 if (b) { 4644 b = quote(b); 4645 } 4646 4647 return a + ".lastIndexOf('" + b + "', 0) == 0"; 4648 }, 4649 endswith: function(a, b, ignore) { 4650 if (ignore) { 4651 a = a + ".toLowerCase()"; 4652 if (b) { 4653 b = b.toLowerCase(); 4654 } 4655 } 4656 4657 if (b) { 4658 b = quote(b); 4659 } 4660 4661 return a + ".lastIndexOf('" + b + "') == " + a + ".length - " + (b || "").length; 4662 }, 4663 contains: function(a, b, ignore) { 4664 if (ignore) { 4665 a = a + ".toLowerCase()"; 4666 if (b) { 4667 b = b.toLowerCase(); 4668 } 4669 } 4670 4671 if (b) { 4672 b = quote(b); 4673 } 4674 4675 return a + ".indexOf('" + b + "') >= 0"; 4676 } 4677 }; 4678 })(); 4679 4680 function Query(data) { 4681 this.data = data || []; 4682 } 4683 4684 Query.filterExpr = function(expression) { 4685 var expressions = [], 4686 logic = { and: " && ", or: " || " }, 4687 idx, 4688 length, 4689 filter, 4690 expr, 4691 fieldFunctions = [], 4692 operatorFunctions = [], 4693 field, 4694 operator, 4695 filters = expression.filters; 4696 4697 for (idx = 0, length = filters.length; idx < length; idx++) { 4698 filter = filters[idx]; 4699 field = filter.field; 4700 operator = filter.operator; 4701 4702 if (filter.filters) { 4703 expr = Query.filterExpr(filter); 4704 //Nested function fields or operators - update their index e.g. __o[0] -> __o[1] 4705 filter = expr.expression 4706 .replace(/__o\[(\d+)\]/g, function(match, index) { 4707 index = +index; 4708 return "__o[" + (operatorFunctions.length + index) + "]"; 4709 }) 4710 .replace(/__f\[(\d+)\]/g, function(match, index) { 4711 index = +index; 4712 return "__f[" + (fieldFunctions.length + index) + "]"; 4713 }); 4714 4715 operatorFunctions.push.apply(operatorFunctions, expr.operators); 4716 fieldFunctions.push.apply(fieldFunctions, expr.fields); 4717 } else { 4718 if (typeof field === FUNCTION) { 4719 expr = "__f[" + fieldFunctions.length +"](d)"; 4720 fieldFunctions.push(field); 4721 } else { 4722 expr = kendo.expr(field); 4723 } 4724 4725 if (typeof operator === FUNCTION) { 4726 filter = "__o[" + operatorFunctions.length + "](" + expr + ", " + filter.value + ")"; 4727 operatorFunctions.push(operator); 4728 } else { 4729 filter = operators[(operator || "eq").toLowerCase()](expr, filter.value, filter.ignoreCase !== undefined? filter.ignoreCase : true); 4730 } 4731 } 4732 4733 expressions.push(filter); 4734 } 4735 4736 return { expression: "(" + expressions.join(logic[expression.logic]) + ")", fields: fieldFunctions, operators: operatorFunctions }; 4737 }; 4738 4739 function normalizeSort(field, dir) { 4740 if (field) { 4741 var descriptor = typeof field === STRING ? { field: field, dir: dir } : field, 4742 descriptors = isArray(descriptor) ? descriptor : (descriptor !== undefined ? [descriptor] : []); 4743 4744 return grep(descriptors, function(d) { return !!d.dir; });
4745 } 4746 } 4747 4748 var operatorMap = { 4749 "==": "eq", 4750 equals: "eq", 4751 isequalto: "eq", 4752 equalto: "eq", 4753 equal: "eq", 4754 "!=": "neq", 4755 ne: "neq", 4756 notequals: "neq", 4757 isnotequalto: "neq", 4758 notequalto: "neq", 4759 notequal: "neq", 4760 "<": "lt", 4761 islessthan: "lt", 4762 lessthan: "lt", 4763 less: "lt", 4764 "<=": "lte", 4765 le: "lte", 4766 islessthanorequalto: "lte", 4767 lessthanequal: "lte", 4768 ">": "gt", 4769 isgreaterthan: "gt", 4770 greaterthan: "gt", 4771 greater: "gt", 4772 ">=": "gte", 4773 isgreaterthanorequalto: "gte", 4774 greaterthanequal: "gte", 4775 ge: "gte" 4776 }; 4777 4778 function normalizeOperator(expression) { 4779 var idx, 4780 length, 4781 filter, 4782 operator, 4783 filters = expression.filters; 4784 4785 if (filters) { 4786 for (idx = 0, length = filters.length; idx < length; idx++) { 4787 filter = filters[idx]; 4788 operator = filter.operator; 4789 4790 if (operator && typeof operator === STRING) { 4791 filter.operator = operatorMap[operator.toLowerCase()] || operator; 4792 } 4793 4794 normalizeOperator(filter); 4795 } 4796 } 4797 } 4798 4799 function normalizeFilter(expression) { 4800 if (expression && !isEmptyObject(expression)) { 4801 if (isArray(expression) || !expression.filters) { 4802 expression = { 4803 logic: "and", 4804 filters: isArray(expression) ? expression : [expression] 4805 }; 4806 } 4807 4808 normalizeOperator(expression); 4809 4810 return expression; 4811 } 4812 } 4813 4814 Query.normalizeFilter = normalizeFilter; 4815 4816 function normalizeAggregate(expressions) { 4817 return isArray(expressions) ? expressions : [expressions]; 4818 } 4819 4820 function normalizeGroup(field, dir) { 4821 var descriptor = typeof field === STRING ? { field: field, dir: dir } : field, 4822 descriptors = isArray(descriptor) ? descriptor : (descriptor !== undefined ? [descriptor] : []); 4823 4824 return map(descriptors, function(d) { return { field: d.field, dir: d.dir || "asc", aggregates: d.aggregates }; }); 4825 } 4826 4827 Query.prototype = { 4828 toArray: function () { 4829 return this.data; 4830 }, 4831 range: function(index, count) { 4832 return new Query(this.data.slice(index, index + count)); 4833 }, 4834 skip: function (count) { 4835 return new Query(this.data.slice(count)); 4836 }, 4837 take: function (count) { 4838 return new Query(this.data.slice(0, count)); 4839 }, 4840 select: function (selector) { 4841 return new Query(map(this.data, selector)); 4842 }, 4843 orderBy: function (selector) { 4844 var result = this.data.slice(0), 4845 comparer = isFunction(selector) || !selector ? Comparer.asc(selector) : selector.compare; 4846 4847 return new Query(result.sort(comparer)); 4848 }, 4849 orderByDescending: function (selector) { 4850 return new Query(this.data.slice(0).sort(Comparer.desc(selector))); 4851 }, 4852 sort: function(field, dir, comparer) { 4853 var idx, 4854 length, 4855 descriptors = normalizeSort(field, dir), 4856 comparers = []; 4857 4858 comparer = comparer || Comparer; 4859 4860 if (descriptors.length) { 4861 for (idx = 0, length = descriptors.length; idx < length; idx++) { 4862 comparers.push(comparer.create(descriptors[idx])); 4863 } 4864 4865 return this.orderBy({ compare: comparer.combine(comparers) }); 4866 } 4867 4868 return this; 4869 }, 4870 4871 filter: function(expressions) { 4872 var idx, 4873 current, 4874 length, 4875 compiled, 4876 predicate, 4877 data = this.data, 4878 fields, 4879 operators, 4880 result = [], 4881 filter; 4882 4883 expressions = normalizeFilter(expressions); 4884 4885 if (!expressions || expressions.filters.length === 0) { 4886 return this; 4887 } 4888 4889 compiled = Query.filterExpr(expressions); 4890 fields = compiled.fields; 4891 operators = compiled.operators; 4892 4893 predicate = filter = new Function("d, __f, __o", "return " + compiled.expression); 4894 4895 if (fields.length || operators.length) { 4896 filter = function(d) { 4897 return predicate(d, fields, operators); 4898 }; 4899 } 4900 4901 for (idx = 0, length = data.length; idx < length; idx++) { 4902 current = data[idx]; 4903 4904 if (filter(current)) { 4905 result.push(current); 4906 } 4907 } 4908 return new Query(result); 4909 }, 4910 4911 group: function(descriptors, allData) { 4912 descriptors = normalizeGroup(descriptors || []); 4913 allData = allData || this.data; 4914 4915 var that = this, 4916 result = new Query(that.data), 4917 descriptor; 4918 4919 if (descriptors.length > 0) { 4920 descriptor = descriptors[0]; 4921 result = result.groupBy(descriptor).select(function(group) { 4922 var data = new Query(allData).filter([ { field: group.field, operator: "eq", value: group.value } ]); 4923 return { 4924 field: group.field, 4925 value: group.value, 4926 items: descriptors.length > 1 ? new Query(group.items).group(descriptors.slice(1), data.toArray()).toArray() : group.items, 4927 hasSubgroups: descriptors.length > 1, 4928 aggregates: data.aggregate(descriptor.aggregates) 4929 }; 4930 }); 4931 } 4932 return result; 4933 }, 4934 4935 groupBy: function(descriptor) { 4936 if (isEmptyObject(descriptor) || !this.data.length) { 4937 return new Query([]); 4938 } 4939 4940 var field = descriptor.field, 4941 sorted = this._sortForGrouping(field, descriptor.dir || "asc"), 4942 accessor = kendo.accessor(field), 4943 item, 4944 groupValue = accessor.get(sorted[0], field), 4945 group = { 4946 field: field, 4947 value: groupValue, 4948 items: [] 4949 }, 4950 currentValue, 4951 idx, 4952 len, 4953 result = [group]; 4954 4955 for(idx = 0, len = sorted.length; idx < len; idx++) { 4956 item = sorted[idx]; 4957 currentValue = accessor.get(item, field); 4958 if(!groupValueComparer(groupValue, currentValue)) { 4959 groupValue = currentValue; 4960 group = { 4961 field: field, 4962 value: groupValue, 4963 items: [] 4964 }; 4965 result.push(group); 4966 } 4967 group.items.push(item); 4968 } 4969 return new Query(result); 4970 }, 4971 4972 _sortForGrouping: function(field, dir) { 4973 var idx, length, 4974 data = this.data; 4975 4976 if (!stableSort) { 4977 for (idx = 0, length = data.length; idx < length; idx++) { 4978 data[idx].__position = idx; 4979 } 4980 4981 data = new Query(data).sort(field, dir, PositionComparer).toArray(); 4982 4983 for (idx = 0, length = data.length; idx < length; idx++) { 4984 delete data[idx].__position; 4985 } 4986 return data; 4987 } 4988 return this.sort(field, dir).toArray(); 4989 }, 4990 4991 aggregate: function (aggregates) { 4992 var idx, 4993 len, 4994 result = {}; 4995 4996 if (aggregates && aggregates.length) { 4997 for(idx = 0, len = this.data.length; idx < len; idx++) { 4998 calculateAggregate(result, aggregates, this.data[idx], idx, len); 4999 } 5000 } 5001 return result; 5002 } 5003 }; 5004 5005 function groupValueComparer(a, b) { 5006 if (a && a.getTime && b && b.getTime) { 5007 return a.getTime() === b.getTime(); 5008 } 5009 return a === b; 5010 } 5011 5012 function calculateAggregate(accumulator, aggregates, item, index, length) { 5013 aggregates = aggregates || []; 5014 var idx, 5015 aggr, 5016 functionName, 5017 len = aggregates.length; 5018 5019 for (idx = 0; idx < len; idx++) { 5020 aggr = aggregates[idx]; 5021 functionName = aggr.aggregate; 5022 var field = aggr.field; 5023 accumulator[field] = accumulator[field] || {}; 5024 accumulator[field][functionName] = functions[functionName.toLowerCase()](accumulator[field][functionName], item, kendo.accessor(field), index, length); 5025 } 5026 } 5027 5028 var functions = { 5029 sum: function(accumulator, item, accessor) { 5030 return (accumulator || 0) + accessor.get(item); 5031 }, 5032 count: function(accumulator, item, accessor) { 5033 return (accumulator || 0) + 1; 5034 }, 5035 average: function(accumulator, item, accessor, index, length) { 5036 accumulator = (accumulator || 0) + accessor.get(item); 5037 if(index == length - 1) { 5038 accumulator = accumulator / length; 5039 } 5040 return accumulator; 5041 }, 5042 max: function(accumulator, item, accessor) { 5043 var value = accessor.get(item); 5044 5045 accumulator = accumulator || 0; 5046 5047 if(accumulator < value) { 5048 accumulator = value; 5049 } 5050 return accumulator; 5051 }, 5052 min: function(accumulator, item, accessor) { 5053 var value = accessor.get(item); 5054 5055 accumulator = (accumulator || value); 5056 5057 if(accumulator > value) { 5058 accumulator = value; 5059 } 5060 return accumulator; 5061 } 5062 }; 5063 5064 function toJSON(array) { 5065 var idx, length = array.length, result = new Array(length); 5066 5067 for (idx = 0; idx < length; idx++) { 5068 result[idx] = array[idx].toJSON(); 5069 } 5070 5071 return result; 5072 } 5073 5074 function process(data, options) { 5075 options = options || {}; 5076 5077 var query = new Query(data), 5078 group = options.group, 5079 sort = normalizeGroup(group || []).concat(normalizeSort(options.sort || [])), 5080 total, 5081 filter = options.filter, 5082 skip = options.skip, 5083 take = options.take; 5084 5085 if (filter) { 5086 query = query.filter(filter); 5087 total = query.toArray().length; 5088 } 5089 5090 if (sort) { 5091 query = query.sort(sort); 5092 5093 if (group) { 5094 data = query.toArray(); 5095 } 5096 } 5097 5098 if (skip !== undefined && take !== undefined) { 5099 query = query.range(skip, take); 5100 } 5101 5102 if (group) { 5103 query = query.group(group, data); 5104 } 5105 5106 return { 5107 total: total, 5108 data: query.toArray() 5109 }; 5110 } 5111 5112 function calculateAggregates(data, options) { 5113 options = options || {}; 5114 5115 var query = new Query(data), 5116 aggregates = options.aggregate, 5117 filter = options.filter; 5118 5119 if(filter) { 5120 query = query.filter(filter); 5121 } 5122 5123 return query.aggregate(aggregates); 5124 } 5125 5126 var LocalTransport = Class.extend({ 5127 init: function(options) { 5128 this.data = options.data; 5129 }, 5130 5131 read: function(options) { 5132 options.success(this.data); 5133 }, 5134 update: function(options) { 5135 options.success(options.data); 5136 }, 5137 create: function(options) { 5138 options.success(options.data); 5139 }, 5140 destroy: noop 5141 });
5142 5143 var RemoteTransport = Class.extend( { 5144 init: function(options) { 5145 var that = this, parameterMap; 5146 5147 options = that.options = extend({}, that.options, options); 5148 5149 each(crud, function(index, type) { 5150 if (typeof options[type] === STRING) { 5151 options[type] = { 5152 url: options[type] 5153 }; 5154 } 5155 }); 5156 5157 that.cache = options.cache? Cache.create(options.cache) : { 5158 find: noop, 5159 add: noop 5160 }; 5161 5162 parameterMap = options.parameterMap; 5163 5164 that.parameterMap = isFunction(parameterMap) ? parameterMap : function(options) { 5165 var result = {}; 5166 5167 each(options, function(option, value) { 5168 if (option in parameterMap) { 5169 option = parameterMap[option]; 5170 if (isPlainObject(option)) { 5171 value = option.value(value); 5172 option = option.key; 5173 } 5174 } 5175 5176 result[option] = value; 5177 }); 5178 5179 return result; 5180 }; 5181 }, 5182 5183 options: { 5184 parameterMap: identity 5185 }, 5186 5187 create: function(options) { 5188 return ajax(this.setup(options, CREATE)); 5189 }, 5190 5191 read: function(options) { 5192 var that = this, 5193 success, 5194 error, 5195 result, 5196 cache = that.cache; 5197 5198 options = that.setup(options, READ); 5199 5200 success = options.success || noop; 5201 error = options.error || noop; 5202 5203 result = cache.find(options.data); 5204 5205 if(result !== undefined) { 5206 success(result); 5207 } else { 5208 options.success = function(result) { 5209 cache.add(options.data, result); 5210 5211 success(result); 5212 }; 5213 5214 $.ajax(options); 5215 } 5216 }, 5217 5218 update: function(options) { 5219 return ajax(this.setup(options, UPDATE)); 5220 }, 5221 5222 destroy: function(options) { 5223 return ajax(this.setup(options, DESTROY)); 5224 }, 5225 5226 setup: function(options, type) { 5227 options = options || {}; 5228 5229 var that = this, 5230 parameters, 5231 operation = that.options[type], 5232 data = isFunction(operation.data) ? operation.data() : operation.data; 5233 5234 options = extend(true, {}, operation, options); 5235 parameters = extend(data, options.data); 5236 5237 options.data = that.parameterMap(parameters, type); 5238 5239 if (isFunction(options.url)) { 5240 options.url = options.url(parameters); 5241 } 5242 5243 return options; 5244 } 5245 }); 5246 5247 var Cache = Class.extend({ 5248 init: function() { 5249 this._store = {}; 5250 }, 5251 add: function(key, data) { 5252 if(key !== undefined) { 5253 this._store[stringify(key)] = data; 5254 } 5255 }, 5256 find: function(key) { 5257 return this._store[stringify(key)]; 5258 }, 5259 clear: function() { 5260 this._store = {}; 5261 }, 5262 remove: function(key) { 5263 delete this._store[stringify(key)]; 5264 } 5265 }); 5266 5267 Cache.create = function(options) { 5268 var store = { 5269 "inmemory": function() { return new Cache(); } 5270 }; 5271 5272 if (isPlainObject(options) && isFunction(options.find)) { 5273 return options; 5274 } 5275 5276 if (options === true) { 5277 return new Cache(); 5278 } 5279 5280 return store[options](); 5281 }; 5282 5283 var DataReader = Class.extend({ 5284 init: function(schema) { 5285 var that = this, member, get, model; 5286 5287 schema = schema || {}; 5288 5289 for (member in schema) { 5290 get = schema[member]; 5291 5292 that[member] = typeof get === STRING ? getter(get) : get; 5293 } 5294 5295 if (isPlainObject(that.model)) { 5296 that.model = model = kendo.data.Model.define(that.model); 5297 5298 var dataFunction = that.data, 5299 getters = {}; 5300 5301 if (model.fields) { 5302 each(model.fields, function(field, value) { 5303 if (isPlainObject(value) && value.field) { 5304 getters[value.field] = getter(value.field); 5305 } else { 5306 getters[field] = getter(field); 5307 } 5308 }); 5309 } 5310 5311 that.data = function(data) { 5312 var record, 5313 getter, 5314 idx, 5315 length, 5316 modelInstance = new that.model(); 5317 5318 data = dataFunction(data); 5319 5320 if (data && !isEmptyObject(getters)) { 5321 if (toString.call(data) !== "[object Array]" && !(data instanceof ObservableArray)) { 5322 data = [data]; 5323 } 5324 5325 for (idx = 0, length = data.length; idx < length; idx++) { 5326 record = data[idx]; 5327 for (getter in getters) { 5328 record[getter] = modelInstance._parse(getter, getters[getter](record)); 5329 } 5330 } 5331 } 5332 5333 return data || []; 5334 }; 5335 } 5336 }, 5337 parse: identity, 5338 data: identity, 5339 total: function(data) { 5340 return data.length; 5341 }, 5342 groups: identity, 5343 status: function(data) { 5344 return data.status; 5345 }, 5346 aggregates: function() { 5347 return {}; 5348 } 5349 });
5350 5351 function flattenGroups(data) { 5352 var idx, length, result = []; 5353 5354 for (idx = 0, length = data.length; idx < length; idx++) { 5355 if (data[idx].hasSubgroups) { 5356 result = result.concat(flattenGroups(data[idx].items)); 5357 } else { 5358 result = result.concat(data[idx].items.toJSON()); 5359 } 5360 } 5361 return result; 5362 } 5363 5364 var DataSource = Observable.extend({ 5365 init: function(options) { 5366 var that = this, id, model, transport; 5367 5368 options = that.options = extend({}, that.options, options); 5369 5370 extend(that, { 5371 _map: {}, 5372 _prefetch: {}, 5373 _data: [], 5374 _ranges: [], 5375 _view: [], 5376 _pristine: [], 5377 _destroyed: [], 5378 _pageSize: options.pageSize, 5379 _page: options.page || (options.pageSize ? 1 : undefined), 5380 _sort: normalizeSort(options.sort), 5381 _filter: normalizeFilter(options.filter), 5382 _group: normalizeGroup(options.group), 5383 _aggregate: options.aggregate 5384 }); 5385 5386 Observable.fn.init.call(that); 5387 5388 transport = options.transport; 5389 5390 if (transport) { 5391 transport.read = typeof transport.read === STRING ? { url: transport.read } : transport.read; 5392 5393 if (options.type) { 5394 transport = extend(true, {}, kendo.data.transports[options.type], transport); 5395 options.schema = extend(true, {}, kendo.data.schemas[options.type], options.schema); 5396 } 5397 5398 that.transport = isFunction(transport.read) ? transport: new RemoteTransport(transport); 5399 } else { 5400 that.transport = new LocalTransport({ data: options.data }); 5401 } 5402 5403 that.reader = new kendo.data.readers[options.schema.type || "json" ](options.schema); 5404 5405 model = that.reader.model || {}; 5406 5407 that._data = that._observe(that._data); 5408 5409 id = model.id; 5410 5411 if (id) { 5412 that.id = function(record) { 5413 return id(record); 5414 }; 5415 } 5416 5417 that.bind([ERROR, CHANGE, REQUESTSTART], options); 5418 }, 5419 5420 options: { 5421 data: [], 5422 schema: {}, 5423 serverSorting: false, 5424 serverPaging: false, 5425 serverFiltering: false, 5426 serverGrouping: false, 5427 serverAggregates: false, 5428 sendAllFields: true, 5429 batch: false 5430 }, 5431 5432 _flatData: function(data) { 5433 if (this.options.serverGrouping) { 5434 return flattenGroups(data); 5435 } 5436 return data; 5437 }, 5438 5439 get: function(id) { 5440 var idx, length, data = this._flatData(this._data); 5441 5442 for (idx = 0, length = data.length; idx < length; idx++) { 5443 if (data[idx].id == id) { 5444 return data[idx]; 5445 } 5446 } 5447 }, 5448 5449 getByUid: function(id) { 5450 var idx, length, data = this._flatData(this._data); 5451 5452 for (idx = 0, length = data.length; idx < length; idx++) { 5453 if (data[idx].uid == id) { 5454 return data[idx]; 5455 } 5456 } 5457 }, 5458 5459 sync: function() { 5460 var that = this, 5461 idx, 5462 length, 5463 created = [], 5464 updated = [], 5465 destroyed = that._destroyed, 5466 data = that._data; 5467 5468 if (!that.reader.model) { 5469 return; 5470 } 5471 5472 for (idx = 0, length = data.length; idx < length; idx++) { 5473 if (data[idx].isNew()) { 5474 created.push(data[idx]); 5475 } else if (data[idx].dirty) { 5476 updated.push(data[idx]); 5477 } 5478 } 5479 5480 var promises = that._send("create", created); 5481
5482 promises.push.apply(promises ,that._send("update", updated)); 5483 promises.push.apply(promises ,that._send("destroy", destroyed)); 5484 5485 $.when.apply(null, promises) 5486 .then(function() { 5487 var idx, 5488 length; 5489 5490 for (idx = 0, length = arguments.length; idx < length; idx++){ 5491 that._accept(arguments[idx]); 5492 } 5493 5494 that._change(); 5495 }); 5496 5497 }, 5498 5499 _accept: function(result) { 5500 var that = this, 5501 models = result.models, 5502 response = result.response, 5503 idx = 0, 5504 pristine = that.reader.data(that._pristine), 5505 type = result.type, 5506 length; 5507 5508 if (response) { 5509 response = that.reader.parse(response); 5510 5511 response = that.reader.data(response); 5512 5513 if (!$.isArray(response)) { 5514 response = [response]; 5515 } 5516 } else { 5517 response = $.map(models, function(model) { return model.toJSON(); } ); 5518 } 5519 5520 if (type === "destroy") { 5521 that._destroyed = []; 5522 } 5523 5524 for (idx = 0, length = models.length; idx < length; idx++) { 5525 if (type !== "destroy") { 5526 models[idx].accept(response[idx]); 5527 5528 if (type === "create") { 5529 pristine.push(models[idx]); 5530 } else if (type === "update") { 5531 extend(pristine[that._pristineIndex(models[idx])], response[idx]); 5532 } 5533 } else { 5534 pristine.splice(that._pristineIndex(models[idx]), 1); 5535 } 5536 } 5537 }, 5538 5539 _pristineIndex: function(model) { 5540 var that = this, 5541 idx, 5542 length, 5543 pristine = that.reader.data(that._pristine); 5544 5545 for (idx = 0, length = pristine.length; idx < length; idx++) { 5546 if (pristine[idx][model.idField] === model.id) { 5547 return idx; 5548 } 5549 } 5550 return -1; 5551 }, 5552 5553 _promise: function(data, models, type) { 5554 var that = this, 5555 transport = that.transport; 5556 5557 return $.Deferred(function(deferred) { 5558 transport[type].call(transport, extend({ 5559 success: function(response) { 5560 deferred.resolve({ 5561 response: response, 5562 models: models, 5563 type: type 5564 }); 5565 }, 5566 error: function(response) { 5567 deferred.reject(response); 5568 that.trigger(ERROR, response); 5569 } 5570 }, data) 5571 ); 5572 }).promise(); 5573 }, 5574 5575 _send: function(method, data) { 5576 var that = this, 5577 idx, 5578 length, 5579 promises = []; 5580 5581 if (that.options.batch) { 5582 if (data.length) { 5583 promises.push(that._promise( { data: { models: toJSON(data) } }, data , method)); 5584 } 5585 } else { 5586 for (idx = 0, length = data.length; idx < length; idx++) { 5587 promises.push(that._promise( { data: data[idx].toJSON() }, [ data[idx] ], method)); 5588 } 5589 } 5590 5591 return promises; 5592 }, 5593 5594 add: function(model) { 5595 return this.insert(this._data.length, model); 5596 }, 5597 5598 insert: function(index, model) { 5599 if (!model) { 5600 model = index; 5601 index = 0; 5602 } 5603 5604 if (!(model instanceof Model)) { 5605 if (this.reader.model) { 5606 model = new this.reader.model(model); 5607 } else { 5608 model = new ObservableObject(model); 5609 } 5610 } 5611 5612 this._data.splice(index, 0, model); 5613 5614 return model; 5615 }, 5616 5617 cancelChanges: function(model) { 5618 var that = this, 5619 pristineIndex, 5620 pristine = that.reader.data(that._pristine), 5621 index; 5622 5623 if (model instanceof kendo.data.Model) { 5624 index = that.indexOf(model); 5625 pristineIndex = that._pristineIndex(model); 5626 if (index != -1) { 5627 if (pristineIndex != -1 && !model.isNew()) { 5628 extend(true, that._data[index], pristine[pristineIndex]); 5629 } else { 5630 that._data.splice(index, 1); 5631 } 5632 } 5633 } else { 5634 that._destroyed = []; 5635 that._data = that._observe(pristine); 5636 that._change(); 5637 } 5638 }, 5639 5640 read: function(data) { 5641 var that = this, params = that._params(data); 5642 5643 that._queueRequest(params, function() { 5644 that.trigger(REQUESTSTART); 5645 that._ranges = []; 5646 that.transport.read({ 5647 data: params, 5648 success: proxy(that.success, that), 5649 error: proxy(that.error, that) 5650 }); 5651 }); 5652 }, 5653 5654 indexOf: function(model) { 5655 var idx, length, data = this._data; 5656 5657 if (model) { 5658 for (idx = 0, length = data.length; idx < length; idx++) { 5659 if (data[idx].uid == model.uid) { 5660 return idx; 5661 } 5662 } 5663 } 5664 return -1; 5665 }, 5666 5667 _params: function(data) { 5668 var that = this, 5669 options = extend({ 5670 take: that.take(), 5671 skip: that.skip(), 5672 page: that.page(), 5673 pageSize: that.pageSize(), 5674 sort: that._sort, 5675 filter: that._filter, 5676 group: that._group, 5677 aggregate: that._aggregate 5678 }, data); 5679 5680 if (!that.options.serverPaging) { 5681 delete options.take; 5682 delete options.skip; 5683 delete options.page; 5684 delete options.pageSize; 5685 } 5686 return options; 5687 }, 5688 5689 _queueRequest: function(options, callback) { 5690 var that = this; 5691 if (!that._requestInProgress) { 5692 that._requestInProgress = true; 5693 that._pending = undefined; 5694 callback(); 5695 } else { 5696 that._pending = { callback: proxy(callback, that), options: options }; 5697 } 5698 }, 5699 5700 _dequeueRequest: function() { 5701 var that = this; 5702 that._requestInProgress = false; 5703 if (that._pending) { 5704 that._queueRequest(that._pending.options, that._pending.callback); 5705 } 5706 }, 5707 5708 remove: function(model) { 5709 var idx, length, data = this._data; 5710 5711 for (idx = 0, length = data.length; idx < length; idx++) { 5712 if (data[idx].uid == model.uid) { 5713 model = data[idx]; 5714 data.splice(idx, 1); 5715 return model; 5716 } 5717 } 5718 }, 5719 5720 error: function(xhr, status, errorThrown) { 5721 this._dequeueRequest(); 5722 this.trigger(ERROR, { xhr: xhr, status: status, errorThrown: errorThrown }); 5723 }, 5724 5725 _parent: noop, 5726 5727 success: function(data) { 5728 var that = this, 5729 options = that.options, 5730 hasGroups = options.serverGrouping === true && that._group && that._group.length > 0;
5731 5732 data = that.reader.parse(data); 5733 5734 that._pristine = isPlainObject(data) ? $.extend(true, {}, data) : data.slice(0); 5735 5736 that._total = that.reader.total(data); 5737 5738 if (that._aggregate && options.serverAggregates) { 5739 that._aggregateResult = that.reader.aggregates(data); 5740 } 5741 5742 if (hasGroups) { 5743 data = that.reader.groups(data); 5744 } else { 5745 data = that.reader.data(data); 5746 } 5747 5748 that._data = that._observe(data); 5749 5750 var start = that._skip || 0, 5751 end = start + that._data.length; 5752 5753 that._ranges.push({ start: start, end: end, data: that._data }); 5754 that._ranges.sort( function(x, y) { return x.start - y.start; } ); 5755 5756 that._dequeueRequest(); 5757 that._process(that._data); 5758 }, 5759 5760 _observe: function(data) { 5761 var that = this, 5762 model = that.reader.model, 5763 wrap = false; 5764 5765 if (model && data.length) { 5766 wrap = !(data[0] instanceof model); 5767 } 5768 5769 if (data instanceof ObservableArray) { 5770 if (wrap) { 5771 data.type = that.reader.model; 5772 data.wrapAll(data, data); 5773 } 5774 } else { 5775 data = new ObservableArray(data, that.reader.model); 5776 data.parent = function() { return that._parent(); }; 5777 } 5778 5779 return data.bind(CHANGE, proxy(that._change, that)); 5780 }, 5781 5782 _change: function(e) { 5783 var that = this, idx, length, action = e ? e.action : ""; 5784 5785 if (action === "remove") { 5786 for (idx = 0, length = e.items.length; idx < length; idx++) { 5787 if (!e.items[idx].isNew || !e.items[idx].isNew()) { 5788 that._destroyed.push(e.items[idx]); 5789 } 5790 } 5791 } 5792 5793 if (that.options.autoSync && (action === "add" || action === "remove" || action === "itemchange")) { 5794 that.sync(); 5795 } else { 5796 var total = that._total || that.reader.total(that._pristine); 5797 if (action === "add") { 5798 total++; 5799 } else if (action === "remove") { 5800 total--; 5801 } else if (action !== "itemchange" && !that.options.serverPaging) { 5802 total = that.reader.total(that._pristine); 5803 } 5804 5805 that._total = total; 5806 that._process(that._data, e); 5807 } 5808 }, 5809 5810 _process: function (data, e) { 5811 var that = this, 5812 options = {}, 5813 result; 5814 5815 if (that.options.serverPaging !== true) { 5816 options.skip = that._skip; 5817 options.take = that._take || that._pageSize; 5818 5819 if(options.skip === undefined && that._page !== undefined && that._pageSize !== undefined) { 5820 options.skip = (that._page - 1) * that._pageSize; 5821 } 5822 } 5823 5824 if (that.options.serverSorting !== true) { 5825 options.sort = that._sort; 5826 } 5827 5828 if (that.options.serverFiltering !== true) { 5829 options.filter = that._filter; 5830 } 5831 5832 if (that.options.serverGrouping !== true) { 5833 options.group = that._group; 5834 } 5835 5836 if (that.options.serverAggregates !== true) { 5837 options.aggregate = that._aggregate; 5838 that._aggregateResult = calculateAggregates(data, options); 5839 } 5840 5841 result = process(data, options); 5842 5843 that._view = result.data; 5844 5845 if (result.total !== undefined && !that.options.serverFiltering) { 5846 that._total = result.total; 5847 } 5848 5849 that.trigger(CHANGE, e); 5850 }, 5851 5852 at: function(index) { 5853 return this._data[index]; 5854 }, 5855 5856 data: function(value) { 5857 var that = this; 5858 if (value !== undefined) { 5859 that._data = this._observe(value); 5860 5861 that._total = that._data.length; 5862 5863 that._process(that._data); 5864 } else { 5865 return that._data; 5866 } 5867 }, 5868 5869 view: function() { 5870 return this._view; 5871 }, 5872 5873 query: function(options) { 5874 var that = this, 5875 result, 5876 remote = that.options.serverSorting || that.options.serverPaging || that.options.serverFiltering || that.options.serverGrouping || that.options.serverAggregates;
5877 5878 if (options !== undefined) { 5879 that._pageSize = options.pageSize; 5880 that._page = options.page; 5881 that._sort = options.sort; 5882 that._filter = options.filter; 5883 that._group = options.group; 5884 that._aggregate = options.aggregate; 5885 that._skip = options.skip; 5886 that._take = options.take; 5887 5888 if(that._skip === undefined) { 5889 that._skip = that.skip(); 5890 options.skip = that.skip(); 5891 } 5892 5893 if(that._take === undefined && that._pageSize !== undefined) { 5894 that._take = that._pageSize; 5895 options.take = that._take; 5896 } 5897 5898 if (options.sort) { 5899 that._sort = options.sort = normalizeSort(options.sort); 5900 } 5901 5902 if (options.filter) { 5903 that._filter = options.filter = normalizeFilter(options.filter); 5904 } 5905 5906 if (options.group) { 5907 that._group = options.group = normalizeGroup(options.group); 5908 } 5909 if (options.aggregate) { 5910 that._aggregate = options.aggregate = normalizeAggregate(options.aggregate); 5911 } 5912 } 5913 5914 if (remote || (that._data === undefined || that._data.length === 0)) { 5915 that.read(options); 5916 } else { 5917 that.trigger(REQUESTSTART); 5918 result = process(that._data, options); 5919 5920 if (!that.options.serverFiltering) { 5921 if (result.total !== undefined) { 5922 that._total = result.total; 5923 } else { 5924 that._total = that._data.length; 5925 } 5926 } 5927 5928 that._view = result.data; 5929 that._aggregateResult = calculateAggregates(that._data, options); 5930 that.trigger(CHANGE); 5931 } 5932 }, 5933 5934 fetch: function(callback) { 5935 var that = this; 5936 5937 if (callback && isFunction(callback)) { 5938 that.one(CHANGE, callback); 5939 } 5940 5941 that._query(); 5942 }, 5943 5944 _query: function(options) { 5945 var that = this; 5946 5947 that.query(extend({}, { 5948 page: that.page(), 5949 pageSize: that.pageSize(), 5950 sort: that.sort(), 5951 filter: that.filter(), 5952 group: that.group(), 5953 aggregate: that.aggregate() 5954 }, options)); 5955 }, 5956 5957 next: function() { 5958 var that = this, 5959 page = that.page(), 5960 total = that.total(); 5961 5962 if (!page) { 5963 return; 5964 } 5965 5966 if (total) { 5967 that.page(page + 1); 5968 } else { 5969 that._skip = page * that.take(); 5970 that._query({ page: page + 1 }); 5971 } 5972 }, 5973 5974 prev: function() { 5975 var that = this, 5976 page = that.page(), 5977 total = that.total(); 5978 5979 if (!page || page === 1) { 5980 return; 5981 } 5982 5983 if (total) { 5984 that.page(page - 1); 5985 } else { 5986 that._skip = that._skip - that.take(); 5987 that._query({ page: page - 1}); 5988 } 5989 }, 5990 5991 page: function(val) { 5992 var that = this, 5993 skip; 5994 5995 if(val !== undefined) { 5996 val = math.max(math.min(math.max(val, 1), that.totalPages()), 1); 5997 that._query({ page: val }); 5998 return; 5999 } 6000 skip = that.skip(); 6001 6002 return skip !== undefined ? math.round((skip || 0) / (that.take() || 1)) + 1 : undefined; 6003 }, 6004 6005 pageSize: function(val) { 6006 var that = this; 6007 6008 if(val !== undefined) { 6009 that._query({ pageSize: val }); 6010 return; 6011 } 6012 6013 return that.take(); 6014 }, 6015 6016 sort: function(val) { 6017 var that = this; 6018 6019 if(val !== undefined) { 6020 that._query({ sort: val }); 6021 return; 6022 } 6023 6024 return that._sort; 6025 }, 6026 6027 filter: function(val) { 6028 var that = this; 6029 6030 if (val === undefined) { 6031 return that._filter; 6032 } 6033 6034 that._query({ filter: val, page: 1 }); 6035 }, 6036 6037 group: function(val) { 6038 var that = this; 6039 6040 if(val !== undefined) { 6041 that._query({ group: val }); 6042 return; 6043 } 6044 6045 return that._group; 6046 }, 6047 6048 total: function() { 6049 return this._total || 0; 6050 }, 6051 6052 aggregate: function(val) { 6053 var that = this; 6054 6055 if(val !== undefined) { 6056 that._query({ aggregate: val }); 6057 return; 6058 } 6059 6060 return that._aggregate; 6061 }, 6062 6063 aggregates: function() { 6064 return this._aggregateResult; 6065 }, 6066 6067 totalPages: function() { 6068 var that = this, 6069 pageSize = that.pageSize() || that.total(); 6070 6071 return math.ceil((that.total() || 0) / pageSize); 6072 }, 6073 6074 inRange: function(skip, take) { 6075 var that = this, 6076 end = math.min(skip + take, that.total()); 6077 6078 if (!that.options.serverPaging && that.data.length > 0) { 6079 return true; 6080 } 6081 6082 return that._findRange(skip, end).length > 0;
6083 }, 6084 6085 range: function(skip, take) { 6086 skip = math.min(skip || 0, this.total()); 6087 var that = this, 6088 pageSkip = math.max(math.floor(skip / take), 0) * take, 6089 size = math.min(pageSkip + take, that.total()), 6090 data; 6091 6092 data = that._findRange(skip, math.min(skip + take, that.total())); 6093 6094 if (data.length) { 6095 that._skip = skip > that.skip() ? math.min(size, (that.totalPages() - 1) * that.take()) : pageSkip; 6096 6097 that._take = take; 6098 6099 var paging = that.options.serverPaging; 6100 var sorting = that.options.serverSorting; 6101 try { 6102 that.options.serverPaging = true; 6103 that.options.serverSorting = true; 6104 if (paging) { 6105 that._data = data = that._observe(data); 6106 } 6107 that._process(data); 6108 } finally { 6109 that.options.serverPaging = paging; 6110 that.options.serverSorting = sorting; 6111 } 6112 6113 return; 6114 } 6115 6116 if (take !== undefined) { 6117 if (!that._rangeExists(pageSkip, size)) { 6118 that.prefetch(pageSkip, take, function() { 6119 if (skip > pageSkip && size < that.total() && !that._rangeExists(size, math.min(size + take, that.total()))) { 6120 that.prefetch(size, take, function() { 6121 that.range(skip, take); 6122 }); 6123 } else { 6124 that.range(skip, take); 6125 } 6126 }); 6127 } else if (pageSkip < skip) { 6128 that.prefetch(size, take, function() { 6129 that.range(skip, take); 6130 }); 6131 } 6132 } 6133 }, 6134 6135 _findRange: function(start, end) { 6136 var that = this, 6137 ranges = that._ranges, 6138 range, 6139 data = [], 6140 skipIdx, 6141 takeIdx, 6142 startIndex, 6143 endIndex, 6144 rangeData, 6145 rangeEnd, 6146 processed, 6147 options = that.options, 6148 remote = options.serverSorting || options.serverPaging || options.serverFiltering || options.serverGrouping || options.serverAggregates, 6149 length; 6150 6151 for (skipIdx = 0, length = ranges.length; skipIdx < length; skipIdx++) { 6152 range = ranges[skipIdx]; 6153 if (start >= range.start && start <= range.end) { 6154 var count = 0; 6155 6156 for (takeIdx = skipIdx; takeIdx < length; takeIdx++) { 6157 range = ranges[takeIdx]; 6158 6159 if (range.data.length && start + count >= range.start /*&& count + count <= range.end*/) { 6160 rangeData = range.data; 6161 rangeEnd = range.end; 6162 6163 if (!remote) { 6164 processed = process(range.data, { sort: that.sort(), filter: that.filter() }); 6165 rangeData = processed.data; 6166 6167 if (processed.total !== undefined) { 6168 rangeEnd = processed.total; 6169 } 6170 } 6171 6172 startIndex = 0; 6173 if (start + count > range.start) { 6174 startIndex = (start + count) - range.start; 6175 } 6176 endIndex = rangeData.length; 6177 if (rangeEnd > end) { 6178 endIndex = endIndex - (rangeEnd - end); 6179 } 6180 count += endIndex - startIndex; 6181 data = data.concat(rangeData.slice(startIndex, endIndex)); 6182 6183 if (end <= range.end && count == end - start) { 6184 return data; 6185 } 6186 } 6187 } 6188 break; 6189 } 6190 } 6191 return []; 6192 }, 6193 6194 skip: function() { 6195 var that = this; 6196 6197 if (that._skip === undefined) { 6198 return (that._page !== undefined ? (that._page - 1) * (that.take() || 1) : undefined); 6199 } 6200 return that._skip; 6201 }, 6202 6203 take: function() { 6204 var that = this; 6205 return that._take || that._pageSize; 6206 }, 6207 6208 prefetch: function(skip, take, callback) { 6209 var that = this, 6210 size = math.min(skip + take, that.total()), 6211 range = { start: skip, end: size, data: [] }, 6212 options = { 6213 take: take, 6214 skip: skip, 6215 page: skip / take + 1, 6216 pageSize: take, 6217 sort: that._sort, 6218 filter: that._filter, 6219 group: that._group, 6220 aggregate: that._aggregate 6221 }; 6222 6223 if (!that._rangeExists(skip, size)) { 6224 clearTimeout(that._timeout); 6225 6226 that._timeout = setTimeout(function() { 6227 that._queueRequest(options, function() { 6228 that.transport.read({ 6229 data: options, 6230 success: function (data) { 6231 that._dequeueRequest(); 6232 var found = false; 6233 for (var i = 0, len = that._ranges.length; i < len; i++) { 6234 if (that._ranges[i].start === skip) { 6235 found = true; 6236 range = that._ranges[i]; 6237 break; 6238 } 6239 } 6240 if (!found) { 6241 that._ranges.push(range); 6242 } 6243 6244 data = that.reader.parse(data); 6245 range.data = that._observe(that.reader.data(data)); 6246 range.end = range.start + range.data.length; 6247 that._ranges.sort( function(x, y) { return x.start - y.start; } ); 6248 that._total = that.reader.total(data); 6249 if (callback) { 6250 callback(); 6251 } 6252 } 6253 }); 6254 }); 6255 }, 100); 6256 } else if (callback) { 6257 callback(); 6258 } 6259 }, 6260 6261 _rangeExists: function(start, end) { 6262 var that = this, 6263 ranges = that._ranges, 6264 idx, 6265 length; 6266 6267 for (idx = 0, length = ranges.length; idx < length; idx++) { 6268 if (ranges[idx].start <= start && ranges[idx].end >= end) { 6269 return true; 6270 } 6271 } 6272 return false; 6273 } 6274 });
6275 6276 DataSource.create = function(options) { 6277 options = options && options.push ? { data: options } : options; 6278 6279 var dataSource = options || {}, 6280 data = dataSource.data, 6281 fields = dataSource.fields, 6282 table = dataSource.table, 6283 select = dataSource.select, 6284 idx, 6285 length, 6286 model = {}, 6287 field; 6288 6289 if (!data && fields && !dataSource.transport) { 6290 if (table) { 6291 data = inferTable(table, fields); 6292 } else if (select) { 6293 data = inferSelect(select, fields); 6294 } 6295 } 6296 6297 if (kendo.data.Model && fields && (!dataSource.schema || !dataSource.schema.model)) { 6298 for (idx = 0, length = fields.length; idx < length; idx++) { 6299 field = fields[idx]; 6300 if (field.type) { 6301 model[field.field] = field; 6302 } 6303 } 6304 6305 if (!isEmptyObject(model)) { 6306 dataSource.schema = extend(true, dataSource.schema, { model: { fields: model } }); 6307 } 6308 } 6309 6310 dataSource.data = data; 6311 6312 return dataSource instanceof DataSource ? dataSource : new DataSource(dataSource); 6313 }; 6314 6315 function inferSelect(select, fields) { 6316 var options = $(select)[0].children, 6317 idx, 6318 length, 6319 data = [], 6320 record, 6321 firstField = fields[0], 6322 secondField = fields[1], 6323 value, 6324 option; 6325 6326 for (idx = 0, length = options.length; idx < length; idx++) { 6327 record = {}; 6328 option = options[idx]; 6329 6330 record[firstField.field] = option.text; 6331 6332 value = option.attributes.value; 6333 6334 if (value && value.specified) { 6335 value = option.value; 6336 } else { 6337 value = option.text; 6338 } 6339 6340 record[secondField.field] = value; 6341 6342 data.push(record); 6343 } 6344 6345 return data; 6346 } 6347 6348 function inferTable(table, fields) { 6349 var tbody = $(table)[0].tBodies[0], 6350 rows = tbody ? tbody.rows : [], 6351 idx, 6352 length, 6353 fieldIndex, 6354 fieldCount = fields.length, 6355 data = [], 6356 cells, 6357 record, 6358 cell, 6359 empty; 6360 6361 for (idx = 0, length = rows.length; idx < length; idx++) { 6362 record = {}; 6363 empty = true; 6364 cells = rows[idx].cells; 6365 6366 for (fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { 6367 cell = cells[fieldIndex]; 6368 if(cell.nodeName.toLowerCase() !== "th") { 6369 empty = false; 6370 record[fields[fieldIndex].field] = cell.innerHTML; 6371 } 6372 } 6373 if(!empty) { 6374 data.push(record); 6375 } 6376 } 6377 6378 return data; 6379 } 6380 6381 extend(true, kendo.data, /** @lends kendo.data */ { 6382 readers: { 6383 json: DataReader 6384 }, 6385 Query: Query, 6386 DataSource: DataSource, 6387 ObservableObject: ObservableObject, 6388 ObservableArray: ObservableArray, 6389 LocalTransport: LocalTransport, 6390 RemoteTransport: RemoteTransport,
6391 Cache: Cache, 6392 DataReader: DataReader, 6393 Model: Model 6394 }); 6395})(jQuery); 6396(function ($, unefined) { 6397 var kendo = window.kendo, 6398 Observable = kendo.Observable, 6399 ObservableObject = kendo.data.ObservableObject, 6400 ObservableArray = kendo.data.ObservableArray, 6401 toString = {}.toString, 6402 binders = {}, 6403 Class = kendo.Class, 6404 innerText, 6405 proxy = $.proxy, 6406 VALUE = "value", 6407 CHECKED = "checked", 6408 CHANGE = "change"; 6409 6410 (function() { 6411 var a = document.createElement("a"); 6412 if (a.innerText !== undefined) { 6413 innerText = "innerText"; 6414 } else if (a.textContent !== undefined) { 6415 innerText = "textContent"; 6416 } 6417 })(); 6418 6419 var Binding = Observable.extend( { 6420 init: function(source, path) { 6421 var that = this; 6422 6423 Observable.fn.init.call(that); 6424 6425 that.source = source; 6426 that.path = path; 6427 that.dependencies = {}; 6428 that.dependencies[path] = true; 6429 that.observable = that.source instanceof Observable; 6430 6431 that._access = function(e) { 6432 that.dependencies[e.field] = true; 6433 }; 6434 6435 if (that.observable) { 6436 that._change = function(e) { 6437 that.change(e); 6438 }; 6439 6440 that.source.bind(CHANGE, that._change); 6441 } 6442 }, 6443 6444 change: function(e) { 6445 var dependency, 6446 idx, 6447 ch, 6448 that = this; 6449 6450 if (that.path === "this") { 6451 that.trigger(CHANGE, e); 6452 } else { 6453 for (dependency in that.dependencies) { 6454 idx = dependency.indexOf(e.field); 6455 6456 if (idx === 0) {
6457 ch = dependency.charAt(e.field.length); 6458 6459 if (!ch || ch === "." || ch === "[") { 6460 that.trigger(CHANGE, e); 6461 break; 6462 } 6463 } 6464 } 6465 } 6466 }, 6467 6468 start: function() { 6469 if (this.observable) { 6470 this.source.bind("get", this._access); 6471 } 6472 }, 6473 6474 stop: function() { 6475 if (this.observable) { 6476 this.source.unbind("get", this._access); 6477 } 6478 }, 6479 6480 get: function() { 6481 var that = this, 6482 source = that.source, 6483 index, 6484 path = that.path, 6485 result = source; 6486 6487 that.start(); 6488 6489 if (that.observable) { 6490 result = source.get(path); 6491 6492 // Traverse the observable hierarchy if the binding is not resolved at the current level. 6493 while (result === undefined && source) { 6494 source = source.parent(); 6495 6496 if (source instanceof ObservableObject) { 6497 result = source.get(path); 6498 } 6499 } 6500 6501 // If the result is a function - invoke it 6502 if (typeof result === "function") { 6503 index = path.lastIndexOf("."); 6504 6505 // If the function is a member of a nested observable object make that nested observable the context (this) of the function 6506 if (index > 0) { 6507 source = source.get(path.substring(0, index)); 6508 } 6509 6510 // Set the context (this) of the function 6511 result = proxy(result, source); 6512 6513 // Invoke the function 6514 result = result(that.source); 6515 } 6516 } 6517 6518 that.stop(); 6519 6520 return result; 6521 }, 6522 6523 set: function(value) { 6524 this.source.set(this.path, value); 6525 }, 6526 6527 destroy: function() { 6528 if (this.observable) { 6529 this.source.unbind(CHANGE, this._change); 6530 } 6531 } 6532 }); 6533 6534 var EventBinding = Binding.extend( { 6535 get: function() { 6536 var source = this.source, 6537 path = this.path, 6538 handler; 6539 6540 handler = source.get(path); 6541 6542 while (!handler && source) { 6543 source = source.parent(); 6544 if (source instanceof ObservableObject) { 6545 handler = source.get(path); 6546 } 6547 } 6548 6549 return proxy(handler, source); 6550 } 6551 }); 6552 6553 var TemplateBinding = Binding.extend( { 6554 init: function(source, path, template) { 6555 var that = this; 6556 6557 Binding.fn.init.call(that, source, path); 6558 6559 that.template = template; 6560 }, 6561 6562 render: function(value) { 6563 var html; 6564 6565 this.start(); 6566 6567 html = kendo.render(this.template, value); 6568 6569 this.stop(); 6570 6571 return html; 6572 } 6573 }); 6574 6575 var Binder = Class.extend({ 6576 init: function(element, bindings, options) { 6577 this.element = element; 6578 this.bindings = bindings; 6579 this.options = options; 6580 }, 6581 6582 bind: function(binding, attribute) { 6583 var that = this; 6584 6585 binding = attribute ? binding[attribute] : binding; 6586 6587 binding.bind(CHANGE, function(e) { 6588 that.refresh(attribute || e); 6589 }); 6590 6591 that.refresh(attribute); 6592 }, 6593 6594 destroy: function() { 6595 } 6596 }); 6597 6598 binders.attr = Binder.extend({ 6599 refresh: function(key) { 6600 this.element.setAttribute(key, this.bindings.attr[key].get()); 6601 } 6602 }); 6603 6604 binders.style = Binder.extend({ 6605 refresh: function(key) { 6606 this.element.style[key] = this.bindings.style[key].get(); 6607 } 6608 });
6609 6610 binders.enabled = Binder.extend({ 6611 refresh: function() { 6612 if (this.bindings.enabled.get()) { 6613 this.element.removeAttribute("disabled"); 6614 } else { 6615 this.element.setAttribute("disabled", "disabled"); 6616 } 6617 } 6618 }); 6619 6620 binders.disabled = Binder.extend({ 6621 refresh: function() { 6622 if (this.bindings.disabled.get()) { 6623 this.element.setAttribute("disabled", "disabled"); 6624 } else { 6625 this.element.removeAttribute("disabled"); 6626 } 6627 } 6628 }); 6629 6630 binders.events = Binder.extend({ 6631 init: function(element, bindings, options) { 6632 Binder.fn.init.call(this, element, bindings, options); 6633 this.handlers = {}; 6634 }, 6635 6636 refresh: function(key) { 6637 var binding = this.bindings.events[key], 6638 handler = this.handlers[key] = binding.get(); 6639 6640 $(this.element).bind(key, binding.source, handler); 6641 }, 6642 6643 destroy: function() { 6644 var element = $(this.element), 6645 handler; 6646 6647 for (handler in this.handlers) { 6648 element.unbind(handler, this.handlers[handler]); 6649 } 6650 } 6651 }); 6652 6653 binders.text = Binder.extend({ 6654 refresh: function() { 6655 var text = this.bindings.text.get(); 6656 6657 if (text == null) { 6658 text = ""; 6659 } 6660 6661 this.element[innerText] = text; 6662 } 6663 }); 6664 6665 binders.visible = Binder.extend({ 6666 refresh: function() { 6667 if (this.bindings.visible.get()) { 6668 this.element.style.display = ""; 6669 } else { 6670 this.element.style.display = "none"; 6671 } 6672 } 6673 }); 6674 6675 binders.invisible = Binder.extend({ 6676 refresh: function() { 6677 if (!this.bindings.invisible.get()) { 6678 this.element.style.display = ""; 6679 } else { 6680 this.element.style.display = "none"; 6681 } 6682 } 6683 }); 6684 6685 binders.html = Binder.extend({ 6686 refresh: function() { 6687 this.element.innerHTML = this.bindings.html.get(); 6688 } 6689 }); 6690 6691 binders.value = Binder.extend({ 6692 init: function(element, bindings, options) { 6693 Binder.fn.init.call(this, element, bindings, options); 6694 6695 this._change = proxy(this.change, this); 6696 this.eventName = options.valueUpdate || CHANGE; 6697 6698 $(this.element).bind(this.eventName, this._change); 6699 6700 this._initChange = false; 6701 }, 6702 6703 change: function() { 6704 this._initChange = this.eventName != CHANGE; 6705 this.bindings[VALUE].set(this.element.value); 6706 }, 6707 6708 refresh: function() { 6709 if (!this._initChange) { 6710 var value = this.bindings[VALUE].get(); 6711 6712 if (value == null) { 6713 value = ""; 6714 } 6715 6716 this.element.value = value; 6717 } 6718 6719 this._initChange = false; 6720 }, 6721 6722 destroy: function() { 6723 $(this.element).unbind(this.eventName, this._change); 6724 } 6725 }); 6726 6727 binders.source = Binder.extend({ 6728 init: function(element, bindings, options) { 6729 Binder.fn.init.call(this, element, bindings, options); 6730 }, 6731 6732 refresh: function(e) { 6733 var that = this, 6734 source = that.bindings.source.get(); 6735 6736 if (source instanceof ObservableArray) { 6737 e = e || {}; 6738 6739 if (e.action == "add") { 6740 that.add(e.index, e.items); 6741 } else if (e.action == "remove") { 6742 that.remove(e.index, e.items); 6743 } else if (e.action != "itemchange") { 6744 that.render(); 6745 } 6746 } else { 6747 that.render(); 6748 } 6749 }, 6750 6751 container: function() { 6752 var element = this.element; 6753 6754 if (element.nodeName.toLowerCase() == "table") { 6755 if (!element.tBodies[0]) { 6756 element.appendChild(document.createElement("tbody")); 6757 } 6758 element = element.tBodies[0]; 6759 } 6760 6761 return element; 6762 }, 6763 6764 template: function() { 6765 var options = this.options, 6766 template = options.template, 6767 nodeName = this.container().nodeName.toLowerCase(); 6768 6769 if (!template) { 6770 if (nodeName == "select") { 6771 if (options.valueField || options.textField) { 6772 template = kendo.format('<option value="#:{0}#">#:{1}#</option>', 6773 options.valueField || options.textField, options.textField || options.valueField); 6774 } else { 6775 template = "<option>#:data#</option>"; 6776 } 6777 } else if (nodeName == "tbody") { 6778 template = "<tr><td>#:data#</td></tr>"; 6779 } else if (nodeName == "ul" || nodeName == "ol") { 6780 template = "<li>#:data#</li>"; 6781 } else { 6782 template = "#:data#"; 6783 } 6784 6785 template = kendo.template(template); 6786 } 6787 6788 return template; 6789 }, 6790 6791 destroy: function() { 6792 var source = this.bindings.source.get(); 6793 6794 source.unbind(CHANGE, this._change); 6795 }, 6796 6797 add: function(index, items) { 6798 var element = this.container(), 6799 idx, 6800 length, 6801 child, 6802 clone = element.cloneNode(false), 6803 reference = element.children[index]; 6804 6805 $(clone).html(kendo.render(this.template(), items)); 6806 6807 if (clone.children.length) { 6808 for (idx = 0, length = items.length; idx < length; idx++) { 6809 child = clone.children[0]; 6810 element.insertBefore(child, reference || null); 6811 bindElement(child, items[idx]); 6812 } 6813 } 6814 }, 6815 6816 remove: function(index, items) { 6817 var idx, 6818 element = this.container(); 6819 6820 for (idx = 0; idx < items.length; idx++) { 6821 element.removeChild(element.children[index]); 6822 } 6823 }, 6824 6825 render: function() { 6826 var source = this.bindings.source.get(), 6827 idx, 6828 length, 6829 element = this.container(), 6830 template = this.template(), 6831 parent; 6832 6833 if (!(source instanceof ObservableArray) && toString.call(source) !== "[object Array]") { 6834 if (source.parent) { 6835 parent = source.parent; 6836 } 6837 6838 source = new ObservableArray([source]); 6839 6840 if (source.parent) { 6841 source.parent = parent; 6842 } 6843 } 6844 6845 if (this.bindings.template) { 6846 $(element).html(this.bindings.template.render(source)); 6847 6848 if (element.children.length) { 6849 for (idx = 0, length = source.length; idx < length; idx++) { 6850 bindElement(element.children[idx], source[idx]); 6851 } 6852 } 6853 } 6854 else { 6855 $(element).html(kendo.render(template, source)); 6856 } 6857 } 6858 });
6859 6860 binders.input = { 6861 checked: Binder.extend({ 6862 init: function(element, bindings, options) { 6863 Binder.fn.init.call(this, element, bindings, options); 6864 this._change = proxy(this.change, this); 6865 6866 $(this.element).change(this._change); 6867 }, 6868 change: function() { 6869 var element = this.element; 6870 var value = this.value(); 6871 6872 if (element.type == "radio") { 6873 this.bindings[CHECKED].set(value); 6874 } else if (element.type == "checkbox") { 6875 var source = this.bindings[CHECKED].get(); 6876 var index; 6877 6878 if (source instanceof ObservableArray) { 6879 if (value !== false && value !== true) { 6880 index = source.indexOf(value); 6881 if (index > -1) { 6882 source.splice(index, 1); 6883 } else { 6884 source.push(value); 6885 } 6886 } 6887 } else { 6888 this.bindings[CHECKED].set(value); 6889 } 6890 } 6891 }, 6892 6893 refresh: function() { 6894 var value = this.bindings[CHECKED].get(); 6895 var element = this.element; 6896 6897 if (element.type == "checkbox") { 6898 if (value instanceof ObservableArray) { 6899 if (value.indexOf(this.value(element)) >= 0) { 6900 value = true; 6901 } 6902 } 6903 6904 element.checked = value === true; 6905 } else if (element.type == "radio" && value != null) { 6906 if (element.value === value.toString()) { 6907 element.checked = true; 6908 } 6909 } 6910 }, 6911 6912 value: function() { 6913 var element = this.element, 6914 value = element.value; 6915 6916 if (element.type == "checkbox") { 6917 if (value == "on" || value == "off") { 6918 value = element.checked; 6919 } 6920 } 6921 6922 return value; 6923 }, 6924 destroy: function() { 6925 $(this.element).unbind(CHANGE, this._change); 6926 } 6927 }) 6928 }; 6929 6930 binders.select = { 6931 value: Binder.extend({ 6932 init: function(target, bindings, options) { 6933 Binder.fn.init.call(this, target, bindings, options); 6934 6935 this._change = proxy(this.change, this); 6936 $(this.element).change(this._change); 6937 }, 6938 6939 change: function() { 6940 var values = [], 6941 element = this.element, 6942 source, 6943 field = this.options.valueField || this.options.textField, 6944 option, 6945 valueIndex, 6946 value, 6947 idx, 6948 length; 6949 6950 for (idx = 0, length = element.options.length; idx < length; idx++) { 6951 option = element.options[idx]; 6952 6953 if (option.selected) { 6954 value = option.attributes.value; 6955 6956 if (value && value.specified) { 6957 value = option.value; 6958 } else { 6959 value = option.text; 6960 } 6961 6962 values.push(value); 6963 } 6964 } 6965 6966 if (field) { 6967 source = this.bindings.source.get(); 6968 for (valueIndex = 0; valueIndex < values.length; valueIndex++) { 6969 for (idx = 0, length = source.length; idx < length; idx++) { 6970 if (source[idx].get(field) == values[valueIndex]) { 6971 values[valueIndex] = source[idx]; 6972 break; 6973 } 6974 } 6975 } 6976 } 6977 6978 value = this.bindings[VALUE].get(); 6979 if (value instanceof ObservableArray) { 6980 value.splice.apply(value, [0, value.length].concat(values)); 6981 } else if (value instanceof ObservableObject || !field) { 6982 this.bindings[VALUE].set(values[0]); 6983 } else { 6984 this.bindings[VALUE].set(values[0].get(field)); 6985 } 6986 }, 6987 refresh: function() { 6988 var optionIndex, 6989 element = this.element, 6990 options = element.options, 6991 value = this.bindings[VALUE].get(), 6992 values = value, 6993 field = this.options.valueField || this.options.textField, 6994 optionValue; 6995 6996 if (!(values instanceof ObservableArray)) { 6997 values = new ObservableArray([value]); 6998 } 6999 7000 for (var valueIndex = 0; valueIndex < values.length; valueIndex++) { 7001 value = values[valueIndex]; 7002 7003 if (field && value instanceof ObservableObject) { 7004 value = value.get(field); 7005 } 7006 7007 for (optionIndex = 0; optionIndex < options.length; optionIndex++) { 7008 optionValue = options[optionIndex].value; 7009 if (optionValue === "" && value !== "") { 7010 optionValue = options[optionIndex].text; 7011 } 7012 7013 if (optionValue == value) { 7014 options[optionIndex].selected = true; 7015 } 7016 } 7017 } 7018 }, 7019 destroy: function() { 7020 $(this.element).unbind(CHANGE, this._change); 7021 } 7022 }) 7023 }; 7024 7025 binders.widget = { 7026 events : Binder.extend({ 7027 init: function(widget, bindings, options) { 7028 Binder.fn.init.call(this, widget.element[0], bindings, options); 7029 this.widget = widget; 7030 this.handlers = {}; 7031 }, 7032 7033 refresh: function(key) { 7034 var binding = this.bindings.events[key], 7035 handler = binding.get(); 7036 7037 this.handlers[key] = function(e) { 7038 e.data = binding.source; 7039 7040 handler(e); 7041 }; 7042 7043 this.widget.bind(key, this.handlers[key]); 7044 }, 7045 7046 destroy: function() { 7047 var handler; 7048 7049 for (handler in this.handlers) { 7050 this.widget.unbind(handler, this.handlers[handler]); 7051 } 7052 } 7053 }), 7054 7055 checked: Binder.extend({ 7056 init: function(widget, bindings, options) { 7057 Binder.fn.init.call(this, widget.element[0], bindings, options); 7058 7059 this.widget = widget; 7060 this._change = proxy(this.change, this); 7061 this.widget.bind(CHANGE, this._change); 7062 }, 7063 change: function() { 7064 this.bindings[CHECKED].set(this.value()); 7065 }, 7066 7067 refresh: function() { 7068 this.widget.check(this.bindings[CHECKED].get() === true); 7069 }, 7070 7071 value: function() { 7072 var element = this.element, 7073 value = element.value; 7074 7075 if (value == "on" || value == "off") { 7076 value = element.checked; 7077 } 7078 7079 return value; 7080 }, 7081 7082 destroy: function() { 7083 this.widget.unbind(CHANGE, this._change); 7084 } 7085 }), 7086 7087 visible: Binder.extend({ 7088 init: function(widget, bindings, options) { 7089 Binder.fn.init.call(this, widget.element[0], bindings, options); 7090 7091 this.widget = widget; 7092 }, 7093 7094 refresh: function() { 7095 var visible = this.bindings.visible.get(); 7096 this.widget.wrapper[0].style.display = visible ? "" : "none"; 7097 } 7098 }), 7099 7100 invisible: Binder.extend({ 7101 init: function(widget, bindings, options) { 7102 Binder.fn.init.call(this, widget.element[0], bindings, options); 7103 7104 this.widget = widget; 7105 }, 7106 7107 refresh: function() { 7108 var invisible = this.bindings.invisible.get(); 7109 this.widget.wrapper[0].style.display = invisible ? "none" : ""; 7110 } 7111 }), 7112 7113 enabled: Binder.extend({ 7114 init: function(widget, bindings, options) { 7115 Binder.fn.init.call(this, widget.element[0], bindings, options); 7116 7117 this.widget = widget; 7118 }, 7119 7120 refresh: function() { 7121 if (this.widget.enable) { 7122 this.widget.enable(this.bindings.enabled.get()); 7123 } 7124 } 7125 }), 7126 7127 disabled: Binder.extend({ 7128 init: function(widget, bindings, options) { 7129 Binder.fn.init.call(this, widget.element[0], bindings, options); 7130 7131 this.widget = widget; 7132 }, 7133 7134 refresh: function() { 7135 if (this.widget.enable) { 7136 this.widget.enable(!this.bindings.disabled.get()); 7137 } 7138 } 7139 }), 7140 7141 source: Binder.extend({ 7142 init: function(widget, bindings, options) { 7143 var that = this; 7144 7145 Binder.fn.init.call(that, widget.element[0], bindings, options); 7146 7147 that.widget = widget; 7148 that._dataBinding = proxy(that.dataBinding, that); 7149 that._dataBound = proxy(that.dataBound, that); 7150 that._itemChange = proxy(that.itemChange, that); 7151 }, 7152 7153 itemChange: function(e) { 7154 bindElement(e.item[0], e.data, e.ns || kendo.ui); 7155 }, 7156 7157 dataBinding: function() { 7158 var idx, 7159 length, 7160 widget = this.widget,
7161 items = widget.items(); 7162 7163 for (idx = 0, length = items.length; idx < length; idx++) { 7164 unbindElementTree(items[idx]); 7165 } 7166 }, 7167 7168 dataBound: function(e) { 7169 var idx, 7170 length, 7171 widget = this.widget, 7172 items = widget.items(), 7173 dataSource = widget.dataSource, 7174 view = dataSource.view(), 7175 ns = e.ns || kendo.ui, 7176 groups = dataSource.group() || []; 7177 7178 if (items.length) { 7179 if (groups.length) { 7180 view = flattenGroups(view); 7181 } 7182 7183 for (idx = 0, length = view.length; idx < length; idx++) { 7184 bindElement(items[idx], view[idx], ns); 7185 } 7186 } 7187 }, 7188 7189 refresh: function(e) { 7190 var that = this, 7191 source, 7192 widget = that.widget; 7193 7194 e = e || {}; 7195 7196 if (!e.action) { 7197 that.destroy(); 7198 7199 widget.bind("dataBinding", that._dataBinding); 7200 widget.bind("dataBound", that._dataBound); 7201 widget.bind("itemChange", that._itemChange); 7202 7203 if (widget.dataSource instanceof kendo.data.DataSource) { 7204 source = that.bindings.source.get(); 7205 if (source instanceof kendo.data.DataSource) { 7206 widget.setDataSource(source); 7207 } else { 7208 widget.dataSource.data(source); 7209 } 7210 } 7211 } 7212 }, 7213 7214 destroy: function() { 7215 var widget = this.widget; 7216 7217 widget.unbind("dataBinding", this._dataBinding); 7218 widget.unbind("dataBound", this._dataBound); 7219 widget.unbind("itemChange", this._itemChange); 7220 } 7221 }), 7222 7223 value: Binder.extend({ 7224 init: function(widget, bindings, options) { 7225 Binder.fn.init.call(this, widget.element[0], bindings, options); 7226 7227 this.widget = widget; 7228 this._change = $.proxy(this.change, this); 7229 this.widget.first(CHANGE, this._change); 7230 7231 var value = this.bindings.value.get(); 7232 this._valueIsObservableObject = value == null || value instanceof ObservableObject; 7233 }, 7234 7235 change: function() { 7236 var value = this.widget.value(); 7237 var idx, length; 7238 7239 var field = this.options.dataValueField || this.options.dataTextField; 7240 7241 if (field) { 7242 var source, 7243 isObservableObject = this._valueIsObservableObject; 7244 7245 if (this.bindings.source) { 7246 source = this.bindings.source.get(); 7247 } 7248 7249 if (value === "" && isObservableObject) { 7250 value = null; 7251 } else { 7252 if (!source || source instanceof kendo.data.DataSource) { 7253 source = this.widget.dataSource.view(); 7254 } 7255 7256 for (idx = 0, length = source.length; idx < length; idx++) { 7257 if (source[idx].get(field) == value) { 7258 if (isObservableObject) { 7259 value = source[idx]; 7260 } else { 7261 value = source[idx].get(field); 7262 } 7263 break; 7264 } 7265 } 7266 } 7267 } 7268 7269 this.bindings.value.set(value); 7270 }, 7271 7272 refresh: function() { 7273 var field = this.options.dataValueField || this.options.dataTextField; 7274 var value = this.bindings.value.get(); 7275 7276 if (field && value instanceof ObservableObject) { 7277 value = value.get(field); 7278 } 7279 7280 this.widget.value(value); 7281 }, 7282 7283 destroy: function() { 7284 this.widget.unbind(CHANGE, this._change); 7285 } 7286 }) 7287 }; 7288 7289 var BindingTarget = Class.extend( { 7290 init: function(target, options) { 7291 this.target = target; 7292 this.options = options; 7293 this.toDestroy = []; 7294 }, 7295 7296 bind: function(bindings) { 7297 var nodeName = this.target.nodeName.toLowerCase(), 7298 key, 7299 specificBinders = binders[nodeName] || {}; 7300 7301 for (key in bindings) { 7302 this.applyBinding(key, bindings, specificBinders); 7303 } 7304 }, 7305 7306 applyBinding: function(name, bindings, specificBinders) { 7307 var binder = specificBinders[name] || binders[name], 7308 toDestroy = this.toDestroy, 7309 attribute, 7310 binding = bindings[name]; 7311 7312 if (binder) { 7313 binder = new binder(this.target, bindings, this.options); 7314 7315 toDestroy.push(binder); 7316 7317 if (binding instanceof Binding) { 7318 binder.bind(binding); 7319 toDestroy.push(binding); 7320 } else { 7321 for (attribute in binding) { 7322 binder.bind(binding, attribute); 7323 toDestroy.push(binding[attribute]); 7324 } 7325 } 7326 } else if (name !== "template") { 7327 throw new Error("The " + name + " binding is not supported by the " + this.target.nodeName.toLowerCase() + " element"); 7328 } 7329 }, 7330 7331 destroy: function() { 7332 var idx, 7333 length, 7334 toDestroy = this.toDestroy; 7335 7336 for (idx = 0, length = toDestroy.length; idx < length; idx++) { 7337 toDestroy[idx].destroy(); 7338 } 7339 } 7340 });
7341 7342 var WidgetBindingTarget = BindingTarget.extend( { 7343 bind: function(bindings) { 7344 var that = this, 7345 binding, 7346 hasValue = false, 7347 hasSource = false; 7348 7349 for (binding in bindings) { 7350 if (binding == VALUE) { 7351 hasValue = true; 7352 } else if (binding == "source") { 7353 hasSource = true; 7354 } else { 7355 that.applyBinding(binding, bindings); 7356 } 7357 } 7358 7359 if (hasSource) { 7360 that.applyBinding("source", bindings); 7361 } 7362 7363 if (hasValue) { 7364 that.applyBinding(VALUE, bindings); 7365 } 7366 }, 7367 7368 applyBinding: function(name, bindings) { 7369 var binder = binders.widget[name], 7370 toDestroy = this.toDestroy, 7371 attribute, 7372 binding = bindings[name]; 7373 7374 if (binder) { 7375 binder = new binder(this.target, bindings, this.target.options); 7376 7377 toDestroy.push(binder); 7378 7379 7380 if (binding instanceof Binding) { 7381 binder.bind(binding); 7382 toDestroy.push(binding); 7383 } else { 7384 for (attribute in binding) { 7385 binder.bind(binding, attribute); 7386 toDestroy.push(binding[attribute]); 7387 } 7388 } 7389 } else { 7390 throw new Error("The " + name + " binding is not supported by the " + this.target.options.name + " widget"); 7391 } 7392 } 7393 }); 7394 7395 function flattenGroups(data) { 7396 var idx, length, result = []; 7397 7398 for (idx = 0, length = data.length; idx < length; idx++) { 7399 if (data[idx].hasSubgroups) { 7400 result = result.concat(flattenGroups(data[idx].items)); 7401 } else { 7402 result = result.concat(data[idx].items); 7403 } 7404 } 7405 return result; 7406 } 7407 7408 function bindingTargetForRole(role, element, namespace) { 7409 var type = namespace.roles[role]; 7410 7411 if (type) { 7412 return new WidgetBindingTarget(kendo.initWidget(element, type.options, namespace)); 7413 } 7414 } 7415 7416 var keyValueRegExp = /[A-Za-z0-9_\-]+:(\{([^}]*)\}|[^,}]+)/g, 7417 whiteSpaceRegExp = /\s/g; 7418 7419 function parseBindings(bind) { 7420 var result = {}, 7421 idx, 7422 length, 7423 token, 7424 colonIndex, 7425 key, 7426 value, 7427 tokens; 7428 7429 tokens = bind.match(keyValueRegExp); 7430 7431 for (idx = 0, length = tokens.length; idx < length; idx++) { 7432 token = tokens[idx]; 7433 colonIndex = token.indexOf(":"); 7434 7435 key = token.substring(0, colonIndex); 7436 value = token.substring(colonIndex + 1); 7437 7438 if (value.charAt(0) == "{") { 7439 value = parseBindings(value); 7440 } 7441 7442 result[key] = value; 7443 } 7444 7445 return result; 7446 } 7447 7448 function createBindings(bindings, source, type) { 7449 var binding, 7450 result = {}; 7451 7452 for (binding in bindings) { 7453 result[binding] = new type(source, bindings[binding]); 7454 } 7455 7456 return result; 7457 } 7458 7459 function bindElement(element, source, namespace) { 7460 var role = element.getAttribute("data-" + kendo.ns + "role"), 7461 idx, 7462 bind = element.getAttribute("data-" + kendo.ns + "bind"), 7463 children = element.children, 7464 deep = true, 7465 bindings, 7466 options = {}, 7467 target; 7468 7469 if (!namespace) { 7470 namespace = kendo.ui; 7471 } 7472 7473 if (role || bind) { 7474 unbindElement(element); 7475 } 7476 7477 if (role) { 7478 target = bindingTargetForRole(role, element, namespace); 7479 } 7480 7481 if (bind) { 7482 bind = parseBindings(bind.replace(whiteSpaceRegExp, "")); 7483 7484 if (!target) { 7485 options = kendo.parseOptions(element, { textField: "", valueField: "", template: "", valueUpdate: CHANGE}); 7486 target = new BindingTarget(element, options); 7487 } 7488 7489 target.source = source; 7490 7491 bindings = createBindings(bind, source, Binding); 7492 7493 if (options.template) { 7494 bindings.template = new TemplateBinding(source, "", options.template); 7495 } 7496 7497 if (bindings.click) { 7498 bind.events = bind.events || {}; 7499 bind.events.click = bind.click; 7500 delete bindings.click; 7501 } 7502 7503 if (bindings.source) { 7504 deep = false; 7505 } 7506 7507 if (bind.attr) { 7508 bindings.attr = createBindings(bind.attr, source, Binding); 7509 } 7510 7511 if (bind.style) { 7512 bindings.style = createBindings(bind.style, source, Binding); 7513 } 7514 7515 if (bind.events) { 7516 bindings.events = createBindings(bind.events, source, EventBinding); 7517 } 7518 7519 target.bind(bindings); 7520 } 7521 7522 if (target) { 7523 element.kendoBindingTarget = target; 7524 } 7525 7526 if (deep && children) { 7527 for (idx = 0; idx < children.length; idx++) { 7528 bindElement(children[idx], source, namespace); 7529 } 7530 } 7531 } 7532 7533 function bind(dom, object, namespace) { 7534 var idx, length; 7535
7536 object = kendo.observable(object); 7537 dom = $(dom); 7538 7539 for (idx = 0, length = dom.length; idx < length; idx++ ) { 7540 bindElement(dom[idx], object, namespace); 7541 } 7542 } 7543 7544 function unbindElement(element) { 7545 var bindingTarget = element.kendoBindingTarget; 7546 7547 if (bindingTarget) { 7548 bindingTarget.destroy(); 7549 7550 if ($.support.deleteExpando) { 7551 delete element.kendoBindingTarget; 7552 } else if (element.removeAttribute) { 7553 element.removeAttribute("kendoBindingTarget"); 7554 } else { 7555 element.kendoBindingTarget = null; 7556 } 7557 } 7558 } 7559 7560 function unbindElementTree(element) { 7561 var idx, 7562 length, 7563 children = element.children; 7564 7565 unbindElement(element); 7566 7567 if (children) { 7568 for (idx = 0, length = children.length; idx < length; idx++) { 7569 unbindElementTree(children[idx]); 7570 } 7571 } 7572 } 7573 7574 function unbind(dom) { 7575 var idx, length; 7576 7577 dom = $(dom); 7578 7579 for (idx = 0, length = dom.length; idx < length; idx++ ) { 7580 unbindElementTree(dom[idx]); 7581 } 7582 } 7583 7584 function notify(widget, namespace) { 7585 var element = widget.element, 7586 bindingTarget = element[0].kendoBindingTarget; 7587 7588 if (bindingTarget) { 7589 bind(element, bindingTarget.source, namespace); 7590 } 7591 } 7592 7593 kendo.unbind = unbind; 7594 kendo.bind = bind; 7595 kendo.data.binders = binders; 7596 kendo.data.Binder = Binder; 7597 kendo.notify = notify; 7598 7599 kendo.observable = function(object) { 7600 if (!(object instanceof ObservableObject)) { 7601 object = new ObservableObject(object); 7602 } 7603 7604 return object; 7605 }; 7606 7607})(jQuery); 7608;(function($, undefined) { 7609 var kendo = window.kendo, 7610 Widget = kendo.ui.Widget, 7611 INVALIDMSG = "k-invalid-msg", 7612 INVALIDINPUT = "k-invalid", 7613 emailRegExp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i, 7614 urlRegExp = /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i, 7615 INPUTSELECTOR = ":input:not(:button,[type=submit],[type=reset])", 7616 NUMBERINPUTSELECTOR = "[type=number],[type=range]", 7617 BLUR = "blur", 7618 NAME = "name", 7619 FORM = "form", 7620 NOVALIDATE = "novalidate", 7621 proxy = $.proxy, 7622 patternMatcher = function(value, pattern) { 7623 if (typeof pattern === "string") { 7624 pattern = new RegExp('^(?:' + pattern + ')$'); 7625 } 7626 return pattern.test(value); 7627 }, 7628 matcher = function(input, selector, pattern) { 7629 var value = input.val(); 7630 7631 if (input.filter(selector).length && value !== "") { 7632 return patternMatcher(value, pattern); 7633 } 7634 return true; 7635 }, 7636 hasAttribute = function(input, name) { 7637 if (input.length) { 7638 return input[0].attributes[name] !== undefined; 7639 } 7640 return false; 7641 }, 7642 nameSpecialCharRegExp = /(\[|\]|\$|\.|\:|\+)/g; 7643 7644 /** 7645 * @name kendo.ui.Validator.Description 7646 * 7647 * @section 7648 * <p> 7649 * Validator offers an easy way to do client-side form validation. 7650 * Built around the HTML5 form validation attributes it supports variety of built-in validation rules, but also provides a convenient way for setting custom rules handling. 7651 * </p> 7652 * @exampleTitle <b>Validator</b> initialization to validate input elements inside a container 7653 * @example 7654 * <div id="myform"> 7655 * <input type="text" name="firstName" required /> 7656 * <input type="text" name="lastName" required /> 7657 * <button id="save" type="button">Save</button> 7658 * </div> 7659 * 7660 * <script> 7661 * $(document).ready(function(){
7662 * var validatable = $("#myform").kendoValidator().data("kendoValidator"); 7663 * $("#save").click(function() { 7664 * if (validatable.validate()) { 7665 * save(); 7666 * } 7667 * }); 7668 * }); 7669 * </script> 7670 * @section <h4>Validation Rules</h4> 7671 * 7672 * @exampleTitle <strong>required</strong>- element should have a value 7673 * @example 7674 * <input type="text" name="firstName" required /> 7675 * 7676 * @exampleTitle <strong>pattern</strong>- constrains the value to match a specific regular expression 7677 * @example 7678 * <input type="text" name="twitter" pattern="https?://(?:www\.)?twitter\.com/.+i" /> 7679 * 7680 * @exampleTitle <strong>max/min</strong>- constrain the minimum and/or maximum numeric values that can be entered 7681 * @example 7682 * <input type="number" name="age" min="1" max="42" /> 7683 * 7684 * @exampleTitle <strong>step</strong>- when used in combination with the min and max attributes, constrains the granularity of values that can be entered 7685 * @example 7686 * <input type="number" name="age" min="1" max="100" step="2" /> 7687 * 7688 * @exampleTitle <strong>url</strong>- constrain the value to being a valid URL 7689 * @example 7690 * <input type="url" name="url" /> 7691 * 7692 * @exampleTitle <strong>email</strong>- constrain the value to being a valid email 7693 * @example 7694 * <input type="email" name="email" /> 7695 * 7696 * @section 7697 * <p>Beside the built-in validation rules, KendoUI Validator also provides a convenient way for setting custom rules through its rules configuration option. </p> 7698 * 7699 * @exampleTitle 7700 * @example 7701 * $("#myform").kendoValidator({ 7702 * rules: { 7703 * custom: function(input) { 7704 * // Only Tom will be a valid value for FirstName input 7705 * return input.is("[name=firstname]") && input.val() === "Tom"; 7706 * } 7707 * } 7708 * }); 7709 * 7710 * @section <h4>Validation Messages</h4> 7711 * <p>There are several ways to control the messages which appears if validation fails:</p> 7712 * 7713 * @exampleTitle Set the validation messages for all input elements, through configuration options 7714 * @example 7715 * $("#myform").kendoValidator({ 7716 * rules: { 7717 * custom: function(input) { 7718 * //... 7719 * } 7720 * }, 7721 * messages: { 7722 * // defines message for the 'custom' validation rule 7723 * custom: "Please enter valid value for my custom rule", 7724 * // overrides the built-in message for required rule 7725 * required: "My custom required message", 7726 * // overrides the built-in email rule message with a custom function which return the actual message 7727 * email: function(input) { 7728 * return getMessage(input); 7729 * } 7730 * } 7731 * }); 7732 * @exampleTitle Use the title and validationMessage attributes to set per input element messages 7733 * @example 7734 * <input type="tel" pattern="\d{10}" validationMessage="Plase enter a ten digit phone number" /> 7735 * 7736 * @section <h4>Triggering validation</h4> 7737 * <p>In order to trigger the element(s) validation, <strong>validate</strong> method should be used. It will return either <em>true</em> if validation succeeded or <em>false</em> in case of a failure. </p> 7738 * <p> 7739 * Note that if a HTML form element is set as validation container, the form submits will be automatically prevented if validation fails. 7740 * </p> 7741 * @section <h4>Initialize Kendo Validator with specific tooltip position</h4> 7742 * 7743 * <p> 7744 * Ideally Kendo Validator places its tooltips besides the validated input. However, if the input is later enhanced to a ComboBox, AutoComplete or other Kendo Widget, placing the 7745 * tooltip beside the input may cover important information or break the widget rendering. In this case, you can specify where exactly do you want the tooltip to be placed by 7746 * adding a span with data-for attribute set to the validated input name and a class .k-invalid-msg. Check the example below: 7747 * </p> 7748 * 7749 * @exampleTitle <b>Validator</b> initialization with specific tooltip placement (the tooltip will remain outside of the AutoComplete widget after enhancement) 7750 * @example 7751 * <div id="myform"> 7752 * <input type="text" id="name" name="name" required />
7753 * <span class="k-invalid-msg" data-for="name"></span> 7754 * </div> 7755 * 7756 * <script> 7757 * $("#name").kendoAutoComplete({ 7758 * dataSource: data, 7759 * separator: ", " 7760 * }); 7761 * 7762 * $("#myform").kendoValidator(); 7763 * </script> 7764 */ 7765 var Validator = Widget.extend(/** @lends kendo.ui.Validator.prototype */{ /** 7766 * @constructs 7767 * @extends kendo.ui.Widget 7768 * @param {DomElement} element DOM element 7769 * @param {Object} options Configuration options. 7770 * @option {Object} [rules] Set of validation rules. Those rules will extend the built-in ones. 7771 * _example 7772 * $("#myform").kendoValidator({ 7773 * rules: { 7774 * custom: function(input) { 7775 * return input.is("[name=firstname]") && input.val() === "Tom"; // Only Tom will be a valid value for FirstName input 7776 * } 7777 * } 7778 * }); 7779 * @option {Object} [messages] Set of messages (either strings or functions) which will be shown when given validation rule fails. 7780 * By setting already existing key the appropriate built-in message will be overridden. 7781 * _example 7782 * $("#myform").kendoValidator({ 7783 * rules: { 7784 * custom: function(input) { 7785 * //... 7786 * } 7787 * }, 7788 * messages: { 7789 * custom: "Please enter valid value for my custom rule",// defines message for the 'custom' validation rule 7790 * required: "My custom required message", // overrides the built-in message for required rule 7791 * email: function(input) { // overrides the built-in email rule message with a custom function which return the actual message 7792 * return getMessage(input); 7793 * } 7794 * } 7795 * }); 7796 * @option {Boolean} [validateOnBlur] Determines if validation will be triggered when element loses focus. Default value is true. 7797 */ 7798 init: function(element, options) { 7799 var that = this; 7800 7801 Widget.fn.init.call(that, element, options); 7802 7803 that._errorTemplate = kendo.template(that.options.errorTemplate); 7804 7805 if (that.element.is(FORM)) { 7806 that.element.attr(NOVALIDATE, NOVALIDATE); 7807 } 7808 7809 that._errors = {}; 7810 that._attachEvents(); 7811 }, 7812 7813 options: { 7814 name: "Validator", 7815 errorTemplate: '<span class="k-widget k-tooltip k-tooltip-validation">' + 7816 '<span class="k-icon k-warning"> </span> ${message}</span>', 7817 messages: { 7818 required: "{0} is required", 7819 pattern: "{0} is not valid", 7820 min: "{0} should be greater than or equal to {1}", 7821 max: "{0} should be smaller than or equal to {1}", 7822 step: "{0} is not valid", 7823 email: "{0} is not valid email", 7824 url: "{0} is not valid URL", 7825 date: "{0} is not valid date" 7826 }, 7827 rules: { 7828 required: function(input) { 7829 var checkbox = input.filter("[type=checkbox]").length && input.attr("checked") !== "checked", 7830 value = input.val(); 7831 7832 return !(hasAttribute(input, "required") && (value === "" || !value || checkbox)); 7833 7834 }, 7835 pattern: function(input) { 7836 if (input.filter("[type=text],[type=email],[type=url],[type=tel],[type=search]").filter("[pattern]").length && input.val() !== "") { 7837 return patternMatcher(input.val(), input.attr("pattern")); 7838 } 7839 return true; 7840 }, 7841 min: function(input) { 7842 if (input.filter(NUMBERINPUTSELECTOR + ",[" + kendo.attr("type") + "=number]").filter("[min]").length && input.val() !== "") { 7843 var min = parseFloat(input.attr("min")) || 0, 7844 val = parseFloat(input.val()); 7845 7846 return min <= val; 7847 } 7848 return true; 7849 }, 7850 max: function(input) { 7851 if (input.filter(NUMBERINPUTSELECTOR + ",[" + kendo.attr("type") + "=number]").filter("[max]").length && input.val() !== "") { 7852 var max = parseFloat(input.attr("max")) || 0, 7853 val = parseFloat(input.val()); 7854 7855 return max >= val; 7856 } 7857 return true; 7858 }, 7859 step: function(input) { 7860 if (input.filter(NUMBERINPUTSELECTOR + ",[" + kendo.attr("type") + "=number]").filter("[step]").length && input.val() !== "") { 7861 var min = parseFloat(input.attr("min")) || 0, 7862 step = parseFloat(input.attr("step")) || 0, 7863 val = parseFloat(input.val()); 7864 7865 return (((val-min)*10)%(step*10)) / 100 === 0; 7866 } 7867 return true; 7868 }, 7869 email: function(input) { 7870 return matcher(input, "[type=email],[" + kendo.attr("type") + "=email]", emailRegExp); 7871 }, 7872 url: function(input) { 7873 return matcher(input, "[type=url],[" + kendo.attr("type") + "=url]", urlRegExp); 7874 }, 7875 date: function(input) { 7876 if (input.filter("[type^=date],[" + kendo.attr("type") + "=date]").length && input.val() !== "") { 7877 return kendo.parseDate(input.val(), input.attr(kendo.attr("format"))) !== null; 7878 } 7879 return true; 7880 } 7881 }, 7882 validateOnBlur: true 7883 }, 7884 7885 _submit: function(e) { 7886 if (!this.validate()) { 7887 e.stopPropagation(); 7888 e.stopImmediatePropagation(); 7889 e.preventDefault(); 7890 return false; 7891 } 7892 return true; 7893 }, 7894 7895 _attachEvents: function() { 7896 var that = this; 7897 7898 if (that.element.is(FORM)) { 7899 that.element.submit(proxy(that._submit, that)); 7900 } 7901 7902 if (that.options.validateOnBlur) { 7903 if (!that.element.is(INPUTSELECTOR)) { 7904 that.element.delegate(INPUTSELECTOR, BLUR, function() { 7905 that.validateInput($(this)); 7906 }); 7907 } else { 7908 that.element.bind(BLUR, function() { 7909 that.validateInput(that.element); 7910 }); 7911 } 7912 } 7913 }, 7914 7915 /**
7916 * Validates the input element(s) against the declared validation rules. 7917 * @returns {Boolean} If all rules are passed successfully. 7918 * @example 7919 * // get a reference to the validatable form 7920 * var validatable = $("#myform").kendoValidator().data("kendoValidator"); 7921 * // check validation on save button click 7922 * $("#save").click(function() { 7923 * if (validatable.validate()) { 7924 * save(); 7925 * } 7926 * }); 7927 */ 7928 validate: function() { 7929 var that = this, 7930 inputs, 7931 idx, 7932 invalid = false, 7933 length; 7934 7935 that._errors = {}; 7936 7937 if (!that.element.is(INPUTSELECTOR)) { 7938 inputs = that.element.find(INPUTSELECTOR); 7939 7940 for (idx = 0, length = inputs.length; idx < length; idx++) { 7941 if (!that.validateInput(inputs.eq(idx))) { 7942 invalid = true; 7943 } 7944 } 7945 return !invalid; 7946 } 7947 return that.validateInput(that.element); 7948 }, 7949 7950 /** 7951 * Validates the input element against the declared validation rules. 7952 * @param {DomElement} input Input element to be validated. 7953 * @returns {Boolean} If all rules are passed successfully. 7954 */ 7955 validateInput: function(input) { 7956 input = $(input); 7957 7958 var that = this, 7959 template = that._errorTemplate, 7960 result = that._checkValidity(input), 7961 valid = result.valid, 7962 className = "." + INVALIDMSG, 7963 fieldName = (input.attr(NAME) || ""), 7964 DATAFOR = kendo.attr("for"), 7965 lbl = that.element.find(className + "[" + DATAFOR +"=" + fieldName.replace(nameSpecialCharRegExp, "\\$1") + "]").add(input.next(className)).hide(), 7966 messageText; 7967 7968 if (!valid) { 7969 messageText = that._extractMessage(input, result.key); 7970 that._errors[fieldName] = messageText; 7971 var messageLabel = $(template({ message: messageText })).addClass(INVALIDMSG).attr(DATAFOR, fieldName || ""); 7972 7973 if (!lbl.replaceWith(messageLabel).length) { 7974 messageLabel.insertAfter(input); 7975 } 7976 messageLabel.show(); 7977 } 7978 7979 input.toggleClass(INVALIDINPUT, !valid); 7980 7981 return valid; 7982 }, 7983 7984 _extractMessage: function(input, ruleKey) { 7985 var that = this, 7986 customMessage = that.options.messages[ruleKey], 7987 fieldName = input.attr(NAME); 7988 7989 customMessage = $.isFunction(customMessage) ? customMessage(input) : customMessage; 7990 7991 return kendo.format(input.attr(kendo.attr(ruleKey + "-msg")) || input.attr("validationMessage") || input.attr("title") || customMessage || "", fieldName, input.attr(ruleKey)); 7992 }, 7993 7994 _checkValidity: function(input) { 7995 var rules = this.options.rules, 7996 rule; 7997 7998 for (rule in rules) { 7999 if (!rules[rule](input)) { 8000 return { valid: false, key: rule }; 8001 } 8002 } 8003 8004 return { valid: true }; 8005 }, 8006 8007 /** 8008 * Get the error messages if any. 8009 * @returns {Array} Messages for the failed validation rules. 8010 * @example 8011 * // get a reference to the validatable form 8012 * var validatable = $("#myform").kendoValidator().data("kendoValidator"); 8013 * $("#save").click(function() { 8014 * if (validatable.validate() === false) { 8015 * // get the errors and write them out to the "errors" html container 8016 * var errors = validatable.errors(); 8017 * $(errors).each(function() { 8018 * $("#errors").html(this); 8019 * }); 8020 * } 8021 * }); 8022 */ 8023 errors: function() { 8024 var results = [], 8025 errors = this._errors, 8026 error; 8027 8028 for (error in errors) { 8029 results.push(errors[error]);
8030 } 8031 return results; 8032 } 8033 }); 8034 8035 kendo.ui.plugin(Validator); 8036})(jQuery); 8037(function ($, undefined) { 8038 var kendo = window.kendo, 8039 support = kendo.support, 8040 pointers = support.pointers, 8041 document = window.document, 8042 SURFACE = $(document.documentElement), 8043 Class = kendo.Class, 8044 Widget = kendo.ui.Widget, 8045 Observable = kendo.Observable, 8046 proxy = $.proxy, 8047 extend = $.extend, 8048 getOffset = kendo.getOffset, 8049 draggables = {}, 8050 dropTargets = {}, 8051 lastDropTarget, 8052 RESIZE_EVENT = "resize", 8053 invalidZeroEvents = support.mobileOS && support.mobileOS.android, 8054 START_EVENTS = "mousedown", 8055 MOVE_EVENTS = "mousemove", 8056 END_EVENTS = "mouseup mouseleave", 8057 KEYUP = "keyup", 8058 CHANGE = "change", 8059 8060 // Draggable events 8061 DRAGSTART = "dragstart", 8062 DRAG = "drag", 8063 DRAGEND = "dragend", 8064 DRAGCANCEL = "dragcancel", 8065 8066 // DropTarget events 8067 DRAGENTER = "dragenter", 8068 DRAGLEAVE = "dragleave", 8069 DROP = "drop", 8070 8071 // Drag events 8072 START = "start", 8073 MOVE = "move", 8074 END = "end", 8075 CANCEL = "cancel", 8076 TAP = "tap"; 8077 8078 if (support.touch) { 8079 RESIZE_EVENT = "orientationchange"; 8080 START_EVENTS = "touchstart"; 8081 MOVE_EVENTS = "touchmove"; 8082 END_EVENTS = "touchend touchcancel"; 8083 } 8084 8085 if(pointers) { 8086 RESIZE_EVENT = "orientationchange resize"; 8087 START_EVENTS = "MSPointerDown"; 8088 MOVE_EVENTS = "MSPointerMove"; 8089 END_EVENTS = "MSPointerUp MSPointerCancel"; 8090 } 8091 8092 function contains(parent, child) { 8093 try { 8094 return $.contains(parent, child) || parent == child; 8095 } catch (e) { 8096 return false; 8097 } 8098 } 8099 8100 function elementUnderCursor(e) { 8101 return document.elementFromPoint(e.x.client, e.y.client); 8102 } 8103 8104 function addNS(events, ns) { 8105 return events.replace(/ /g, ns + " "); 8106 } 8107 8108 function preventTrigger(e) { 8109 e.preventDefault(); 8110 $(document.body).trigger(e.type); 8111 } 8112 8113 /** 8114 * @name kendo.DragAxis.Description 8115 * 8116 * @section <h4>DragAxis</h4> 8117 * The DragAxis is used internally by the kendo.Drag component to store and calculate event data. 8118 * The Drag component contains two DragAxis instances: <code>x</code> for the horizontal coordinates, and <code>y</code> for the vertical. 8119 * The two DragAxis instances are available in each Drag event parameter. 8120 * @exampleTitle Access DragAxis information in Drag start event 8121 * @example 8122 * new kendo.Drag($("#foo"), { 8123 * start: function(e) { 8124 * console.log(x); // Horizontal axis 8125 * console.log(y); // Vertical axis 8126 * } 8127 * }); 8128 * 8129 * @section Each axis instance contains the following fields: 8130 * <ul> 8131 * <li><b>location</b> - the offset of the mouse/touch relative to the entire document (pageX/Y);</li> 8132 * <li><b>startLocation</b> - the offset of the mouse/touch relative to the document when the drag started;</li> 8133 * <li><b>client</b> - the offset of the mouse/touch relative to the viewport (clientX/Y);</li> 8134 * <li><b>delta</b> - the change from the previous event location</li> 8135 * <li><b>velocity</b> - the pixels per millisecond speed of the current move.</li> 8136 * </ul> 8137 */ 8138 var DragAxis = Class.extend(/** @lends kendo.DragAxis.prototype */{ 8139 /** 8140 * @constructs 8141 */ 8142 init: function(axis) { 8143 this.axis = axis; 8144 }, 8145 8146 start: function(location, timeStamp) { 8147 var that = this, 8148 offset = location["page" + that.axis]; 8149 8150 that.startLocation = that.location = offset; 8151 that.client = location["client" + that.axis]; 8152 that.velocity = that.delta = 0; 8153 that.timeStamp = timeStamp; 8154 }, 8155 8156 move: function(location, timeStamp) { 8157 var that = this, 8158 offset = location["page" + that.axis]; 8159 8160 if (!offset && invalidZeroEvents) { 8161 return; 8162 } 8163 8164 that.delta = offset - that.location; 8165 that.location = offset; 8166 that.client = location["client" + that.axis]; 8167 that.initialDelta = offset - that.startLocation; 8168 that.velocity = that.delta / (timeStamp - that.timeStamp); 8169 that.timeStamp = timeStamp; 8170 } 8171 });
8172 8173 /** 8174 * @name kendo.Drag.Description 8175 * @section <h4>Drag</h4> The kendo Drag component provides a cross-browser, touch-friendly way to handle mouse and touch drag events. 8176 * @exampleTitle <b>Drag</b> initialization 8177 * @example 8178 * var drag = new kendo.Drag($("#draggable")); 8179 */ 8180 var Drag = Observable.extend(/** @lends kendo.Drag.prototype */{ 8181 /** 8182 * @constructs 8183 * @extends kendo.Observable 8184 * @param {DomElement} element the DOM element from which the drag event starts. 8185 * @param {Object} options Configuration options. 8186 * @option {Integer} [threshold] <0> The minimum distance the mouse/touch should move before the event is triggered. 8187 * @option {Boolean} [global] <false> If set to true, the drag event will be tracked beyond the element boundaries. 8188 * @option {Boolean} [allowSelection] <false> If set to true, the mousedown and selectstart events will not be prevented. 8189 * @option {Boolean} [stopPropagation] <false> If set to true, the mousedown event propagation will stopped, disabling 8190 * drag capturing at parent elements. 8191 * If set to false, dragging outside of the element boundaries will trigger the <code>end</code> event. 8192 * @option {Selector} [filter] If passed, the filter limits the child elements that will trigger the event sequence. 8193 */ 8194 init: function(element, options) { 8195 var that = this, 8196 eventMap = {}, 8197 filter, 8198 preventIfMoving, 8199 ns = "." + kendo.guid(); 8200 8201 options = options || {}; 8202 filter = that.filter = options.filter; 8203 that.threshold = options.threshold || 0; 8204 8205 element = $(element); 8206 Observable.fn.init.call(that); 8207 8208 eventMap = {}; 8209 8210 eventMap[addNS(MOVE_EVENTS, ns)] = proxy(that._move, that); 8211 eventMap[addNS(END_EVENTS, ns)] = proxy(that._end, that); 8212 8213 extend(that, { 8214 x: new DragAxis("X"), 8215 y: new DragAxis("Y"), 8216 element: element, 8217 surface: options.global ? SURFACE : element, 8218 stopPropagation: options.stopPropagation, 8219 pressed: false, 8220 eventMap: eventMap, 8221 ns: ns 8222 }); 8223 8224 element 8225 .on(START_EVENTS, filter, proxy(that._start, that)) 8226 .on("dragstart", filter, kendo.preventDefault); 8227 8228 if (!options.allowSelection) { 8229 var args = ["mousedown selectstart", filter, preventTrigger]; 8230 8231 if (filter instanceof $) { 8232 args.splice(2, 0, null); 8233 } 8234 8235 element.on.apply(element, args); 8236 } 8237 8238 if (support.eventCapture) { 8239 preventIfMoving = function(e) { 8240 if (that.moved) { 8241 e.preventDefault(); 8242 } 8243 }; 8244 8245 that.surface[0].addEventListener(support.mouseup, preventIfMoving, true); 8246 } 8247 8248 that.bind([ 8249 /** 8250 * Fires when the user presses and releases the element without any movement or with a movement below the <code>threshold</code> specified. 8251 * @name kendo.Drag#tap 8252 * @event 8253 * @param {Event} e 8254 * @param {DragAxis} e.x Reference to the horizontal drag axis instance. 8255 * @param {DragAxis} e.y Reference to the vertical drag axis instance. 8256 * @param {jQueryEvent} e.event Reference to the jQuery event object. 8257 * @param {Element} e.target Reference to the DOM element from which the Drag started. 8258 * It is different from the element only if <code>filter</code> option is specified. 8259 */ 8260 TAP, 8261 /** 8262 * Fires when the user starts dragging the element. 8263 * @name kendo.Drag#start 8264 * @event 8265 * @param {Event} e 8266 * @param {DragAxis} e.x Reference to the horizontal drag axis instance. 8267 * @param {DragAxis} e.y Reference to the vertical drag axis instance. 8268 * @param {jQueryEvent} e.event Reference to the jQuery event object. 8269 * @param {Element} e.target Reference to the DOM element from which the Drag started. 8270 * It is different from the element only if <code>filter</code> option is specified. 8271 */ 8272 START, 8273 /** 8274 * Fires while dragging. 8275 * @name kendo.Drag#move 8276 * @event 8277 * @param {Event} e 8278 * @param {DragAxis} e.x Reference to the horizontal drag axis instance. 8279 * @param {DragAxis} e.y Reference to the vertical drag axis instance. 8280 * @param {jQueryEvent} e.event Reference to the jQuery event object. 8281 * @param {Element} e.target Reference to the DOM element from which the Drag started. 8282 * It is different from the element only if <code>filter</code> option is specified. 8283 */ 8284 MOVE, 8285 /** 8286 * Fires when the drag ends. 8287 * @name kendo.Drag#end 8288 * @event 8289 * @param {Event} e 8290 * @param {DragAxis} e.x Reference to the horizontal drag axis instance. 8291 * @param {DragAxis} e.y Reference to the vertical drag axis instance. 8292 * @param {jQueryEvent} e.event Reference to the jQuery event object. 8293 * @param {Element} e.target Reference to the DOM element from which the Drag started. 8294 * It is different from the element only if <code>filter</code> option is specified. 8295 */ 8296 END, 8297 /** 8298 * Fires when the drag is canceled. This when the <code>cancel</code> method is called. 8299 * @name kendo.Drag#cancel 8300 * @event 8301 * @param {Event} e 8302 * @param {DragAxis} e.x Reference to the horizontal drag axis instance. 8303 * @param {DragAxis} e.y Reference to the vertical drag axis instance. 8304 * @param {jQueryEvent} e.event Reference to the jQuery event object. 8305 * @param {Element} e.target Reference to the DOM element from which the Drag started. 8306 * It is different from the element only if <code>filter</code> option is specified. 8307 */ 8308 CANCEL], options); 8309 }, 8310 8311 /** 8312 * Capture the current drag, so that Drag listeners bound to parent elements will not trigger. 8313 * This method will not have any effect if the current drag instance is instantiated with the <code>global</code> option set to true. 8314 */ 8315 capture: function() { 8316 Drag.captured = true; 8317 }, 8318 8319 /** 8320 * Discard the current drag. Calling the <code>cancel</code> method will trigger the <code>cancel</code> event. 8321 * The correct moment to call this method would be in the <code>start</code> event handler. 8322 * @exampleTitle Cancel the drag event sequence 8323 * @example 8324 * new kendo.Drag($("#foo"), { 8325 * start: function(e) { 8326 * e.cancel(); 8327 * } 8328 * }); 8329 */ 8330 cancel: function() { 8331 this._cancel(); 8332 this.trigger(CANCEL); 8333 }, 8334 8335 skip: function() { 8336 this._cancel(); 8337 }, 8338 8339 _cancel: function() { 8340 var that = this; 8341 that.moved = that.pressed = false; 8342 that.surface.off(that.ns); 8343 }, 8344 8345 _start: function(e) { 8346 var that = this, 8347 filter = that.filter, 8348 originalEvent = e.originalEvent, 8349 touch, 8350 location = e; 8351 8352 if (that.pressed) { return; } 8353 8354 if (filter) { 8355 that.target = $(e.target).is(filter) ? $(e.target) : $(e.target).closest(filter); 8356 } else { 8357 that.target = that.element; 8358 } 8359 8360 if (!that.target.length) { 8361 return; 8362 } 8363 8364 if (that.stopPropagation) { 8365 e.stopPropagation(); 8366 } 8367 8368 that.pressed = true; 8369 that.moved = false; 8370 8371 if (support.touch) { 8372 touch = originalEvent.changedTouches[0]; 8373 that.touchID = touch.identifier; 8374 location = touch; 8375 } 8376 8377 if (pointers) { 8378 that.touchID = originalEvent.pointerId; 8379 location = originalEvent; 8380 } 8381 8382 that._perAxis(START, location, e.timeStamp); 8383 that.surface.off(that.eventMap).on(that.eventMap); 8384 Drag.captured = false; 8385 }, 8386 8387 _move: function(e) { 8388 var that = this, 8389 xDelta, 8390 yDelta, 8391 delta; 8392 8393 if (!that.pressed) { return; } 8394 8395 that._withEvent(e, function(location) { 8396 8397 that._perAxis(MOVE, location, e.timeStamp); 8398 8399 if (!that.moved) { 8400 xDelta = that.x.initialDelta; 8401 yDelta = that.y.initialDelta; 8402 8403 delta = Math.sqrt(xDelta * xDelta + yDelta * yDelta); 8404 8405 if (delta <= that.threshold) { 8406 return; 8407 } 8408 8409 if (!Drag.captured) { 8410 that._trigger(START, e); 8411 that.moved = true; 8412 } else { 8413 return that._cancel(); 8414 } 8415 } 8416 8417 // Event handlers may cancel the swipe in the START event handler, hence the double check for pressed. 8418 if (that.pressed) { 8419 that._trigger(MOVE, e); 8420 } 8421 }); 8422 }, 8423 8424 _end: function(e) { 8425 var that = this; 8426 8427 if (!that.pressed) { return; } 8428 8429 that._withEvent(e, function() { 8430 if (that.moved) { 8431 that._trigger(END, e); 8432 that.moved = false; 8433 } else { 8434 that._trigger(TAP, e); 8435 } 8436 8437 that._cancel(); 8438 }); 8439 }, 8440 8441 _perAxis: function(method, location, timeStamp) { 8442 this.x[method](location, timeStamp); 8443 this.y[method](location, timeStamp); 8444 }, 8445 8446 _trigger: function(name, e) { 8447 var data = { 8448 x: this.x, 8449 y: this.y, 8450 target: this.target, 8451 event: e 8452 }; 8453 8454 if(this.trigger(name, data)) { 8455 e.preventDefault(); 8456 } 8457 }, 8458
8459 _withEvent: function(e, callback) { 8460 var that = this, 8461 touchID = that.touchID, 8462 originalEvent = e.originalEvent, 8463 touches, 8464 idx; 8465 8466 if (support.touch) { 8467 touches = originalEvent.changedTouches; 8468 idx = touches.length; 8469 8470 while (idx) { 8471 idx --; 8472 if (touches[idx].identifier === touchID) { 8473 return callback(touches[idx]); 8474 } 8475 } 8476 } 8477 else if (pointers) { 8478 if (touchID === originalEvent.pointerId) { 8479 return callback(originalEvent); 8480 } 8481 } else { 8482 return callback(e); 8483 } 8484 } 8485 }); 8486 8487 var Tap = Observable.extend({ 8488 init: function(element, options) { 8489 var that = this, 8490 domElement = element[0]; 8491 8492 that.capture = false; 8493 domElement.addEventListener(START_EVENTS, proxy(that._press, that), true); 8494 $.each(END_EVENTS.split(" "), function() { 8495 domElement.addEventListener(this, proxy(that._release, that), true); 8496 }); 8497 8498 Observable.fn.init.call(that); 8499 8500 that.bind(["press", "release"], options || {}); 8501 }, 8502 8503 _press: function(e) { 8504 var that = this; 8505 that.trigger("press"); 8506 if (that.capture) { 8507 e.preventDefault(); 8508 } 8509 }, 8510 8511 _release: function(e) { 8512 var that = this; 8513 that.trigger("release"); 8514 8515 if (that.capture) { 8516 e.preventDefault(); 8517 that.cancelCapture(); 8518 } 8519 }, 8520 8521 captureNext: function() { 8522 this.capture = true; 8523 }, 8524 8525 cancelCapture: function() { 8526 this.capture = false; 8527 } 8528 }); 8529 8530 var PaneDimension = Observable.extend({ 8531 init: function(options) { 8532 var that = this; 8533 Observable.fn.init.call(that); 8534 8535 $.extend(that, options); 8536 8537 that.max = 0; 8538 8539 if (that.horizontal) { 8540 that.measure = "width"; 8541 that.scrollSize = "scrollWidth"; 8542 that.axis = "x"; 8543 } else { 8544 that.measure = "height"; 8545 that.scrollSize = "scrollHeight"; 8546 that.axis = "y"; 8547 } 8548 }, 8549 8550 outOfBounds: function(offset) { 8551 return offset > this.max || offset < this.min; 8552 }, 8553 8554 present: function() { 8555 return this.max - this.min; 8556 }, 8557 8558 update: function(silent) { 8559 var that = this; 8560 8561 that.size = that.container[that.measure](); 8562 that.total = that.element[0][that.scrollSize]; 8563 that.min = Math.min(that.max, that.size - that.total); 8564 if (!silent) { 8565 that.trigger(CHANGE, that); 8566 } 8567 } 8568 }); 8569 8570 var PaneDimensions = Observable.extend({ 8571 init: function(options) { 8572 var that = this, 8573 refresh = proxy(that.refresh, that), 8574 resizeHandler = refresh; 8575 8576 Observable.fn.init.call(that); 8577 8578 if (support.mobileOS.android) { 8579 resizeHandler = function() { setTimeout(refresh, 200); }; 8580 } 8581 8582 that.x = new PaneDimension(extend({horizontal: true}, options)); 8583 that.y = new PaneDimension(extend({horizontal: false}, options)); 8584 8585 that.bind(CHANGE, options); 8586 8587 $(window).bind(RESIZE_EVENT, resizeHandler); 8588 }, 8589 8590 refresh: function() { 8591 this.x.update(); 8592 this.y.update(); 8593 this.trigger(CHANGE); 8594 } 8595 }); 8596 8597 var PaneAxis = Observable.extend({ 8598 init: function(options) { 8599 var that = this; 8600 extend(that, options); 8601 Observable.fn.init.call(that); 8602 }, 8603 8604 dragMove: function(delta) { 8605 var that = this, 8606 dimension = that.dimension, 8607 axis = that.axis, 8608 movable = that.movable, 8609 position = movable[axis] + delta; 8610 8611 if (!dimension.present()) { 8612 return; 8613 } 8614 8615 if ((position < dimension.min && delta < 0) || (position > dimension.max && delta > 0)) { 8616 delta *= that.resistance; 8617 } 8618 8619 movable.translateAxis(axis, delta); 8620 that.trigger(CHANGE, that); 8621 } 8622 });
8623 8624 var Pane = Class.extend({ 8625 init: function(options) { 8626 var that = this, 8627 x, 8628 y, 8629 resistance; 8630 8631 extend(that, {elastic: true}, options); 8632 8633 resistance = that.elastic ? 0.5 : 0; 8634 8635 that.x = x = new PaneAxis({ 8636 axis: "x", 8637 dimension: that.dimensions.x, 8638 resistance: resistance, 8639 movable: that.movable 8640 }); 8641 8642 that.y = y = new PaneAxis({ 8643 axis: "y", 8644 dimension: that.dimensions.y, 8645 resistance: resistance, 8646 movable: that.movable 8647 }); 8648 8649 that.drag.bind(["move", "end"], { 8650 move: function(e) { 8651 if (x.dimension.present() || y.dimension.present()) { 8652 x.dragMove(e.x.delta); 8653 y.dragMove(e.y.delta); 8654 e.preventDefault(); 8655 } else { 8656 that.drag.skip(); 8657 } 8658 }, 8659 8660 end: function(e) { 8661 e.preventDefault(); 8662 } 8663 }); 8664 } 8665 }); 8666 8667 var TRANSFORM_STYLE = support.transitions.prefix + "Transform", 8668 round = Math.round, 8669 translate; 8670 8671 if (support.hasHW3D) { 8672 translate = function(x, y) { 8673 return "translate3d(" + round(x) + "px," + round(y) +"px,0)"; 8674 }; 8675 } else { 8676 translate = function(x, y) { 8677 return "translate(" + round(x) + "px," + round(y) +"px)"; 8678 }; 8679 } 8680 8681 var Movable = Observable.extend({ 8682 init: function(element) { 8683 var that = this; 8684 8685 Observable.fn.init.call(that); 8686 8687 that.element = $(element); 8688 that.x = 0; 8689 that.y = 0; 8690 that._saveCoordinates(translate(that.x, that.y)); 8691 }, 8692 8693 translateAxis: function(axis, by) { 8694 this[axis] += by; 8695 this.refresh(); 8696 }, 8697 8698 translate: function(coordinates) { 8699 this.x += coordinates.x; 8700 this.y += coordinates.y; 8701 this.refresh(); 8702 }, 8703 8704 moveAxis: function(axis, value) { 8705 this[axis] = value; 8706 this.refresh(); 8707 }, 8708 8709 moveTo: function(coordinates) { 8710 extend(this, coordinates); 8711 this.refresh(); 8712 }, 8713 8714 refresh: function() { 8715 var that = this, 8716 newCoordinates = translate(that.x, that.y); 8717 8718 if (newCoordinates != that.coordinates) { 8719 that.element[0].style[TRANSFORM_STYLE] = newCoordinates; 8720 that._saveCoordinates(newCoordinates); 8721 that.trigger(CHANGE); 8722 } 8723 }, 8724 8725 _saveCoordinates: function(coordinates) { 8726 this.coordinates = coordinates; 8727 } 8728 }); 8729 8730 var DropTarget = Widget.extend(/** @lends kendo.ui.DropTarget.prototype */ { 8731 /** 8732 * @constructs 8733 * @extends kendo.ui.Widget 8734 * @param {DomElement} element DOM element 8735 * @param {Object} options Configuration options. 8736 * @option {String} [group] <"default"> Used to group sets of draggable and drop targets. A draggable with the same group value as a drop target will be accepted by the drop target. 8737 */ 8738 init: function(element, options) { 8739 var that = this; 8740 8741 Widget.fn.init.call(that, element, options); 8742 8743 var group = that.options.group; 8744 8745 if (!(group in dropTargets)) { 8746 dropTargets[group] = [ that ]; 8747 } else { 8748 dropTargets[group].push( that ); 8749 } 8750 }, 8751 8752 events: [ 8753 /** 8754 * Fires when draggable moves over the drop target. 8755 * @name kendo.ui.DropTarget#dragenter 8756 * @event 8757 * @param {Event} e 8758 * @param {jQueryObject} e.draggable Reference to the draggable that enters the drop target. 8759 */ 8760 DRAGENTER, 8761 /** 8762 * Fires when draggable moves out of the drop target. 8763 * @name kendo.ui.DropTarget#dragleave 8764 * @event 8765 * @param {Event} e 8766 * @param {jQueryObject} e.draggable Reference to the draggable that leaves the drop target. 8767 */ 8768 DRAGLEAVE, 8769 /** 8770 * Fires when draggable is dropped over the drop target. 8771 * @name kendo.ui.DropTarget#drop 8772 * @event 8773 * @param {Event} e 8774 * @param {jQueryObject} e.draggable Reference to the draggable that is dropped over the drop target. 8775 * @param {jQueryObject} e.draggable.currentTarget The element that the drag and drop operation started from. 8776 */ 8777 DROP 8778 ], 8779 8780 options: { 8781 name: "DropTarget", 8782 group: "default" 8783 }, 8784 8785 _trigger: function(eventName, e) { 8786 var that = this, 8787 draggable = draggables[that.options.group]; 8788 8789 if (draggable) { 8790 return that.trigger(eventName, extend({}, e.event, { 8791 draggable: draggable 8792 })); 8793 } 8794 }, 8795 8796 _over: function(e) { 8797 this._trigger(DRAGENTER, e); 8798 }, 8799 8800 _out: function(e) { 8801 this._trigger(DRAGLEAVE, e); 8802 }, 8803 8804 _drop: function(e) { 8805 var that = this, 8806 draggable = draggables[that.options.group]; 8807 8808 if (draggable) { 8809 draggable.dropped = !that._trigger(DROP, e); 8810 } 8811 } 8812 });
8813 8814 /** 8815 * @name kendo.ui.Draggable.Description 8816 * 8817 * @section <h4>Draggable</h4> 8818 * Enable draggable functionality on any DOM element. 8819 * 8820 * @exampleTitle <b>Draggable</b> initialization 8821 * @example 8822 * var draggable = $("#draggable").kendoDraggable(); 8823 * 8824 * @name kendo.ui.DropTarget.Description 8825 * 8826 * @section <h4>DropTarget</h4> 8827 * Enable any DOM element to be a target for draggable elements. 8828 * 8829 * @exampleTitle <b>DropTarget</b> initialization 8830 * @example 8831 * var dropTarget = $("#dropTarget").kendoDropTarget(); 8832 */ 8833 var Draggable = Widget.extend(/** @lends kendo.ui.Draggable.prototype */{ 8834 /** 8835 * @constructs 8836 * @extends kendo.ui.Widget 8837 * @param {DomElement} element DOM element 8838 * @param {Object} options Configuration options. 8839 * @option {Integer} [distance] <5> The required distance that the mouse should travel in order to initiate a drag. 8840 * @option {Selector} [filter] Selects child elements that are draggable if a widget is attached to a container. 8841 * @option {String} [group] <"default"> Used to group sets of draggable and drop targets. A draggable with the same group value as a drop target will be accepted by the drop target. 8842 * @option {Object} [cursorOffset] <null> If set, specifies the offset of the hint relative to the mouse cursor/finger. 8843 * By default, the hint is initially positioned on top of the draggable source offset. The option accepts an object with two keys: <code>top</code> and <code>left</code>. 8844 * _exampleTitle Initialize Draggable with cursorOffset 8845 * _example 8846 * $("#draggable").kendoDraggable({cursorOffset: {top: 10, left: 10}); 8847 * @option {Function | jQueryObject} [hint] Provides a way for customization of the drag indicator. If a function is supplied, it receives one argument - the draggable element's jQuery object. 8848 * _example 8849 * //hint as a function 8850 * $("#draggable").kendoDraggable({ 8851 * hint: function(element) { 8852 * return $("#draggable").clone(); 8853 * // same as 8854 * // return element.clone(); 8855 * } 8856 * }); 8857 * 8858 * //hint as jQuery object 8859 * $("#draggable").kendoDraggable({ 8860 * hint: $("#draggableHint"); 8861 * }); 8862 */ 8863 init: function (element, options) { 8864 var that = this; 8865 8866 Widget.fn.init.call(that, element, options); 8867 8868 that.drag = new Drag(that.element, { 8869 global: true, 8870 stopPropagation: true, 8871 filter: that.options.filter, 8872 threshold: that.options.distance, 8873 start: proxy(that._start, that), 8874 move: proxy(that._drag, that), 8875 end: proxy(that._end, that), 8876 cancel: proxy(that._cancel, that) 8877 }); 8878 8879 that.destroy = proxy(that._destroy, that); 8880 that.captureEscape = function(e) { 8881 if (e.keyCode === kendo.keys.ESC) { 8882 that._trigger(DRAGCANCEL, {event: e}); 8883 that.drag.cancel(); 8884 } 8885 }; 8886 }, 8887 8888 events: [ 8889 /** 8890 * Fires when item drag starts. 8891 * @name kendo.ui.Draggable#dragstart 8892 * @event 8893 * @param {Event} e 8894 */ 8895 DRAGSTART, 8896 /** 8897 * Fires while dragging. 8898 * @name kendo.ui.Draggable#drag 8899 * @event 8900 * @param {Event} e 8901 */ 8902 DRAG, 8903 /** 8904 * Fires when item drag ends. 8905 * @name kendo.ui.Draggable#dragend 8906 * @event 8907 * @param {Event} e 8908 */ 8909 DRAGEND, 8910 /** 8911 * Fires when item drag is canceled by pressing the Escape key. 8912 * @name kendo.ui.Draggable#dragcancel 8913 * @event 8914 * @param {Event} e 8915 */
8916 DRAGCANCEL 8917 ], 8918 8919 options: { 8920 name: "Draggable", 8921 distance: 5, 8922 group: "default", 8923 cursorOffset: null, 8924 dropped: false 8925 }, 8926 8927 _start: function(e) { 8928 var that = this, 8929 options = that.options, 8930 hint = options.hint; 8931 8932 that.currentTarget = that.drag.target; 8933 that.currentTargetOffset = getOffset(that.currentTarget); 8934 8935 if (hint) { 8936 that.hint = $.isFunction(hint) ? $(hint(that.currentTarget)) : hint; 8937 8938 var offset = getOffset(that.currentTarget); 8939 that.hintOffset = offset; 8940 that.hint.css( { 8941 position: "absolute", 8942 zIndex: 20000, // the Window's z-index is 10000 and can be raised because of z-stacking 8943 left: offset.x, 8944 top: offset.y 8945 }) 8946 .appendTo(document.body); 8947 } 8948 8949 draggables[options.group] = that; 8950 8951 that.dropped = false; 8952 8953 if (that._trigger(DRAGSTART, e)) { 8954 that.drag.cancel(); 8955 that.destroy(); 8956 } 8957 8958 $(document).on(KEYUP, that.captureEscape); 8959 }, 8960 8961 updateHint: function(e) { 8962 var that = this, 8963 coordinates, 8964 cursorOffset = that.options.cursorOffset; 8965 8966 if (cursorOffset) { 8967 coordinates = { left: e.x.location + cursorOffset.left, top: e.y.location + cursorOffset.top }; 8968 } else { 8969 that.hintOffset.left += e.x.delta; 8970 that.hintOffset.top += e.y.delta; 8971 coordinates = that.hintOffset; 8972 } 8973 8974 that.hint.css(coordinates); 8975 }, 8976 8977 _drag: function(e) { 8978 var that = this; 8979 8980 e.preventDefault(); 8981 8982 that._withDropTarget(e, function(target) { 8983 if (!target) { 8984 if (lastDropTarget) { 8985 lastDropTarget._trigger(DRAGLEAVE, e); 8986 lastDropTarget = null; 8987 } 8988 return; 8989 } 8990 8991 if (lastDropTarget) { 8992 if (target.element[0] === lastDropTarget.element[0]) { 8993 return; 8994 } 8995 8996 lastDropTarget._trigger(DRAGLEAVE, e); 8997 } 8998 8999 target._trigger(DRAGENTER, e); 9000 lastDropTarget = target; 9001 }); 9002 9003 that._trigger(DRAG, e); 9004 9005 if (that.hint) { 9006 that.updateHint(e); 9007 } 9008 }, 9009 9010 _end: function(e) { 9011 var that = this; 9012 9013 that._withDropTarget(e, function(target) { 9014 if (target) { 9015 target._drop(e); 9016 lastDropTarget = null; 9017 } 9018 }); 9019 9020 that._trigger(DRAGEND, e); 9021 that._cancel(e.event); 9022 }, 9023 9024 _cancel: function(e) { 9025 var that = this; 9026 9027 if (that.hint && !that.dropped) { 9028 that.hint.animate(that.currentTargetOffset, "fast", that.destroy); 9029 } else { 9030 that.destroy(); 9031 } 9032 }, 9033 9034 _trigger: function(eventName, e) { 9035 var that = this; 9036 9037 return that.trigger( 9038 eventName, extend( 9039 {}, 9040 e.event, 9041 { 9042 x: e.x, 9043 y: e.y, 9044 currentTarget: that.currentTarget 9045 })); 9046 }, 9047 9048 _withDropTarget: function(e, callback) { 9049 var that = this, 9050 target, 9051 result, 9052 options = that.options, 9053 targets = dropTargets[options.group]; 9054 9055 if (targets && targets.length) { 9056 target = elementUnderCursor(e); 9057 9058 if (that.hint && contains(that.hint, target)) { 9059 that.hint.hide(); 9060 target = elementUnderCursor(e); 9061 that.hint.show(); 9062 } 9063 9064 $.each(targets, function() { 9065 var that = this, 9066 element = that.element[0]; 9067 9068 if (contains(element, target)) { 9069 result = that; 9070 return false; 9071 } 9072 }); 9073 9074 callback(result); 9075 } 9076 }, 9077 9078 _destroy: function() { 9079 var that = this; 9080 9081 if (that.hint) { 9082 that.hint.remove(); 9083 } 9084 9085 delete draggables[that.options.group]; 9086 9087 that.trigger("destroy"); 9088 $(document).off(KEYUP, that.captureEscape); 9089 } 9090 });
9091 9092 kendo.ui.plugin(DropTarget); 9093 kendo.ui.plugin(Draggable); 9094 kendo.Drag = Drag; 9095 kendo.Tap = Tap; 9096 9097 extend(kendo.ui, { 9098 Pane: Pane, 9099 PaneDimensions: PaneDimensions, 9100 Movable: Movable 9101 }); 9102 9103 })(jQuery); 9104(function($, undefined) { 9105 var kendo = window.kendo, 9106 mobile = kendo.mobile, 9107 fx = kendo.fx, 9108 ui = mobile.ui, 9109 proxy = $.proxy, 9110 extend = $.extend, 9111 Widget = ui.Widget, 9112 Class = kendo.Class, 9113 Movable = kendo.ui.Movable, 9114 Pane = kendo.ui.Pane, 9115 PaneDimensions = kendo.ui.PaneDimensions, 9116 Transition = fx.Transition, 9117 Animation = fx.Animation, 9118 SNAPBACK_DURATION = 500, 9119 SCROLLBAR_OPACITY = 0.7, 9120 FRICTION = 0.93, 9121 OUT_OF_BOUNDS_FRICTION = 0.5, 9122 RELEASECLASS = "km-scroller-release", 9123 REFRESHCLASS = "km-scroller-refresh", 9124 PULL = "pull", 9125 CHANGE = "change", 9126 RESIZE = "resize", 9127 SCROLL = "scroll"; 9128 9129 var DragInertia = Animation.extend({ 9130 init: function(options) { 9131 var that = this; 9132 9133 Animation.fn.init.call(that); 9134 9135 extend(that, options, { 9136 transition: new Transition({ 9137 axis: options.axis, 9138 movable: options.movable, 9139 onEnd: function() { that._end(); } 9140 }) 9141 }); 9142 9143 that.tap.bind("press", function() { that.cancel(); }); 9144 that.drag.bind("end", proxy(that.start, that)); 9145 that.drag.bind("tap", proxy(that.onEnd, that)); 9146 }, 9147 9148 onCancel: function() { 9149 this.transition.cancel(); 9150 }, 9151 9152 freeze: function(location) { 9153 var that = this; 9154 that.cancel(); 9155 that._moveTo(location); 9156 }, 9157 9158 onEnd: function() { 9159 var that = this; 9160 if (that._outOfBounds()) { 9161 that._snapBack(); 9162 } else { 9163 that._end(); 9164 } 9165 }, 9166 9167 done: function() { 9168 return Math.abs(this.velocity) < 1; 9169 }, 9170 9171 start: function() { 9172 var that = this; 9173 9174 if (!that.dimension.present()) { return; } 9175 9176 if (that._outOfBounds()) { 9177 that._snapBack(); 9178 } else { 9179 that.velocity = that.drag[that.axis].velocity * 16; 9180 if (that.velocity) { 9181 that.tap.captureNext(); 9182 Animation.fn.start.call(that); 9183 } 9184 } 9185 }, 9186 9187 tick: function() { 9188 var that = this, 9189 dimension = that.dimension, 9190 friction = that._outOfBounds() ? OUT_OF_BOUNDS_FRICTION : FRICTION, 9191 delta = (that.velocity *= friction), 9192 location = that.movable[that.axis] + delta; 9193 9194 if (!that.elastic && dimension.outOfBounds(location)) { 9195 location = Math.max(Math.min(location, dimension.max), dimension.min); 9196 that.velocity = 0; 9197 } 9198 9199 that.movable.moveAxis(that.axis, location); 9200 }, 9201 9202 _end: function() { 9203 this.tap.cancelCapture(); 9204 this.end(); 9205 }, 9206 9207 _outOfBounds: function() { 9208 return this.dimension.outOfBounds(this.movable[this.axis]); 9209 }, 9210 9211 _snapBack: function() { 9212 var that = this, 9213 dimension = that.dimension, 9214 snapBack = that.movable[that.axis] > dimension.max ? dimension.max : dimension.min; 9215 that._moveTo(snapBack); 9216 }, 9217 9218 _moveTo: function(location) { 9219 this.transition.moveTo({ location: location, duration: SNAPBACK_DURATION, ease: Transition.easeOutExpo }); 9220 } 9221 }); 9222 9223 var ScrollBar = Class.extend({ 9224 init: function(options) { 9225 var that = this, 9226 horizontal = options.axis === "x", 9227 element = $('<div class="km-touch-scrollbar km-' + (horizontal ? "horizontal" : "vertical") + '-scrollbar" />'); 9228 9229 extend(that, options, { 9230 element: element, 9231 elementSize: 0, 9232 movable: new Movable(element), 9233 scrollMovable: options.movable, 9234 size: horizontal ? "width" : "height" 9235 }); 9236 9237 that.scrollMovable.bind(CHANGE, proxy(that._move, that)); 9238 that.container.append(element); 9239 }, 9240 9241 _move: function() { 9242 var that = this, 9243 axis = that.axis, 9244 dimension = that.dimension, 9245 paneSize = dimension.size, 9246 scrollMovable = that.scrollMovable, 9247 sizeRatio = paneSize / dimension.total, 9248 position = Math.round(-scrollMovable[axis] * sizeRatio), 9249 size = Math.round(paneSize * sizeRatio); 9250 9251 if (position + size > paneSize) { 9252 size = paneSize - position; 9253 } else if (position < 0) { 9254 size += position; 9255 position = 0; 9256 } 9257 9258 if (that.elementSize != size) { 9259 that.element.css(that.size, size + "px"); 9260 that.elementSize = size; 9261 } 9262 9263 that.movable.moveAxis(axis, position); 9264 }, 9265 9266 show: function() { 9267 this.element.css({opacity: SCROLLBAR_OPACITY, visibility: "visible"}); 9268 }, 9269 9270 hide: function() { 9271 this.element.css({opacity: 0}); 9272 } 9273 });
9274 9275 /** 9276 * @name kendo.mobile.ui.Scroller.Description 9277 * @section 9278 * <p>The Kendo Mobile Scroller widget enables touch friendly kinetic scrolling for the contents of a given DOM element. </p> 9279 * 9280 * <h3>Getting Started</h3> 9281 * <p>Each mobile View initializes a scroller for its content element. In addition to that, a scroller will be initialized for every element with a 9282 * <code>role</code> data attribute set to <code>scroller</code>. Alternatively, it can be initialized using jQuery selector.</p> 9283 * @exampleTitle Initialize mobile Scroller using a role data attribute. 9284 * @example 9285 * <div data-role="scroller"> 9286 * Foo 9287 * </div> 9288 * 9289 * @exampleTitle Initialize mobile Scroller using a jQuery selector. 9290 * @example 9291 * <div id="scroller"></div> 9292 * <script> 9293 * var scroller = $("#scroller").kendoMobileScroller(); 9294 * </script> 9295 * 9296 * @exampleTitle Obtain the current mobile view scroller 9297 * @example 9298 * <div data-role="view" data-init="getScroller"> 9299 * Foo 9300 * </div> 9301 * <script> 9302 * function getScroller(e) { 9303 * var scroller = e.view.scroller; 9304 * } 9305 * </script> 9306 * 9307 * @section 9308 * <p>The scroller exposes the following fields:</p> 9309 * <ul> 9310 * <li><strong>scrollTop</strong> - the number of pixels that are hidden from view above the scrollable area.</li> 9311 * <li><strong>scrollLeft</strong> - the number of pixels that are hidden from view to the left of the scrollable area.</li> 9312 * </ul> 9313 * 9314 */ 9315 var Scroller = Widget.extend(/** @lends kendo.mobile.ui.Scroller.prototype */{ 9316 /** 9317 * @constructs 9318 * @extends kendo.mobile.ui.Widget 9319 * @param {DomElement} element DOM element 9320 * @param {Object} options 9321 * @option {Boolean} [elastic] <true> Weather or not to allow out of bounds dragging and easing. 9322 * @option {Number} [pullOffset] <140> The threshold bellow which a releasing the scroller will trigger the pull event. 9323 * Has effect only when the pullToRefresh option is set to true. 9324 * @option {String} [pullTemplate] <Pull to refresh> The message template displayed when the user pulls the scroller. 9325 * Has effect only when the pullToRefresh option is set to true. 9326 * @option {Boolean} [pullToRefresh] <false> If set to true, the scroller will display a hint when the user pulls the container beyond its top limit. 9327 * If a pull beyond the specified pullOffset occurs, a pull event will be triggered. 9328 * @option {String} [releaseTemplate] <Release to refresh> The message template displayed when the user pulls the scroller below the 9329 * pullOffset, indicating that pullToRefresh will occur. 9330 * Has effect only when the pullToRefresh option is set to true. 9331 * @option {String} [refreshTemplate] <Refreshing> The message template displayed during the refresh. 9332 * Has effect only when the pullToRefresh option is set to true. 9333 */ 9334 init: function(element, options) { 9335 var that = this; 9336 Widget.fn.init.call(that, element, options); 9337 9338 element = that.element; 9339 9340 element 9341 .css("overflow", "hidden") 9342 .addClass("km-scroll-wrapper") 9343 .wrapInner('<div class="km-scroll-container"/>'); 9344 9345 var inner = element.children().first(), 9346 9347 tap = new kendo.Tap(element), 9348 9349 movable = new Movable(inner), 9350 9351 dimensions = new PaneDimensions({ 9352 element: inner, 9353 container: element, 9354 change: function() { 9355 that.trigger(RESIZE); 9356 } 9357 }), 9358 9359 drag = new kendo.Drag(element, { 9360 allowSelection: true, 9361 start: function(e) { 9362 dimensions.refresh(); 9363 drag.capture(); 9364 } 9365 }), 9366 9367 pane = new Pane({ 9368 movable: movable, 9369 dimensions: dimensions, 9370 drag: drag, 9371 elastic: that.options.elastic 9372 }); 9373 9374 movable.bind(CHANGE, function() { 9375 that.scrollTop = - movable.y; 9376 that.scrollLeft = - movable.x; 9377 9378 that.trigger(SCROLL, { 9379 scrollTop: that.scrollTop, 9380 scrollLeft: that.scrollLeft 9381 }); 9382 }); 9383 9384 extend(that, { 9385 movable: movable, 9386 dimensions: dimensions, 9387 drag: drag, 9388 pane: pane, 9389 tap: tap, 9390 pulled: false, 9391 scrollElement: inner 9392 }); 9393 9394 that._initAxis("x"); 9395 that._initAxis("y"); 9396 9397 dimensions.refresh();
9398 9399 if (that.options.pullToRefresh) { 9400 that._initPullToRefresh(); 9401 } 9402 }, 9403 9404 /** 9405 * Returns the height in pixels of the scroller content. 9406 */ 9407 scrollHeight: function() { 9408 return this.scrollElement[0].scrollHeight; 9409 }, 9410 9411 /** 9412 * Returns the width in pixels of the scroller content. 9413 */ 9414 scrollWidth: function() { 9415 return this.scrollElement[0].scrollWidth; 9416 }, 9417 9418 options: { 9419 name: "Scroller", 9420 pullOffset: 140, 9421 elastic: true, 9422 pullTemplate: "Pull to refresh", 9423 releaseTemplate: "Release to refresh", 9424 refreshTemplate: "Refreshing" 9425 }, 9426 9427 events: [ 9428 /** 9429 * Fires when the pull option is set to true, and the user pulls the scrolling container beyond the specified pullThreshold. 9430 * @name kendo.mobile.ui.Scroller#pull 9431 * @event 9432 * @param {Event} e 9433 */ 9434 PULL, 9435 /** 9436 * Fires when the pull option is set to true, and the user pulls the scrolling container beyond the specified pullThreshold. 9437 * @name kendo.mobile.ui.Scroller#scroll 9438 * @event 9439 * @param {Event} e 9440 * @param {Number} e.scrollTop The number of pixels that are hidden from view above the scrollable area. 9441 * @param {Number} e.scrollLeft The number of pixels that are hidden from view to the left of the scrollable area. 9442 * @exampleTitle Bind to scroller scroll event in view init 9443 * @example 9444 * <div data-role="view" data-init="attachToScroller"> ... </div> 9445 * <script> 9446 * function attachToScroller(e) { 9447 * var scroller = e.view.scroller; 9448 * scroller.bind("scroll", function(e) { 9449 * console.log(e.scrollTop); 9450 * console.log(e.scrollLeft); 9451 * }); 9452 * } 9453 * </script> 9454 */ 9455 SCROLL, 9456 /** 9457 * Fires when the scroller dimensions change (e.g. orientation change or resize) 9458 * @name kendo.mobile.ui.Scroller#resize 9459 * @event 9460 * @param {Event} e 9461 */ 9462 RESIZE 9463 ], 9464 9465 setOptions: function(options) { 9466 var that = this; 9467 Widget.fn.setOptions.call(that, options); 9468 if (options.pullToRefresh) { 9469 that._initPullToRefresh(); 9470 } 9471 }, 9472 9473 /** 9474 * Scrolls the container to the top. 9475 */ 9476 reset: function() { 9477 this.movable.moveTo({x: 0, y: 0}); 9478 }, 9479 9480 /** 9481 * Scrolls the container to the specified location 9482 * @param {Number} x The horizontal offset in pixels to scroll to. 9483 * @param {Number} y The vertical offset in pixels to scroll to. 9484 */ 9485 scrollTo: function(x, y) { 9486 this.movable.moveTo({x: x, y: y}); 9487 }, 9488 9489 /** 9490 * Indicate that the pull event is handled (i.e. data from the server has been retrieved). 9491 */ 9492 pullHandled: function() { 9493 var that = this; 9494 that.refreshHint.removeClass(REFRESHCLASS); 9495 that.hintContainer.html(that.pullTemplate({})); 9496 that.yinertia.onEnd(); 9497 that.xinertia.onEnd(); 9498 }, 9499 9500 _initPullToRefresh: function() { 9501 var that = this; 9502 9503 that.pullTemplate = kendo.template(that.options.pullTemplate); 9504 that.releaseTemplate = kendo.template(that.options.releaseTemplate); 9505 that.refreshTemplate = kendo.template(that.options.refreshTemplate); 9506 9507 that.scrollElement.prepend('<span class="km-scroller-pull"><span class="km-icon"></span><span class="km-template">' + that.pullTemplate({}) + '</span></span>'); 9508 that.refreshHint = that.scrollElement.children().first(); 9509 that.hintContainer = that.refreshHint.children(".km-template"); 9510 9511 that.pane.y.bind("change", proxy(that._paneChange, that)); 9512 that.drag.bind("end", proxy(that._dragEnd, that)); 9513 }, 9514 9515 _dragEnd: function() { 9516 var that = this; 9517 9518 if(!that.pulled) { 9519 return; 9520 } 9521 9522 that.pulled = false; 9523 that.refreshHint.removeClass(RELEASECLASS).addClass(REFRESHCLASS); 9524 that.hintContainer.html(that.refreshTemplate({})); 9525 that.trigger("pull"); 9526 that.yinertia.freeze(that.options.pullOffset / 2); 9527 }, 9528 9529 _paneChange: function() { 9530 var that = this; 9531 9532 if (that.movable.y / OUT_OF_BOUNDS_FRICTION > that.options.pullOffset) { 9533 if (!that.pulled) { 9534 that.pulled = true; 9535 that.refreshHint.removeClass(REFRESHCLASS).addClass(RELEASECLASS); 9536 that.hintContainer.html(that.releaseTemplate({})); 9537 } 9538 } else if (that.pulled) { 9539 that.pulled = false; 9540 that.refreshHint.removeClass(RELEASECLASS); 9541 that.hintContainer.html(that.pullTemplate({})); 9542 } 9543 }, 9544 9545 _initAxis: function(axis) { 9546 var that = this, 9547 movable = that.movable, 9548 dimension = that.dimensions[axis], 9549 tap = that.tap, 9550 9551 scrollBar = new ScrollBar({ 9552 axis: axis, 9553 movable: movable, 9554 dimension: dimension, 9555 container: that.element 9556 }), 9557 9558 inertia = new DragInertia({ 9559 axis: axis, 9560 movable: movable, 9561 tap: tap, 9562 drag: that.drag, 9563 dimension: dimension, 9564 elastic: that.options.elastic, 9565 end: function() { scrollBar.hide(); } 9566 }); 9567 9568 that[axis + "inertia"] = inertia; 9569 9570 that.pane[axis].bind(CHANGE, function() { 9571 scrollBar.show(); 9572 }); 9573 } 9574 });
9575 9576 ui.plugin(Scroller); 9577})(jQuery); 9578(function ($, undefined) { 9579 var kendo = window.kendo, 9580 Widget = kendo.ui.Widget, 9581 proxy = $.proxy, 9582 CONTAINER_EMPTY_TEXT = "Drag a column header and drop it here to group by that column", 9583 indicatorTmpl = kendo.template('<div class="k-group-indicator" data-#=data.ns#field="${data.field}" data-#=data.ns#title="${data.title || ""}" data-#=data.ns#dir="${data.dir || "asc"}">' + 9584 '<a href="\\#" class="k-link">' + 9585 '<span class="k-icon k-arrow-${(data.dir || "asc") == "asc" ? "up" : "down"}-small">(sorted ${(data.dir || "asc") == "asc" ? "ascending": "descending"})</span>' + 9586 '${data.title ? data.title: data.field}' + 9587 '</a>' + 9588 '<a class="k-button k-button-icon k-button-bare">' + 9589 '<span class="k-icon k-group-delete"></span>' + 9590 '</a>' + 9591 '</div>', { useWithBlock:false }), 9592 hint = function(target) { 9593 return $('<div class="k-header k-drag-clue" />') 9594 .css({ 9595 width: target.width(), 9596 paddingLeft: target.css("paddingLeft"), 9597 paddingRight: target.css("paddingRight"), 9598 lineHeight: target.height() + "px", 9599 paddingTop: target.css("paddingTop"), 9600 paddingBottom: target.css("paddingBottom") 9601 }) 9602 .html(target.attr(kendo.attr("title")) || target.attr(kendo.attr("field"))) 9603 .prepend('<span class="k-icon k-drag-status k-denied" />'); 9604 }, 9605 dropCue = $('<div class="k-grouping-dropclue"/>'), 9606 nameSpecialCharRegExp = /(\[|\]|\$|\.|\:|\+)/g; 9607 9608 function dropCueOffsetTop(element) { 9609 return element.position().top + 3; 9610 } 9611 9612 var Groupable = Widget.extend({ 9613 init: function(element, options) { 9614 var that = this, 9615 groupContainer, 9616 group = kendo.guid(), 9617 intializePositions = proxy(that._intializePositions, that), 9618 draggable, 9619 dropCuePositions = that._dropCuePositions = []; 9620 9621 Widget.fn.init.call(that, element, options); 9622 9623 draggable = that.options.draggable || new kendo.ui.Draggable(that.element, { 9624 filter: that.options.filter, 9625 hint: hint, 9626 group: group 9627 }); 9628 9629 groupContainer = that.groupContainer = $(that.options.groupContainer, that.element) 9630 .kendoDropTarget({ 9631 group: draggable.options.group, 9632 dragenter: function(e) { 9633 if (that._canDrag(e.draggable.currentTarget)) { 9634 e.draggable.hint.find(".k-drag-status").removeClass("k-denied").addClass("k-add"); 9635 dropCue.css({top: dropCueOffsetTop(groupContainer), left: 0}).appendTo(groupContainer); 9636 } 9637 }, 9638 dragleave: function(e) { 9639 e.draggable.hint.find(".k-drag-status").removeClass("k-add").addClass("k-denied"); 9640 dropCue.remove(); 9641 }, 9642 drop: function(e) { 9643 var targetElement = e.draggable.currentTarget, 9644 field = targetElement.attr(kendo.attr("field")), 9645 title = targetElement.attr(kendo.attr("title")), 9646 sourceIndicator = that.indicator(field), 9647 dropCuePositions = that._dropCuePositions, 9648 lastCuePosition = dropCuePositions[dropCuePositions.length - 1], 9649 position; 9650 9651 if (!targetElement.hasClass("k-group-indicator") && !that._canDrag(targetElement)) { 9652 return; 9653 } 9654 if(lastCuePosition) { 9655 position = that._dropCuePosition(dropCue.offset().left + parseInt(lastCuePosition.element.css("marginLeft"), 10) + parseInt(lastCuePosition.element.css("marginRight"), 10)); 9656 if(position && that._canDrop($(sourceIndicator), position.element, position.left)) { 9657 if(position.before) { 9658 position.element.before(sourceIndicator || that.buildIndicator(field, title)); 9659 } else { 9660 position.element.after(sourceIndicator || that.buildIndicator(field, title)); 9661 } 9662 9663 that._change(); 9664 } 9665 } else { 9666 that.groupContainer.append(that.buildIndicator(field, title)); 9667 that._change(); 9668 } 9669 } 9670 }) 9671 .kendoDraggable({ 9672 filter: "div.k-group-indicator",
9673 hint: hint, 9674 group: draggable.options.group, 9675 dragcancel: proxy(that._dragCancel, that), 9676 dragstart: function(e) { 9677 var element = e.currentTarget, 9678 marginLeft = parseInt(element.css("marginLeft"), 10), 9679 left = element.position().left - marginLeft; 9680 9681 intializePositions(); 9682 dropCue.css({top: dropCueOffsetTop(groupContainer), left: left}).appendTo(groupContainer); 9683 this.hint.find(".k-drag-status").removeClass("k-denied").addClass("k-add"); 9684 }, 9685 dragend: function() { 9686 that._dragEnd(this); 9687 }, 9688 drag: proxy(that._drag, that) 9689 }) 9690 .delegate(".k-button", "click", function(e) { 9691 e.preventDefault(); 9692 that._removeIndicator($(this).parent()); 9693 }) 9694 .delegate(".k-link", "click", function(e) { 9695 var current = $(this).parent(), 9696 newIndicator = that.buildIndicator(current.attr(kendo.attr("field")), current.attr(kendo.attr("title")), current.attr(kendo.attr("dir")) == "asc" ? "desc" : "asc"); 9697 9698 current.before(newIndicator).remove(); 9699 that._change(); 9700 e.preventDefault(); 9701 }); 9702 9703 draggable.bind([ "dragend", "dragcancel", "dragstart", "drag" ], 9704 { 9705 dragend: function() { 9706 that._dragEnd(this); 9707 }, 9708 dragcancel: proxy(that._dragCancel, that), 9709 dragstart: function(e) { 9710 var element, marginRight, left; 9711 9712 if (!that.options.allowDrag && !that._canDrag(e.currentTarget)) { 9713 e.preventDefault(); 9714 return; 9715 } 9716 9717 intializePositions(); 9718 if(dropCuePositions.length) { 9719 element = dropCuePositions[dropCuePositions.length - 1].element; 9720 marginRight = parseInt(element.css("marginRight"), 10); 9721 left = element.position().left + element.outerWidth() + marginRight; 9722 } else { 9723 left = 0; 9724 } 9725 }, 9726 drag: proxy(that._drag, that) 9727 }); 9728 9729 that.dataSource = that.options.dataSource; 9730 9731 if(that.dataSource) { 9732 that._refreshHandler = proxy(that.refresh, that); 9733 that.dataSource.bind("change", that._refreshHandler); 9734 } 9735 }, 9736 9737 refresh: function() { 9738 var that = this, 9739 dataSource = that.dataSource; 9740 9741 that.groupContainer.empty().append( 9742 $.map(dataSource.group() || [], function(item) { 9743 var fieldName = item.field.replace(nameSpecialCharRegExp, "\\$1"); 9744 var element = that.element.find(that.options.filter).filter("[" + kendo.attr("field") + "=" + fieldName + "]"); 9745 return that.buildIndicator(item.field, element.attr(kendo.attr("title")), item.dir); 9746 }).join("") 9747 ); 9748 that._invalidateGroupContainer(); 9749 }, 9750 9751 destroy: function() { 9752 var that = this; 9753 if (that.dataSource && that._refreshHandler) { 9754 that.dataSource.unbind("change", that._refreshHandler); 9755 } 9756 }, 9757 9758 options: { 9759 name: "Groupable", 9760 filter: "th" 9761 }, 9762 9763 indicator: function(field) { 9764 var indicators = $(".k-group-indicator", this.groupContainer); 9765 return $.grep(indicators, function (item) 9766 { 9767 return $(item).attr(kendo.attr("field")) === field; 9768 })[0]; 9769 }, 9770 buildIndicator: function(field, title, dir) { 9771 return indicatorTmpl({ field: field, dir: dir, title: title, ns: kendo.ns }); 9772 }, 9773 descriptors: function() { 9774 var that = this, 9775 indicators = $(".k-group-indicator", that.groupContainer), 9776 aggregates, 9777 names, 9778 field, 9779 idx, 9780 length; 9781 9782 aggregates = that.element.find(that.options.filter).map(function() { 9783 var cell = $(this), 9784 aggregate = cell.attr(kendo.attr("aggregates")), 9785 member = cell.attr(kendo.attr("field")); 9786 9787 if (aggregate && aggregate !== "") { 9788 names = aggregate.split(","); 9789 aggregate = []; 9790 for (idx = 0, length = names.length; idx < length; idx++) { 9791 aggregate.push({ field: member, aggregate: names[idx] }); 9792 } 9793 } 9794 return aggregate; 9795 }).toArray(); 9796 9797 return $.map(indicators, function(item) { 9798 item = $(item); 9799 field = item.attr(kendo.attr("field")); 9800 9801 return { 9802 field: field, 9803 dir: item.attr(kendo.attr("dir")), 9804 aggregates: aggregates || [] 9805 }; 9806 }); 9807 }, 9808 _removeIndicator: function(indicator) { 9809 var that = this; 9810 indicator.remove(); 9811 that._invalidateGroupContainer(); 9812 that._change(); 9813 }, 9814 _change: function() { 9815 var that = this; 9816 if(that.dataSource) { 9817 that.dataSource.group(that.descriptors()); 9818 } 9819 }, 9820 _dropCuePosition: function(position) { 9821 var dropCuePositions = this._dropCuePositions; 9822 if(!dropCue.is(":visible") || dropCuePositions.length === 0) { 9823 return; 9824 } 9825 9826 position = Math.ceil(position); 9827 9828 var lastCuePosition = dropCuePositions[dropCuePositions.length - 1],
9829 right = lastCuePosition.right, 9830 marginLeft = parseInt(lastCuePosition.element.css("marginLeft"), 10), 9831 marginRight = parseInt(lastCuePosition.element.css("marginRight"), 10); 9832 9833 if(position >= right) { 9834 position = { 9835 left: lastCuePosition.element.position().left + lastCuePosition.element.outerWidth() + marginRight, 9836 element: lastCuePosition.element, 9837 before: false 9838 }; 9839 } else { 9840 position = $.grep(dropCuePositions, function(item) { 9841 return item.left <= position && position <= item.right; 9842 })[0]; 9843 9844 if(position) { 9845 position = { 9846 left: position.element.position().left - marginLeft, 9847 element: position.element, 9848 before: true 9849 }; 9850 } 9851 } 9852 9853 return position; 9854 }, 9855 _drag: function(event) { 9856 var location = kendo.touchLocation(event), 9857 position = this._dropCuePosition(location.x); 9858 if(position) { 9859 dropCue.css({ left: position.left }); 9860 } 9861 }, 9862 _canDrag: function(element) { 9863 return element.attr(kendo.attr("groupable")) != "false" && !this.indicator(element.attr(kendo.attr("field"))); 9864 }, 9865 _canDrop: function(source, target, position) { 9866 var next = source.next(); 9867 return source[0] !== target[0] && (!next[0] || target[0] !== next[0] || position > next.position().left); 9868 }, 9869 _dragEnd: function(draggable) { 9870 var that = this, 9871 field = draggable.currentTarget.attr(kendo.attr("field")), 9872 sourceIndicator = that.indicator(field); 9873 9874 if (draggable !== that.options.draggable && !draggable.dropped && sourceIndicator) { 9875 that._removeIndicator($(sourceIndicator)); 9876 } 9877 9878 that._dragCancel(); 9879 }, 9880 _dragCancel: function() { 9881 dropCue.remove(); 9882 this._dropCuePositions = []; 9883 }, 9884 _intializePositions: function() { 9885 var that = this, 9886 indicators = $(".k-group-indicator", that.groupContainer), 9887 left; 9888 that._dropCuePositions = $.map(indicators, function(item) { 9889 item = $(item); 9890 left = item.offset().left; 9891 return { 9892 left: parseInt(left, 10), 9893 right: parseInt(left + item.outerWidth(), 10), 9894 element: item 9895 }; 9896 }); 9897 }, 9898 _invalidateGroupContainer: function() { 9899 var groupContainer = this.groupContainer; 9900 if(groupContainer.is(":empty")) { 9901 groupContainer.html(CONTAINER_EMPTY_TEXT); 9902 } 9903 } 9904 }); 9905 9906 kendo.ui.plugin(Groupable); 9907 9908})(jQuery); 9909(function ($, undefined) { 9910 var kendo = window.kendo, 9911 Widget = kendo.ui.Widget, 9912 CHANGE = "change", 9913 KREORDERABLE = "k-reorderable"; 9914 9915 function toggleHintClass(hint, denied) { 9916 hint = $(hint); 9917 9918 if (denied) { 9919 hint.find(".k-drag-status").removeClass("k-add").addClass("k-denied"); 9920 } else { 9921 hint.find(".k-drag-status").removeClass("k-denied").addClass("k-add"); 9922 } 9923 } 9924 9925 var Reorderable = Widget.extend({ 9926 init: function(element, options) { 9927 var that = this, 9928 draggable, 9929 group = kendo.guid() + "-reorderable"; 9930 9931 Widget.fn.init.call(that, element, options); 9932 9933 element = that.element.addClass(KREORDERABLE); 9934 options = that.options; 9935 draggable = options.draggable || new kendo.ui.Draggable(element, { 9936 group: group, 9937 filter: options.filter, 9938 hint: options.hint 9939 }); 9940 9941 that.reorderDropCue = $('<div class="k-reorder-cue"><div class="k-icon k-arrow-down"></div><div class="k-icon k-arrow-up"></div></div>'); 9942
9943 element.find(draggable.options.filter).kendoDropTarget({ 9944 group: draggable.options.group, 9945 dragenter: function(e) { 9946 if (!that._draggable) { 9947 return; 9948 } 9949 9950 var dropTarget = this.element, 9951 same = dropTarget[0] === that._draggable[0]; 9952 9953 toggleHintClass(e.draggable.hint, same); 9954 if (!same) { 9955 that.reorderDropCue.css({ 9956 height: dropTarget.outerHeight(), 9957 top: element.offset().top, 9958 left: dropTarget.offset().left + (dropTarget.index() > that._draggable.index() ? dropTarget.outerWidth() : 0) 9959 }) 9960 .appendTo(document.body); 9961 } 9962 }, 9963 dragleave: function(e) { 9964 toggleHintClass(e.draggable.hint, true); 9965 that.reorderDropCue.remove(); 9966 }, 9967 drop: function() { 9968 if (!that._draggable) { 9969 return; 9970 } 9971 9972 var draggableElement = that._draggable[0], 9973 dropTarget = this.element[0], 9974 container; 9975 9976 if (draggableElement !== dropTarget) { 9977 container = element.find(draggable.options.filter); 9978 that.trigger(CHANGE, { 9979 element: that._draggable, 9980 oldIndex: container.index(draggableElement), 9981 newIndex: container.index(dropTarget) 9982 }); 9983 } 9984 } 9985 }); 9986 9987 draggable.bind([ "dragcancel", "dragend", "dragstart" ], 9988 { 9989 dragcancel: function() { 9990 that.reorderDropCue.remove(); 9991 that._draggable = null; 9992 }, 9993 dragend: function() { 9994 that.reorderDropCue.remove(); 9995 that._draggable = null; 9996 }, 9997 dragstart: function(e) { 9998 that._draggable = e.currentTarget; 9999 } 10000 } 10001 ); 10002 }, 10003 10004 options: { 10005 name: "Reorderable", 10006 filter: "*" 10007 }, 10008 10009 events: [ 10010 CHANGE 10011 ] 10012 10013 }); 10014 10015 kendo.ui.plugin(Reorderable); 10016 10017})(jQuery); 10018(function($, undefined) { 10019 var kendo = window.kendo, 10020 ui = kendo.ui, 10021 Widget = ui.Widget, 10022 proxy = $.proxy, 10023 isFunction = $.isFunction, 10024 extend = $.extend, 10025 HORIZONTAL = "horizontal", 10026 VERTICAL = "vertical", 10027 START = "start", 10028 RESIZE = "resize", 10029 RESIZEEND = "resizeend"; 10030 10031 var Resizable = Widget.extend({ 10032 init: function(element, options) { 10033 var that = this; 10034 10035 Widget.fn.init.call(that, element, options); 10036 10037 that.orientation = that.options.orientation.toLowerCase() != VERTICAL ? HORIZONTAL : VERTICAL; 10038 that._positionMouse = that.orientation == HORIZONTAL ? "pageX" : "pageY"; 10039 that._position = that.orientation == HORIZONTAL ? "left" : "top"; 10040 that._sizingDom = that.orientation == HORIZONTAL ? "outerWidth" : "outerHeight"; 10041 10042 new ui.Draggable(element, { 10043 distance: 0, 10044 filter: options.handle, 10045 drag: proxy(that._resize, that), 10046 dragstart: proxy(that._start, that), 10047 dragend: proxy(that._stop, that) 10048 }); 10049 }, 10050 10051 events: [ 10052 RESIZE, 10053 RESIZEEND, 10054 START 10055 ], 10056 10057 options: { 10058 name: "Resizable", 10059 orientation: HORIZONTAL 10060 }, 10061 _max: function(e) { 10062 var that = this, 10063 hintSize = that.hint ? that.hint[that._sizingDom]() : 0, 10064 size = that.options.max; 10065 10066 return isFunction(size) ? size(e) : size !== undefined ? (that._initialElementPosition + size) - hintSize : size; 10067 }, 10068 _min: function(e) { 10069 var that = this, 10070 size = that.options.min; 10071 10072 return isFunction(size) ? size(e) : size !== undefined ? that._initialElementPosition + size : size; 10073 }, 10074 _start: function(e) { 10075 var that = this, 10076 hint = that.options.hint, 10077 el = $(e.currentTarget); 10078 10079 that._initialMousePosition = e[that._positionMouse]; 10080 that._initialElementPosition = el.position()[that._position]; 10081 10082 if (hint) { 10083 that.hint = isFunction(hint) ? $(hint(el)) : hint; 10084 10085 that.hint.css({ 10086 position: "absolute" 10087 }) 10088 .css(that._position, that._initialElementPosition) 10089 .appendTo(that.element); 10090 } 10091 10092 that.trigger(START, e); 10093 10094 that._maxPosition = that._max(e); 10095 that._minPosition = that._min(e); 10096 10097 $(document.body).css("cursor", el.css("cursor")); 10098 }, 10099 _resize: function(e) { 10100 var that = this, 10101 handle = $(e.currentTarget), 10102 maxPosition = that._maxPosition, 10103 minPosition = that._minPosition, 10104 currentPosition = that._initialElementPosition + (e[that._positionMouse] - that._initialMousePosition), 10105 position; 10106 10107 position = minPosition !== undefined ? Math.max(minPosition, currentPosition) : currentPosition; 10108 that.position = position = maxPosition !== undefined ? Math.min(maxPosition, position) : position; 10109 10110 if(that.hint) { 10111 that.hint.toggleClass(that.options.invalidClass || "", position == maxPosition || position == minPosition) 10112 .css(that._position, position); 10113 } 10114 10115 that.trigger(RESIZE, extend(e, { position: position })); 10116 }, 10117 _stop: function(e) { 10118 var that = this; 10119 10120 if(that.hint) { 10121 that.hint.remove(); 10122 } 10123 10124 that.trigger(RESIZEEND, extend(e, { position: that.position })); 10125 $(document.body).css("cursor", ""); 10126 } 10127 });
10128 10129 kendo.ui.plugin(Resizable); 10130 10131})(jQuery); 10132(function($, undefined) { 10133 var kendo = window.kendo, 10134 proxy = $.proxy, 10135 DIR = "dir", 10136 ASC = "asc", 10137 SINGLE = "single", 10138 FIELD = "field", 10139 DESC = "desc", 10140 TLINK = ".k-link", 10141 Widget = kendo.ui.Widget; 10142 10143 var Sortable = Widget.extend({ 10144 init: function(element, options) { 10145 var that = this, link; 10146 10147 Widget.fn.init.call(that, element, options); 10148 10149 that.dataSource = that.options.dataSource.bind("change", proxy(that.refresh, that)); 10150 link = that.element.find(TLINK); 10151 10152 if (!link[0]) { 10153 link = that.element.wrapInner('<a class="k-link" href="#"/>').find(TLINK); 10154 } 10155 10156 that.link = link; 10157 that.element.click(proxy(that._click, that)); 10158 }, 10159 10160 options: { 10161 name: "Sortable", 10162 mode: SINGLE, 10163 allowUnsort: true 10164 }, 10165 10166 refresh: function() { 10167 var that = this, 10168 sort = that.dataSource.sort() || [], 10169 idx, 10170 length, 10171 descriptor, 10172 dir, 10173 element = that.element, 10174 field = element.data(FIELD); 10175 10176 element.removeData(DIR); 10177 10178 for (idx = 0, length = sort.length; idx < length; idx++) { 10179 descriptor = sort[idx]; 10180 10181 if (field == descriptor.field) { 10182 element.data(DIR, descriptor.dir); 10183 } 10184 } 10185 10186 dir = element.data(DIR); 10187 10188 element.find(".k-arrow-up,.k-arrow-down").remove(); 10189 10190 if (dir === ASC) { 10191 $('<span class="k-icon k-arrow-up" />').appendTo(that.link); 10192 } else if (dir === DESC) { 10193 $('<span class="k-icon k-arrow-down" />').appendTo(that.link); 10194 } 10195 }, 10196 10197 _click: function(e) { 10198 var that = this, 10199 element = that.element, 10200 field = element.data(FIELD), 10201 dir = element.data(DIR), 10202 options = that.options, 10203 sort = that.dataSource.sort() || [], 10204 idx, 10205 length; 10206 10207 if (dir === ASC) { 10208 dir = DESC; 10209 } else if (dir === DESC && options.allowUnsort) { 10210 dir = undefined; 10211 } else { 10212 dir = ASC; 10213 } 10214 10215 if (options.mode === SINGLE) { 10216 sort = [ { field: field, dir: dir } ]; 10217 } else if (options.mode === "multiple") { 10218 for (idx = 0, length = sort.length; idx < length; idx++) { 10219 if (sort[idx].field === field) { 10220 sort.splice(idx, 1); 10221 break; 10222 } 10223 } 10224 sort.push({ field: field, dir: dir }); 10225 } 10226 10227 e.preventDefault(); 10228 10229 that.dataSource.sort(sort); 10230 } 10231 }); 10232 10233 kendo.ui.plugin(Sortable); 10234})(jQuery); 10235(function ($, undefined) { 10236 var kendo = window.kendo, 10237 touch = kendo.support.touch, 10238 Widget = kendo.ui.Widget, 10239 proxy = $.proxy, 10240 MOUSEUP = touch? "touchend" : "mouseup", 10241 MOUSEDOWN = touch? "touchstart" : "mousedown", 10242 MOUSEMOVE = touch? "touchmove" : "mousemove", 10243 SELECTED = "k-state-selected", 10244 ACTIVE = "k-state-selecting", 10245 SELECTABLE = "k-selectable", 10246 SELECTSTART = "selectstart", 10247 DOCUMENT = $(document), 10248 CHANGE = "change", 10249 UNSELECTING = "k-state-unselecting"; 10250 10251 var Selectable = Widget.extend({ 10252 init: function(element, options) { 10253 var that = this; 10254 10255 Widget.fn.init.call(that, element, options); 10256 10257 that._marquee = $("<div class='k-marquee'></div>"); 10258 that._lastActive = null; 10259 10260 that._moveDelegate = proxy(that._move, that); 10261 that._upDelegate = proxy(that._up, that); 10262 10263 that.element.addClass(SELECTABLE); 10264 that.element.delegate("." + SELECTABLE + " " + that.options.filter, MOUSEDOWN, proxy(that._down, that)); 10265 }, 10266 10267 events: [CHANGE], 10268 10269 options: { 10270 name: "Selectable", 10271 filter: ">*", 10272 multiple: false 10273 }, 10274 _collide: function(element, marqueePos) { 10275 var pos = element.offset(), 10276 selectee = { 10277 left: pos.left, 10278 top: pos.top, 10279 right: pos.left + element.outerWidth(), 10280 bottom: pos.top + element.outerHeight() 10281 }; 10282 10283 return (!(selectee.left > marqueePos.right || 10284 selectee.right < marqueePos.left || 10285 selectee.top > marqueePos.bottom || 10286 selectee.bottom < marqueePos.top)); 10287 }, 10288 _position: function(event) { 10289 var pos = this._originalPosition, 10290 left = pos.x, 10291 top = pos.y, 10292 right = event.pageX, 10293 bottom = event.pageY, 10294 tmp; 10295 10296 if (left > right) { 10297 tmp = right; 10298 right = left; 10299 left = tmp; 10300 } 10301 10302 if (top > bottom) { 10303 tmp = bottom; 10304 bottom = top; 10305 top = tmp; 10306 } 10307 10308 return { 10309 top: top, 10310 right: right, 10311 left: left, 10312 bottom: bottom 10313 }; 10314 }, 10315 _down: function (event) { 10316 var that = this, 10317 selected, 10318 ctrlKey = event.ctrlKey,
10319 shiftKey = event.shiftKey, 10320 single = !that.options.multiple; 10321 10322 that._downTarget = $(event.currentTarget); 10323 that._shiftPressed = shiftKey; 10324 10325 if (that._downTarget.closest("." + SELECTABLE)[0] !== that.element[0]) { 10326 return; 10327 } 10328 10329 DOCUMENT 10330 .unbind(MOUSEUP, that._upDelegate) // more cancel friendly 10331 .bind(MOUSEUP, that._upDelegate); 10332 10333 that._originalPosition = { 10334 x: event.pageX, 10335 y: event.pageY 10336 }; 10337 10338 if(!single && $(event.target).is(":not(:input)")) { 10339 DOCUMENT 10340 .unbind(MOUSEMOVE, that._moveDelegate) 10341 .bind(MOUSEMOVE, that._moveDelegate) 10342 .unbind(SELECTSTART, false) 10343 .bind(SELECTSTART, false); 10344 10345 if (!kendo.support.touch) { 10346 event.preventDefault(); 10347 } 10348 } 10349 10350 if (!single) { 10351 $("body").append(that._marquee); 10352 that._marquee.css({ 10353 left: event.clientX + 1, 10354 top: event.clientY + 1, 10355 width: 0, 10356 height: 0 10357 }); 10358 } 10359 10360 selected = that._downTarget.hasClass(SELECTED); 10361 if(single || !(ctrlKey || shiftKey)) { 10362 that.element 10363 .find(that.options.filter + "." + SELECTED) 10364 .not(that._downTarget) 10365 .removeClass(SELECTED); 10366 } 10367 if(ctrlKey) { 10368 that._lastActive = that._downTarget; 10369 } 10370 10371 if(selected && (ctrlKey || shiftKey)) { 10372 that._downTarget.addClass(SELECTED); 10373 if(!shiftKey) { 10374 that._downTarget.addClass(UNSELECTING); 10375 } 10376 } 10377 else { 10378 if (!(kendo.support.touch && single)) { 10379 that._downTarget.addClass(ACTIVE); 10380 } 10381 } 10382 }, 10383 _move: function (event) { 10384 var that = this, 10385 pos = that._position(event), 10386 ctrlKey = event.ctrlKey, 10387 selectee, collide; 10388 10389 that._marquee.css({ 10390 left: pos.left, 10391 top: pos.top, 10392 width: pos.right - pos.left, 10393 height: pos.bottom - pos.top 10394 }); 10395 10396 that.element.find(that.options.filter).each(function () { 10397 selectee = $(this); 10398 collide = that._collide(selectee, pos); 10399 10400 if (collide) { 10401 if(selectee.hasClass(SELECTED)) { 10402 if(that._downTarget[0] !== selectee[0] && ctrlKey) { 10403 selectee 10404 .removeClass(SELECTED) 10405 .addClass(UNSELECTING); 10406 } 10407 } else if (!selectee.hasClass(ACTIVE) && !selectee.hasClass(UNSELECTING)) { 10408 selectee.addClass(ACTIVE); 10409 } 10410 } 10411 else { 10412 if (selectee.hasClass(ACTIVE)) { 10413 selectee.removeClass(ACTIVE); 10414 } 10415 else if(ctrlKey && selectee.hasClass(UNSELECTING)) { 10416 selectee 10417 .removeClass(UNSELECTING) 10418 .addClass(SELECTED); 10419 } 10420 } 10421 }); 10422 }, 10423 _up: function (event) { 10424 var that = this, 10425 options = that.options, 10426 single = !options.multiple; 10427 10428 DOCUMENT 10429 .unbind(SELECTSTART, false) 10430 .unbind(MOUSEMOVE, that._moveDelegate) 10431 .unbind(MOUSEUP, that._upDelegate); 10432 10433 if (!single) { 10434 that._marquee.remove(); 10435 } 10436 10437 if (kendo.support.touch && single) { 10438 that._downTarget.addClass(ACTIVE); 10439 } 10440 10441 if(!single && that._shiftPressed === true) { 10442 that.selectRange(that._firstSelectee(), that._downTarget); 10443 } 10444 else { 10445 that.element 10446 .find(options.filter + "." + UNSELECTING) 10447 .removeClass(UNSELECTING) 10448 .removeClass(SELECTED); 10449 10450 that.value(that.element.find(options.filter + "." + ACTIVE)); 10451 } 10452 if(!that._shiftPressed) { 10453 that._lastActive = that._downTarget; 10454 } 10455 that._downTarget = null; 10456 that._shiftPressed = false; 10457 }, 10458 value: function(val) { 10459 var that = this, 10460 selectElement = proxy(that._selectElement, that); 10461 10462 if(val) { 10463 val.each(function() { 10464 selectElement(this); 10465 }); 10466 10467 that.trigger(CHANGE, {}); 10468 return; 10469 } 10470 10471 return that.element 10472 .find(that.options.filter + "." + SELECTED); 10473 }, 10474 _firstSelectee: function() { 10475 var that = this, selected; 10476 if(that._lastActive !== null) { 10477 return that._lastActive; 10478 } 10479 10480 selected = that.value(); 10481 return selected.length > 0 ? 10482 selected[0] : 10483 that.element.find(that.options.filter); 10484 }, 10485 _selectElement: function(el) { 10486 var selecee = $(el), 10487 isPrevented = this.trigger("select", { element: el });
10488 10489 selecee.removeClass(ACTIVE); 10490 if(!isPrevented) { 10491 selecee.addClass(SELECTED); 10492 } 10493 }, 10494 clear: function() { 10495 var that = this; 10496 that.element 10497 .find(that.options.filter + "." + SELECTED) 10498 .removeClass(SELECTED); 10499 }, 10500 selectRange: function(start, end) { 10501 var that = this, 10502 found = false, 10503 selectElement = proxy(that._selectElement, that), 10504 selectee; 10505 start = $(start)[0]; 10506 end = $(end)[0]; 10507 that.element.find(that.options.filter).each(function () { 10508 selectee = $(this); 10509 if(found) { 10510 selectElement(this); 10511 found = this !== end; 10512 } 10513 else if(this === start) { 10514 found = start !== end; 10515 selectElement(this); 10516 } 10517 else if(this === end) { 10518 var tmp = start; 10519 start = end; 10520 end = tmp; 10521 found = true; 10522 selectElement(this); 10523 } 10524 else { 10525 selectee.removeClass(SELECTED); 10526 } 10527 }); 10528 that.trigger(CHANGE, {}); 10529 } 10530 }); 10531 10532 kendo.ui.plugin(Selectable); 10533 10534})(jQuery); 10535(function($, undefined) { 10536 var kendo = window.kendo, 10537 ui = kendo.ui, 10538 Widget = ui.Widget, 10539 proxy = $.proxy; 10540 10541 function button(template, idx, text, numeric) { 10542 return template( { 10543 idx: idx, 10544 text: text, 10545 ns: kendo.ns, 10546 numeric: numeric 10547 }); 10548 } 10549 10550 var Pager = Widget.extend( { 10551 init: function(element, options) { 10552 var that = this; 10553 10554 Widget.fn.init.call(that, element, options); 10555 10556 options = that.options; 10557 that.dataSource = options.dataSource; 10558 that.linkTemplate = kendo.template(that.options.linkTemplate); 10559 that.selectTemplate = kendo.template(that.options.selectTemplate); 10560 10561 that._refreshHandler = proxy(that.refresh, that); 10562 10563 that.dataSource.bind("change", that._refreshHandler); 10564 10565 that.list = $('<ul class="k-pager k-reset k-numeric" />').appendTo(that.element);//.html(that.selectTemplate({ text: 1 })); 10566 that._clickHandler = proxy(that._click, that); 10567 10568 that.element.delegate("a", "click", that._clickHandler); 10569 10570 that.refresh(); 10571 }, 10572 10573 destroy: function() { 10574 var that = this; 10575 10576 that.element.undelegate("a", "click", that._clickHandler); 10577 that.dataSource.unbind("change", that._refreshHandler); 10578 }, 10579 10580 events: [ 10581 "change" 10582 ], 10583 10584 options: { 10585 name: "Pager", 10586 selectTemplate: '<li><span class="k-state-active">#=text#</span></li>', 10587 linkTemplate: '<li><a href="\\#" class="k-link" data-#=ns#page="#=idx#">#=text#</a></li>', 10588 buttonCount: 10 10589 }, 10590 10591 refresh: function() { 10592 var that = this, 10593 idx, 10594 end, 10595 start = 1, 10596 html = "", 10597 reminder, 10598 page = that.page(), 10599 totalPages = that.totalPages(), 10600 linkTemplate = that.linkTemplate, 10601 buttonCount = that.options.buttonCount; 10602 10603 if (page > buttonCount) { 10604 reminder = (page % buttonCount); 10605 10606 start = (reminder === 0) ? (page - buttonCount) + 1 : (page - reminder) + 1; 10607 } 10608 10609 end = Math.min((start + buttonCount) - 1, totalPages); 10610 10611 if(start > 1) { 10612 html += button(linkTemplate, start - 1, "...", false); 10613 } 10614 10615 for(idx = start; idx <= end; idx++) { 10616 html += button(idx == page ? that.selectTemplate : linkTemplate, idx, idx, true); 10617 } 10618 10619 if(end < totalPages) { 10620 html += button(linkTemplate, idx, "...", false); 10621 } 10622 10623 if (html === "") { 10624 html = that.selectTemplate({ text: 1 }); 10625 } 10626 10627 that.list.empty().append(html); 10628 }, 10629 10630 _click: function(e) { 10631 var page = $(e.currentTarget).attr(kendo.attr("page")); 10632 e.preventDefault(); 10633 10634 this.dataSource.page(page); 10635 10636 this.trigger("change", { index: page });
10637 }, 10638 10639 totalPages: function() { 10640 return Math.ceil((this.dataSource.total() || 0) / this.pageSize()); 10641 }, 10642 10643 pageSize: function() { 10644 return this.dataSource.pageSize() || this.dataSource.total(); 10645 }, 10646 10647 page: function() { 10648 return this.dataSource.page() || 1; 10649 } 10650 }); 10651 10652 ui.plugin(Pager); 10653})(jQuery); 10654(function($, undefined) { 10655 var kendo = window.kendo, 10656 ui = kendo.ui, 10657 Widget = ui.Widget, 10658 support = kendo.support, 10659 touch = support.touch, 10660 getOffset = kendo.getOffset, 10661 appendingToBodyTriggersResize = $.browser.msie && $.browser.version < 9, 10662 OPEN = "open", 10663 CLOSE = "close", 10664 CLOSED = "closed", 10665 CENTER = "center", 10666 LEFT = "left", 10667 RIGHT = "right", 10668 TOP = "top", 10669 BOTTOM = "bottom", 10670 ABSOLUTE = "absolute", 10671 HIDDEN = "hidden", 10672 BODY = "body", 10673 LOCATION = "location", 10674 POSITION = "position", 10675 VISIBLE = "visible", 10676 FITTED = "fitted", 10677 EFFECTS = "effects", 10678 ACTIVE = "k-state-active", 10679 ACTIVEBORDER = "k-state-border", 10680 ACTIVECHILDREN = ".k-picker-wrap, .k-dropdown-wrap, .k-link", 10681 MOUSEDOWN = touch ? "touchstart" : "mousedown", 10682 DOCUMENT= $(document), 10683 WINDOW = $(window), 10684 DOCUMENT_ELEMENT = $(document.documentElement), 10685 RESIZE_SCROLL = "resize scroll", 10686 cssPrefix = support.transitions.css, 10687 TRANSFORM = cssPrefix + "transform", 10688 extend = $.extend, 10689 styles = ["font-family", 10690 "font-size", 10691 "font-stretch", 10692 "font-style", 10693 "font-weight", 10694 "line-height"]; 10695 10696 function contains(container, target) { 10697 return container === target || $.contains(container, target); 10698 } 10699 10700 var Popup = Widget.extend({ 10701 init: function(element, options) { 10702 var that = this; 10703 10704 Widget.fn.init.call(that, element, options); 10705 10706 element = that.element; 10707 options = that.options; 10708 10709 that.collisions = options.collision ? options.collision.split(" ") : []; 10710 10711 if (that.collisions.length === 1) { 10712 that.collisions.push(that.collisions[0]); 10713 } 10714 10715 that.element.hide() 10716 .addClass("k-popup k-group k-reset") 10717 .css({ position : ABSOLUTE }) 10718 .appendTo($(options.appendTo)) 10719 .bind("mouseenter mouseleave", function(e) { 10720 that._hovered = e.type === "mouseenter"; 10721 }); 10722 10723 that.wrapper = $(); 10724 10725 if (options.animation === false) { 10726 options.animation = { open: { show: true, effects: {} }, close: { hide: true, effects: {} } }; 10727 } 10728 10729 extend(options.animation.open, { 10730 complete: function() { 10731 that.wrapper.css({ overflow: VISIBLE }); // Forcing refresh causes flickering in mobile. 10732 } 10733 }); 10734 10735 extend(options.animation.close, { 10736 complete: function() { 10737 that.wrapper.hide(); 10738 10739 var location = that.wrapper.data(LOCATION), 10740 anchor = $(options.anchor), 10741 direction, dirClass; 10742 10743 if (location) { 10744 that.wrapper.css(location); 10745 } 10746 10747 if (options.anchor != BODY) { 10748 direction = anchor.hasClass(ACTIVEBORDER + "-down") ? "down" : "up"; 10749 dirClass = ACTIVEBORDER + "-" + direction; 10750 10751 anchor 10752 .removeClass(dirClass) 10753 .children(ACTIVECHILDREN) 10754 .removeClass(ACTIVE) 10755 .removeClass(dirClass); 10756 10757 element.removeClass(ACTIVEBORDER + "-" + kendo.directions[direction].reverse); 10758 } 10759 10760 that._closing = false; 10761 that.trigger(CLOSED); 10762 } 10763 }); 10764 10765 that._mousedownProxy = function(e) { 10766 that._mousedown(e); 10767 }; 10768 10769 that._currentWidth = DOCUMENT.width(); 10770 10771 that._resizeProxy = function(e) { 10772 that._resize(e); 10773 }; 10774 10775 if (options.toggleTarget) { 10776 $(options.toggleTarget).bind(options.toggleEvent, $.proxy(that.toggle, that)); 10777 } 10778 }, 10779 10780 events: [ 10781 OPEN, 10782 CLOSE, 10783 CLOSED 10784 ], 10785 10786 options: { 10787 name: "Popup", 10788 toggleEvent: "click", 10789 origin: BOTTOM + " " + LEFT, 10790 position: TOP + " " + LEFT, 10791 anchor: BODY, 10792 appendTo: BODY, 10793 collision: "flip fit", 10794 animation: { 10795 open: { 10796 effects: "slideIn:down", 10797 transition: true, 10798 duration: 200, 10799 show: true 10800 }, 10801 close: { // if close animation effects are defined, they will be used instead of open.reverse 10802 duration: 100, 10803 show: false, 10804 hide: true 10805 } 10806 } 10807 }, 10808 10809 open: function(x, y) { 10810 var that = this, 10811 fixed = { isFixed: !isNaN(parseInt(y,10)), x: x, y: y }, 10812 element = that.element, 10813 options = that.options, 10814 direction = "down", 10815 animation, wrapper, 10816 anchor = $(options.anchor), 10817 style, 10818 idx; 10819 10820 if (!that.visible()) { 10821 for (idx = 0; idx < styles.length; idx++) { 10822 style = styles[idx];
10823 element.css(style, anchor.css(style)); 10824 } 10825 10826 if (element.data("animating") || that.trigger(OPEN)) { 10827 return; 10828 } 10829 10830 DOCUMENT_ELEMENT.unbind(MOUSEDOWN, that._mousedownProxy) 10831 .bind(MOUSEDOWN, that._mousedownProxy); 10832 if (!touch) { 10833 WINDOW.unbind(RESIZE_SCROLL, that._resizeProxy) 10834 .bind(RESIZE_SCROLL, that._resizeProxy); 10835 } 10836 10837 that.wrapper = wrapper = kendo.wrap(element) 10838 .css({ 10839 overflow: HIDDEN, 10840 display: "block", 10841 position: ABSOLUTE 10842 }); 10843 10844 if (support.mobileOS.android) { 10845 wrapper.add(anchor).css(TRANSFORM, "translatez(0)"); // Android is VERY slow otherwise. Should be tested in other droids as well since it may cause blur. 10846 } 10847 10848 wrapper.css(POSITION); 10849 10850 if (options.appendTo == BODY) { 10851 wrapper.css(TOP, "-10000px"); 10852 } 10853 10854 animation = extend(true, {}, options.animation.open); 10855 animation.effects = kendo.parseEffects(animation.effects, that._position(window, fixed)); 10856 direction = animation.effects.slideIn ? animation.effects.slideIn.direction : direction; 10857 10858 if (options.anchor != BODY) { 10859 var dirClass = ACTIVEBORDER + "-" + direction; 10860 10861 element.addClass(ACTIVEBORDER + "-" + kendo.directions[direction].reverse); 10862 10863 anchor 10864 .addClass(dirClass) 10865 .children(ACTIVECHILDREN) 10866 .addClass(ACTIVE) 10867 .addClass(dirClass); 10868 } 10869 10870 element.data(EFFECTS, animation.effects) 10871 .kendoStop(true) 10872 .kendoAnimate(animation); 10873 } 10874 }, 10875 10876 toggle: function() { 10877 var that = this; 10878 10879 that[that.visible() ? CLOSE : OPEN](); 10880 }, 10881 10882 visible: function() { 10883 return this.element.is(":" + VISIBLE); 10884 }, 10885 10886 close: function() { 10887 var that = this, 10888 options = that.options, 10889 animation, openEffects, closeEffects; 10890 10891 if (that.visible()) { 10892 if (that._closing || that.trigger(CLOSE)) { 10893 return; 10894 } 10895 10896 DOCUMENT_ELEMENT.unbind(MOUSEDOWN, that._mousedownProxy); 10897 WINDOW.unbind(RESIZE_SCROLL, that._resizeProxy); 10898 10899 animation = extend(true, {}, options.animation.close); 10900 openEffects = that.element.data(EFFECTS); 10901 closeEffects = animation.effects; 10902 10903 that.wrapper = kendo.wrap(that.element).css({ overflow: HIDDEN }); 10904 10905 if (!closeEffects && !kendo.size(closeEffects) && openEffects && kendo.size(openEffects)) { 10906 animation.effects = openEffects; 10907 animation.reverse = true; 10908 } 10909 10910 that._closing = true; 10911 10912 that.element.kendoStop(true).kendoAnimate(animation); 10913 } 10914 }, 10915 10916 _resize: function(e) { 10917 var that = this; 10918 10919 if (appendingToBodyTriggersResize) { 10920 var width = DOCUMENT.width(); 10921 if (width == that._currentWidth) { 10922 return; 10923 } 10924 that._currentWidth = width; 10925 } 10926 10927 if (!that._hovered) { 10928 that.close(); 10929 } 10930 }, 10931 10932 _mousedown: function(e) { 10933 var that = this, 10934 container = that.element[0], 10935 options = that.options, 10936 anchor = $(options.anchor)[0], 10937 toggleTarget = options.toggleTarget, 10938 target = kendo.eventTarget(e), 10939 popup = $(target).closest(".k-popup")[0]; 10940 10941 if (popup && popup !== that.element[0] ){ 10942 return; 10943 } 10944 10945 if (!contains(container, target) && !contains(anchor, target) && !(toggleTarget && contains($(toggleTarget)[0], target))) { 10946 that.close(); 10947 } 10948 }, 10949 10950 _fit: function(position, size, viewPortSize) { 10951 var output = 0; 10952 10953 if (position + size > viewPortSize) { 10954 output = viewPortSize - (position + size); 10955 } 10956 10957 if (position < 0) { 10958 output = -position; 10959 } 10960 10961 return output; 10962 }, 10963 10964 _flip: function(offset, size, anchorSize, viewPortSize, origin, position, boxSize) { 10965 var output = 0; 10966 boxSize = boxSize || size; 10967 10968 if (position !== origin && position !== CENTER && origin !== CENTER) { 10969 if (offset + boxSize > viewPortSize) { 10970 output += -(anchorSize + size); 10971 } 10972 10973 if (offset + output < 0) { 10974 output += anchorSize + size; 10975 } 10976 } 10977 return output; 10978 }, 10979 10980 _position: function(viewport, fixed) { 10981 viewport = $(viewport); 10982 10983 var that = this, 10984 element = that.element, 10985 wrapper = that.wrapper, 10986 options = that.options, 10987 anchor = $(options.anchor), 10988 origins = options.origin.toLowerCase().split(" "), 10989 positions = options.position.toLowerCase().split(" "), 10990 collisions = that.collisions, 10991 zoomLevel = support.zoomLevel(), 10992 zIndex = 10002; 10993 10994 var siblingContainer = anchor.parents().filter(wrapper.siblings()); 10995 10996 if (siblingContainer[0]) { 10997 var parentZIndex = Number($(siblingContainer).css("zIndex")); 10998 if (parentZIndex) { 10999 zIndex = parentZIndex + 1; 11000 } 11001 } 11002 11003 wrapper.css("zIndex", zIndex); 11004 11005 if (fixed && fixed.isFixed) { 11006 wrapper.css({ left: fixed.x, top: fixed.y }); 11007 } else { 11008 wrapper.css(that._align(origins, positions)); 11009 } 11010
11011 var pos = getOffset(wrapper, POSITION), 11012 offset = getOffset(wrapper), 11013 anchorParent = anchor.offsetParent().parent(".k-animation-container"); // If the parent is positioned, get the current positions 11014 11015 if (anchorParent.length && anchorParent.data(FITTED)) { 11016 pos = getOffset(wrapper, POSITION); 11017 offset = getOffset(wrapper); 11018 } 11019 11020 offset = { 11021 top: offset.top - (window.pageYOffset || document.documentElement.scrollTop || 0), 11022 left: offset.left - (window.pageXOffset || document.documentElement.scrollLeft || 0) 11023 }; 11024 11025 if (!that.wrapper.data(LOCATION)) { // Needed to reset the popup location after every closure - fixes the resize bugs. 11026 wrapper.data(LOCATION, extend({}, pos)); 11027 } 11028 11029 var offsets = extend({}, offset), 11030 location = extend({}, pos); 11031 11032 if (collisions[0] === "fit") { 11033 location.top += that._fit(offsets.top, wrapper.outerHeight(), viewport.height() / zoomLevel); 11034 } 11035 11036 if (collisions[1] === "fit") { 11037 location.left += that._fit(offsets.left, wrapper.outerWidth(), viewport.width() / zoomLevel); 11038 } 11039 11040 if (location.left != pos.left || location.top != pos.top) { 11041 wrapper.data(FITTED, true); 11042 } else { 11043 wrapper.removeData(FITTED); 11044 } 11045 11046 var flipPos = extend({}, location); 11047 11048 if (collisions[0] === "flip") { 11049 location.top += that._flip(offsets.top, element.outerHeight(), anchor.outerHeight(), viewport.height() / zoomLevel, origins[0], positions[0], wrapper.outerHeight()); 11050 } 11051 11052 if (collisions[1] === "flip") { 11053 location.left += that._flip(offsets.left, element.outerWidth(), anchor.outerWidth(), viewport.width() / zoomLevel, origins[1], positions[1], wrapper.outerWidth()); 11054 } 11055 11056 wrapper.css(location); 11057 11058 return (location.left != flipPos.left || location.top != flipPos.top); 11059 }, 11060 11061 _align: function(origin, position) { 11062 var that = this, 11063 element = that.wrapper, 11064 anchor = $(that.options.anchor), 11065 verticalOrigin = origin[0], 11066 horizontalOrigin = origin[1], 11067 verticalPosition = position[0], 11068 horizontalPosition = position[1], 11069 anchorOffset = getOffset(anchor), 11070 appendTo = $(that.options.appendTo), 11071 appendToOffset, 11072 width = element.outerWidth(), 11073 height = element.outerHeight(), 11074 anchorWidth = anchor.outerWidth(), 11075 anchorHeight = anchor.outerHeight(), 11076 top = anchorOffset.top, 11077 left = anchorOffset.left, 11078 round = Math.round; 11079 11080 if (appendTo[0] != document.body) { 11081 appendToOffset = getOffset(appendTo); 11082 top -= appendToOffset.top; 11083 left -= appendToOffset.left; 11084 } 11085 11086 11087 if (verticalOrigin === BOTTOM) { 11088 top += anchorHeight; 11089 } 11090 11091 if (verticalOrigin === CENTER) { 11092 top += round(anchorHeight / 2); 11093 } 11094 11095 if (verticalPosition === BOTTOM) { 11096 top -= height; 11097 } 11098 11099 if (verticalPosition === CENTER) { 11100 top -= round(height / 2); 11101 } 11102 11103 if (horizontalOrigin === RIGHT) { 11104 left += anchorWidth; 11105 } 11106 11107 if (horizontalOrigin === CENTER) { 11108 left += round(anchorWidth / 2); 11109 } 11110 11111 if (horizontalPosition === RIGHT) { 11112 left -= width; 11113 } 11114 11115 if (horizontalPosition === CENTER) { 11116 left -= round(width / 2); 11117 } 11118 11119 return { 11120 top: top, 11121 left: left 11122 }; 11123 } 11124 });
11125 11126 ui.plugin(Popup); 11127})(jQuery); 11128(function($, undefined) { 11129 /** 11130 * @name kendo.ui.List.Description 11131 * 11132 * @section Common class for ComboBox, DropDownList and AutoComplete widgets. 11133 */ 11134 var kendo = window.kendo, 11135 ui = kendo.ui, 11136 Widget = ui.Widget, 11137 keys = kendo.keys, 11138 touch = kendo.support.touch, 11139 ID = "id", 11140 LI = "li", 11141 CLICK = touch ? "touchend" : "click", 11142 CHANGE = "change", 11143 CHARACTER = "character", 11144 FOCUSED = "k-state-focused", 11145 HOVER = "k-state-hover", 11146 LOADING = "k-loading", 11147 OPEN = "open", 11148 CLOSE = "close", 11149 SELECT = "select", 11150 extend = $.extend, 11151 proxy = $.proxy, 11152 isIE8 = $.browser.msie && parseInt($.browser.version, 10) < 9, 11153 quotRegExp = /"/g; 11154 11155 var List = Widget.extend(/** @lends kendo.ui.List */{ 11156 /** 11157 * Creates a List instance. 11158 * @constructs 11159 * @extends kendo.ui.Widget 11160 */ 11161 init: function(element, options) { 11162 var that = this, id, list; 11163 11164 Widget.fn.init.call(that, element, options); 11165 11166 that._template(); 11167 11168 that.ul = $('<ul unselectable="on" class="k-list k-reset"/>') 11169 .css({ overflow: kendo.support.touch ? "": "auto" }) 11170 .delegate(LI, "mouseenter", function() { $(this).addClass(HOVER); }) 11171 .delegate(LI, "mouseleave", function() { $(this).removeClass(HOVER); }) 11172 .delegate(LI, CLICK, proxy(that._click, that)); 11173 11174 that.list = list = $("<div class='k-list-container'/>") 11175 .append(that.ul) 11176 .mousedown(function(e) { 11177 e.preventDefault(); 11178 }); 11179 11180 id = that.element.attr(ID); 11181 if (id) { 11182 list.attr(ID, id + "-list"); 11183 } 11184 11185 $(document.documentElement).bind("mousedown", proxy(that._mousedown, that)); 11186 }, 11187 11188 items: function() { 11189 return this.ul[0].children; 11190 }, 11191 11192 current: function(candidate) { 11193 var that = this; 11194 11195 if (candidate !== undefined) { 11196 if (that._current) { 11197 that._current.removeClass(FOCUSED); 11198 } 11199 11200 if (candidate) { 11201 candidate.addClass(FOCUSED); 11202 that._scroll(candidate); 11203 } 11204 11205 that._current = candidate; 11206 } else { 11207 return that._current; 11208 } 11209 }, 11210 11211 dataItem: function(index) { 11212 var that = this; 11213 11214 if (index === undefined) { 11215 index = that.selectedIndex; 11216 } 11217 11218 return that._data()[index]; 11219 }, 11220 11221 _accessors: function() { 11222 var that = this, 11223 element = that.element, 11224 options = that.options, 11225 getter = kendo.getter, 11226 textField = element.attr(kendo.attr("text-field")), 11227 valueField = element.attr(kendo.attr("value-field")); 11228 11229 if (textField) { 11230 options.dataTextField = textField; 11231 } 11232 11233 if (valueField) { 11234 options.dataValueField = valueField; 11235 } 11236 11237 that._text = getter(options.dataTextField); 11238 that._value = getter(options.dataValueField); 11239 }, 11240 11241 _blur: function() { 11242 var that = this; 11243 11244 that._change(); 11245 that.close(); 11246 }, 11247 11248 _change: function() { 11249 var that = this, 11250 index = that.selectedIndex, 11251 value = that.value(), 11252 trigger; 11253 11254 if (value !== that._old) { 11255 trigger = true; 11256 } else if (index !== undefined && index !== that._oldIndex) { 11257 trigger = true; 11258 } 11259 11260 if (trigger) { 11261 that._old = value; 11262 that._oldIndex = index; 11263 11264 that.trigger(CHANGE); 11265 11266 // trigger the DOM change event so any subscriber gets notified 11267 that.element.trigger(CHANGE); 11268 } 11269 }, 11270 11271 _click: function(e) { 11272 if (!e.isDefaultPrevented()) { 11273 this._accept($(e.currentTarget)); 11274 } 11275 }, 11276 11277 _data: function() { 11278 return this.dataSource.view(); 11279 }, 11280 11281 _enable: function() { 11282 var that = this, 11283 options = that.options; 11284 11285 if (that.element.prop("disabled")) { 11286 options.enable = false; 11287 } 11288 11289 that.enable(options.enable); 11290 }, 11291 11292 _focus: function(li) { 11293 var that = this; 11294 11295 if (that.popup.visible() && that.trigger(SELECT, {item: li})) { 11296 that.close(); 11297 return; 11298 } 11299 11300 that._select(li); 11301 that._blur(); 11302 }, 11303 11304 _height: function(length) { 11305 if (length) { 11306 var that = this, 11307 list = that.list, 11308 visible = that.popup.visible(), 11309 height = that.options.height; 11310 11311 list = list.add(list.parent(".k-animation-container")).show() 11312 .height(that.ul[0].scrollHeight > height ? height : "auto"); 11313 11314 if (!visible) { 11315 list.hide(); 11316 } 11317 } 11318 }, 11319 11320 _adjustListWidth: function() { 11321 var list = this.list, 11322 width = list[0].style.width, 11323 wrapper = this.wrapper, 11324 computedStyle, computedWidth; 11325 11326 if (!width) { 11327 computedStyle = window.getComputedStyle ? window.getComputedStyle(wrapper[0], null) : 0; 11328 computedWidth = computedStyle ? parseFloat(computedStyle.width) : wrapper.outerWidth(); 11329 11330 if (computedStyle && ($.browser.mozilla || $.browser.msie)) { // getComputedStyle returns different box in FF and IE. 11331 computedWidth += parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight) + parseFloat(computedStyle.borderLeftWidth) + parseFloat(computedStyle.borderRightWidth);
11332 } 11333 11334 width = computedWidth - (list.outerWidth() - list.width()); 11335 } 11336 11337 list.css({ 11338 fontFamily: wrapper.css("font-family"), 11339 width: width 11340 }); 11341 11342 return true; 11343 }, 11344 11345 _popup: function() { 11346 var that = this, 11347 list = that.list, 11348 options = that.options, 11349 wrapper = that.wrapper, 11350 opened = false; 11351 11352 that.popup = new ui.Popup(list, extend({}, options.popup, { 11353 anchor: wrapper, 11354 open: function(e) { 11355 if (!opened) { 11356 opened = that._adjustListWidth(); 11357 } 11358 11359 if (that.trigger(OPEN)) { 11360 e.preventDefault(); 11361 } 11362 }, 11363 close: function(e) { 11364 if (that.trigger(CLOSE)) { 11365 e.preventDefault(); 11366 } 11367 }, 11368 animation: options.animation 11369 })); 11370 11371 that._touchScroller = kendo.touchScroller(that.popup.element); 11372 }, 11373 11374 _makeUnselectable: function(element) { 11375 if (isIE8) { 11376 this.list.find("*").attr("unselectable", "on"); 11377 } 11378 }, 11379 11380 _toggleHover: function(e) { 11381 if (!touch) { 11382 $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter"); 11383 } 11384 }, 11385 11386 _toggle: function(open) { 11387 var that = this; 11388 open = open !== undefined? open : !that.popup.visible(); 11389 11390 if (!touch && that._focused[0] !== document.activeElement) { 11391 that._focused.focus(); 11392 } 11393 11394 that[open ? OPEN : CLOSE](); 11395 }, 11396 11397 _scroll: function (item) { 11398 11399 if (!item) { 11400 return; 11401 } 11402 11403 if (item[0]) { 11404 item = item[0]; 11405 } 11406 11407 var ul = this.ul[0], 11408 itemOffsetTop = item.offsetTop, 11409 itemOffsetHeight = item.offsetHeight, 11410 ulScrollTop = ul.scrollTop, 11411 ulOffsetHeight = ul.clientHeight, 11412 bottomDistance = itemOffsetTop + itemOffsetHeight; 11413 11414 ul.scrollTop = ulScrollTop > itemOffsetTop ? 11415 itemOffsetTop : bottomDistance > (ulScrollTop + ulOffsetHeight) ? 11416 bottomDistance - ulOffsetHeight : ulScrollTop; 11417 }, 11418 11419 _template: function() { 11420 var that = this, 11421 options = that.options, 11422 template = options.template, 11423 hasDataSource = options.dataSource; 11424 11425 if (that.element.is(SELECT) && that.element[0].length) { 11426 if (!hasDataSource) { 11427 options.dataTextField = options.dataTextField || "text"; 11428 options.dataValueField = options.dataValueField || "value"; 11429 } 11430 } 11431 11432 if (!template) { 11433 that.template = kendo.template('<li unselectable="on" class="k-item">${data' + (options.dataTextField ? "." : "") + options.dataTextField + "}</li>", { useWithBlock: false }); 11434 } else { 11435 template = kendo.template(template); 11436 that.template = function(data) { 11437 return '<li unselectable="on" class="k-item">' + template(data) + "</li>"; 11438 }; 11439 } 11440 } 11441 }); 11442 11443 extend(List, { 11444 caret: function(element) { 11445 var caret, 11446 selection = element.ownerDocument.selection; 11447 11448 if (selection) { 11449 caret = Math.abs(selection.createRange().moveStart(CHARACTER, -element.value.length)); 11450 } else { 11451 caret = element.selectionStart; 11452 } 11453 11454 return caret; 11455 }, 11456 11457 selectText: function (element, selectionStart, selectionEnd) { 11458 if (element.createTextRange) { 11459 var textRange = element.createTextRange(); 11460 textRange.collapse(true); 11461 textRange.moveStart(CHARACTER, selectionStart); 11462 textRange.moveEnd(CHARACTER, selectionEnd - selectionStart); 11463 textRange.select(); 11464 } else { 11465 element.setSelectionRange(selectionStart, selectionEnd); 11466 } 11467 }, 11468 inArray: function(node, parentNode) { 11469 var idx, length, siblings = parentNode.children; 11470 11471 if (!node || node.parentNode !== parentNode) { 11472 return -1; 11473 } 11474 11475 for (idx = 0, length = siblings.length; idx < length; idx++) { 11476 if (node === siblings[idx]) { 11477 return idx; 11478 } 11479 } 11480 11481 return -1; 11482 } 11483 });
11484 11485 kendo.ui.List = List; 11486 11487 /** 11488 * @name kendo.ui.Select.Description 11489 * 11490 * @section Common class for ComboBox and DropDownList widgets. 11491 */ 11492 ui.Select = List.extend(/** @lends kendo.ui.Select */{ 11493 /** 11494 * @extends kendo.ui.List 11495 * @constructs 11496 */ 11497 init: function(element, options) { 11498 List.fn.init.call(this, element, options); 11499 }, 11500 11501 setDataSource: function(dataSource) { 11502 this.options.dataSource = dataSource; 11503 11504 this._dataSource(); 11505 11506 if (this.options.autoBind) { 11507 this._selectItem(); 11508 } 11509 }, 11510 11511 /** 11512 * Closes the drop-down list. 11513 * @example 11514 * dropdownlist.close(); 11515 * 11516 * @example 11517 * combobox.close(); 11518 */ 11519 close: function() { 11520 this.popup.close(); 11521 }, 11522 11523 _accessor: function(value, idx) { 11524 var element = this.element, 11525 isSelect = element.is(SELECT), 11526 option, selectedIndex; 11527 11528 element = element[0]; 11529 11530 if (value === undefined) { 11531 if (isSelect) { 11532 selectedIndex = element.selectedIndex; 11533 11534 if (selectedIndex > -1) { 11535 option = element.options[selectedIndex]; 11536 11537 if (option) { 11538 value = option.value; 11539 } 11540 } 11541 } else { 11542 value = element.value; 11543 } 11544 return value; 11545 } else { 11546 if (isSelect) { 11547 element.selectedIndex = idx; 11548 } else { 11549 element.value = value; 11550 } 11551 } 11552 }, 11553 11554 _hideBusy: function () { 11555 var that = this; 11556 clearTimeout(that._busy); 11557 that._arrow.removeClass(LOADING); 11558 }, 11559 11560 _showBusy: function () { 11561 var that = this; 11562 11563 if (that._busy) { 11564 return; 11565 } 11566 11567 that._busy = setTimeout(function () { 11568 that._arrow.addClass(LOADING); 11569 }, 100); 11570 }, 11571 11572 _dataSource: function() { 11573 var that = this, 11574 element = that.element, 11575 options = that.options, 11576 dataSource = options.dataSource || {}, 11577 idx; 11578 11579 dataSource = $.isArray(dataSource) ? {data: dataSource} : dataSource; 11580 11581 if (element.is(SELECT)) { 11582 idx = element[0].selectedIndex; 11583 if (idx > -1) { 11584 options.index = idx; 11585 } 11586 11587 dataSource.select = element; 11588 dataSource.fields = [{ field: options.dataTextField }, 11589 { field: options.dataValueField }]; 11590 } 11591 11592 if (that.dataSource && that._refreshHandler) { 11593 that.dataSource.unbind(CHANGE, that._refreshHandler) 11594 .unbind("requestStart", that._requestStartHandler); 11595 } else { 11596 that._refreshHandler = proxy(that.refresh, that); 11597 that._requestStartHandler = proxy(that._showBusy, that); 11598 } 11599 11600 that.dataSource = kendo.data.DataSource.create(dataSource) 11601 .bind(CHANGE, that._refreshHandler) 11602 .bind("requestStart", that._requestStartHandler); 11603 }, 11604 11605 _index: function(value) { 11606 var that = this, 11607 idx, 11608 length, 11609 data = that._data(), 11610 valueFromData; 11611 11612 for (idx = 0, length = data.length; idx < length; idx++) { 11613 valueFromData = that._value(data[idx]); 11614 11615 if (valueFromData === undefined) { 11616 valueFromData = that._text(data[idx]); 11617 } 11618 11619 if (valueFromData == value) { 11620 return idx; 11621 } 11622 } 11623 11624 return -1; 11625 }, 11626 11627 _get: function(li) { 11628 var that = this, 11629 data = that._data(), 11630 idx, length; 11631 11632 if (typeof li === "function") { 11633 for (idx = 0, length = data.length; idx < length; idx++) { 11634 if (li(data[idx])) { 11635 li = idx; 11636 break; 11637 } 11638 } 11639 } 11640
11641 if (typeof li === "number") { 11642 if (li < 0) { 11643 return $(); 11644 } 11645 11646 li = $(that.ul[0].children[li]); 11647 } 11648 11649 if (li && li.nodeType) { 11650 li = $(li); 11651 } 11652 11653 return li; 11654 }, 11655 11656 _move: function(e) { 11657 var that = this, 11658 key = e.keyCode, 11659 ul = that.ul[0], 11660 current = that._current, 11661 down = key === keys.DOWN, 11662 pressed; 11663 11664 if (key === keys.UP || down) { 11665 if (e.altKey) { 11666 that.toggle(down); 11667 } else if (down) { 11668 that._select(current ? current[0].nextSibling : ul.firstChild); 11669 e.preventDefault(); 11670 } else { 11671 that._select(current ? current[0].previousSibling : ul.lastChild); 11672 e.preventDefault(); 11673 } 11674 pressed = true; 11675 } else if (key === keys.ENTER || key === keys.TAB) { 11676 11677 if (that.popup.visible()) { 11678 e.preventDefault(); 11679 } 11680 11681 that._accept(current); 11682 pressed = true; 11683 } else if (key === keys.ESC) { 11684 that.close(); 11685 pressed = true; 11686 } 11687 11688 return pressed; 11689 }, 11690 11691 _valueOnFetch: function(value) { 11692 var that = this; 11693 11694 if (!that.ul[0].firstChild && !that._fetch) { 11695 that.dataSource.one(CHANGE, function() { 11696 that._fetch = true; 11697 that.value(value); 11698 }).fetch(); 11699 11700 return true; 11701 } 11702 11703 that._fetch = false; 11704 }, 11705 11706 _options: function(data, optionLabel) { 11707 var that = this, 11708 element = that.element, 11709 selectedIndex = element[0].selectedIndex, 11710 length = data.length, 11711 options = "", 11712 option, 11713 dataItem, 11714 dataText, 11715 dataValue, 11716 idx = 0; 11717 11718 if (optionLabel) { 11719 options = optionLabel; 11720 idx = 1; 11721 } 11722 11723 for (; idx < length; idx++) { 11724 option = "<option"; 11725 dataItem = data[idx]; 11726 dataText = that._text(dataItem); 11727 dataValue = that._value(dataItem); 11728 11729 if (dataValue !== undefined) { 11730 dataValue += ""; 11731 11732 if (dataValue.indexOf('"') !== -1) { 11733 dataValue = dataValue.replace(quotRegExp, """); 11734 } 11735 11736 option += ' value="' + dataValue + '"'; 11737 } 11738 11739 option += ">"; 11740 11741 if (dataText !== undefined) { 11742 option += dataText; 11743 } 11744 11745 option += "</option>"; 11746 options += option; 11747 } 11748 11749 element.html(options); 11750 element[0].selectedIndex = selectedIndex; 11751 }, 11752 11753 _reset: function() { 11754 var that = this, 11755 element = that.element; 11756 11757 element.closest("form") 11758 .bind("reset", function() { 11759 setTimeout(function() { 11760 that.value(element[0].value); 11761 }); 11762 }); 11763 } 11764 }); 11765 11766})(jQuery); 11767(function($, undefined) { 11768 /** 11769 * @name kendo.ui.Calendar.Description 11770 * 11771 * @section 11772 * <p> 11773 * The <b>Calendar</b> renders a graphical calendar that supports 11774 * navigation and selection. It supports custom templates for its 11775 * "month" view, configurable options for a minimum and maximum date, 11776 * start view and the depth of the navigation. 11777 * </p> 11778 * <h3>Getting Started</h3> 11779 * 11780 * @exampleTitle Create a div element 11781 * @example 11782 * <div id="calendar"></div> 11783 * 11784 * @exampleTitle Initialize the Calendar via a jQuery ID selector 11785 * @example 11786 * $(document).ready(function(){
11787 * $("#calendar").kendoCalendar(); 11788 * }); 11789 * 11790 * @section 11791 * <p> 11792 * When a <b>Calendar</b> is initialized, it will automatically be 11793 * displayed near the location of the used HTML element. 11794 * </p> 11795 * <h3>Configuring Calendar Behaviors</h3> 11796 * <p> 11797 * The <b>Calendar</b> provides many configuration options that can be 11798 * easily set during initialization. Among the properties that can be 11799 * controlled: 11800 * </p> 11801 * <ul> 11802 * <li>Selected date</li> 11803 * <li>Minimum and/or maximum date</li> 11804 * <li>Start view</li> 11805 * <li> 11806 * Define the navigation depth (last view to which end user can 11807 * navigate) 11808 * </li> 11809 * <li>Day template</li> 11810 * <li>Footer template</li> 11811 * </ul> 11812 * 11813 * @exampleTitle Create Calendar with selected date and a defined minimum 11814 * and maximum date 11815 * @example 11816 * $("#calendar").kendoCalendar({ 11817 * value: new Date(), 11818 * min: new Date(1950, 0, 1), 11819 * max: new Date(2049, 11, 31) 11820 * }); 11821 * 11822 * @section 11823 * <p> 11824 * The <b>Calendar</b> will not navigate before than the minimum 11825 * date specified. It will also not navigate ahead the maximum date 11826 * specified. 11827 * </p> 11828 * @section 11829 * <h3>Define start view and navigation depth</h3> 11830 * <p> 11831 * The first rendered view can be defined with "start" option. 11832 * Navigation depth can be controlled with "depth" option. Predefined 11833 * views are: 11834 * <ul> 11835 * <li>"month" - shows the days from the month</li> 11836 * <li>"year" - shows the months of the year</li> 11837 * <li>"decade" - shows the years from the decade</li> 11838 * <li>"century" - shows the decades from the century</li> 11839 * </ul> 11840 * </p> 11841 * 11842 * @exampleTitle Create a Calendar, which allows a user to select a month 11843 * @example 11844 * $("#calendar").kendoCalendar({ 11845 * start: "year", 11846 * depth: "year" 11847 * }); 11848 * 11849 * @section 11850 * <h3>Customize day template</h3> 11851 * <p> 11852 * The <b>Calendar</b> allows to customize content of the rendered day 11853 * in the "month" view. 11854 * </p> 11855 * 11856 * @exampleTitle Create a Calendar with custom template 11857 * @example 11858 * $("#calendar").kendoCalendar({ 11859 * month: { 11860 * content: '<div class="custom"><#=data.value#></div>' 11861 * } 11862 * }); 11863 * 11864 * @section 11865 * <p> 11866 * This templates wraps the "value" in a div HTML element. Here is an 11867 * example of the object passed to the template function: 11868 * </p> 11869 * 11870 * @exampleTitle Structure of the data object passed to the template 11871 * @example 11872 * data = { 11873 * date: date, // Date object corresponding to the current cell 11874 * title: kendo.toString(date, "D"), 11875 * value: date.getDate(), 11876 * dateString: "2011/0/1" // formatted date using yyyy/MM/dd format and month is zero-based 11877 * }; 11878 * 11879 * @section 11880 * <h3>Accessing an Existing Calendar</h3> 11881 * <p> 11882 * You can reference an existing <b>Calendar</b> instance via 11883 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. 11884 * Once a reference has been established, you can use the API to control 11885 * its behavior. 11886 * </p> 11887 * 11888 * @exampleTitle Accessing an existing Calendar instance 11889 * @example 11890 * var calendar = $("#calendar").data("kendoCalendar"); 11891 * 11892 */ 11893 var kendo = window.kendo, 11894 ui = kendo.ui, 11895 Widget = ui.Widget, 11896 parse = kendo.parseDate, 11897 template = kendo.template, 11898 touch = kendo.support.touch, 11899 transitions = kendo.support.transitions, 11900 transitionOrigin = transitions ? transitions.css + "transform-origin" : "", 11901 cellTemplate = template('<td#=data.cssClass#><a class="k-link" href="\\#" data-#=data.ns#value="#=data.dateString#">#=data.value#</a></td>', { useWithBlock: false }), 11902 emptyCellTemplate = template("<td> </td>", { useWithBlock: false }), 11903 isIE8 = $.browser.msie && parseInt($.browser.version, 10) < 9, 11904 CLICK = touch ? "touchend" : "click", 11905 MIN = "min", 11906 LEFT = "left", 11907 SLIDE = "slide", 11908 MONTH = "month", 11909 CENTURY = "century", 11910 CHANGE = "change", 11911 NAVIGATE = "navigate", 11912 VALUE = "value", 11913 HOVER = "k-state-hover", 11914 DISABLED = "k-state-disabled", 11915 OTHERMONTH = "k-other-month", 11916 OTHERMONTHCLASS = ' class="' + OTHERMONTH + '"', 11917 TODAY = "k-nav-today", 11918 CELLSELECTOR = "td:has(.k-link)", 11919 MOUSEENTER = touch ? "touchstart" : "mouseenter", 11920 MOUSELEAVE = touch ? "touchend" : "mouseleave", 11921 MS_PER_MINUTE = 60000, 11922 MS_PER_DAY = 86400000, 11923 PREVARROW = "_prevArrow", 11924 NEXTARROW = "_nextArrow", 11925 proxy = $.proxy, 11926 extend = $.extend, 11927 DATE = Date, 11928 views = { 11929 month: 0, 11930 year: 1, 11931 decade: 2, 11932 century: 3 11933 }; 11934 11935 var Calendar = Widget.extend(/** @lends kendo.ui.Calendar.prototype */{ 11936 /** 11937 * @constructs 11938 * @extends kendo.ui.Widget 11939 * @param {DomElement} element DOM element 11940 * @param {Object} options Configuration options. 11941 * @option {Date} [value] <null> Specifies the selected date. 11942 * _example 11943 * // set the selected date to Jan 1st. 2012 11944 * $("#calendar").kendoCalendar({ 11945 * value: new Date(2012, 0, 1) 11946 * }); 11947 * _exampleTitle To set after initialization 11948 * _example 11949 * // get a reference to the Kendo UI calendar widget 11950 * var calendar = $("#calendar").data("kendoCalendar"); 11951 * // set the selected date on the calendar to Jan 1st, 2012 11952 * calendar.value(new Date(2012, 0, 1)); 11953 * @option {Date} [min] <Date(1900, 0, 1)> Specifies the minimum date, which the calendar can show. 11954 * _example 11955 * // set the min date to Jan 1st, 2011 11956 * $("#calendar").kendoCalendar({ 11957 * min = new Date(2011, 0, 1) 11958 * }); 11959 * _exampleTitle To set after initialization 11960 * _example 11961 * // get a reference to the Kendo UI calendar widget 11962 * var calendar = $("#calendar").data("kendoCalendar"); 11963 * // set the min date to Jan 1st, 2011 11964 * calendar.min(new Date(2011, 0, 1)); 11965 * @option {Date} [max] <Date(2099, 11, 31)> Specifies the maximum date, which the calendar can show. 11966 * _example 11967 * $("#calendar").kendoCalendar({ 11968 * max = new Date(2013, 0, 1); 11969 * }); 11970 * _exampleTitle To set after initialization 11971 * _example 11972 * // get a reference to the Kendo UI calendar widget 11973 * var calendar = $("#calendar").data("kendoCalendar"); 11974 * // set the max date to Jan 1st, 2013 11975 * calendar.max(new Date(2013, 0, 1)); 11976 * @option {String} [footer] <> Specifies the content of the footer. If false, the footer will not be rendered. 11977 * _example 11978 * // change the footer text from the default current date 11979 * $("#calendar").kendoCalendar({ 11980 * footer = "My Custom Footer" 11981 * }); 11982 * _exampleTitle Hide the footer 11983 * _example 11984 * $("#calendar").kendoCalendar({ 11985 * footer = false; 11986 * }); 11987 * @option {String} [format] <MM/dd/yyyy> Specifies the format, which is used to parse value set with value() method. 11988 * _example 11989 * $("#calendar").kendoCalendar({ 11990 * format: "yyyy/MM/dd" 11991 * }); 11992 * @option {String} [start] <month> Specifies the start view. 11993 * _example 11994 * $("#calendar").kendoCalendar({ 11995 * start: "year" 11996 * }); 11997 * @option {String} [depth] Specifies the navigation depth. 11998 * _example 11999 * $("#calendar").kendoCalendar({ 12000 * depth: "year" 12001 * }); 12002 * @option {Function} [footer] <> Template to be used for rendering the footer. If false, the footer will not be rendered. 12003 * _example 12004 * 12005 * //calendar intialization 12006 * <script> 12007 * $("#calendar").kendoCalendar({ 12008 * footer: kendo.template("Today - #=kendo.toString(data, 'd') #") 12009 * }); 12010 * </script> 12011 * @option {Object} [month] <> Templates for the cells rendered in the "month" view. 12012 * @option {Function} [month.content] <> Template to be used for rendering the cells in the "month" view, which are in range. 12013 * _example 12014 * //template 12015 * <script id="cellTemplate" type="text/x-kendo-tmpl"> 12016 * <div class="${ data.value < 10 ? exhibition : party }"> 12017 * </div> 12018 * ${ data.value } 12019 * </script> 12020 * 12021 * //calendar intialization 12022 * <script> 12023 * $("#calendar").kendoCalendar({ 12024 * month: { 12025 * content: kendo.template($("#cellTemplate").html()), 12026 * } 12027 * }); 12028 * </script> 12029 * @option {Function} [month.empty] <> Template to be used for rendering the cells in the "month" view, which are not in the min/max range. 12030 */ 12031 init: function(element, options) { 12032 var that = this, value; 12033 12034 Widget.fn.init.call(that, element, options); 12035 12036 element = that.wrapper = that.element; 12037 options = that.options; 12038 12039 element.addClass("k-widget k-calendar"); 12040 12041 that._templates(); 12042 12043 that._header(); 12044 12045 that._footer(that.footer); 12046 12047 element 12048 .delegate(CELLSELECTOR, MOUSEENTER, mouseenter) 12049 .delegate(CELLSELECTOR, MOUSELEAVE, mouseleave) 12050 .delegate(CELLSELECTOR, CLICK, proxy(that._click, that)); 12051 12052 value = options.value; 12053 validate(options); 12054 12055 that._index = views[options.start]; 12056 that._current = new DATE(restrictValue(value, options.min, options.max)); 12057 12058 that.value(value); 12059 12060 kendo.notify(that); 12061 }, 12062 12063 options: { 12064 name: "Calendar", 12065 value: null, 12066 min: new DATE(1900, 0, 1), 12067 max: new DATE(2099, 11, 31), 12068 footer : '#= kendo.toString(data,"D") #', 12069 format : "", 12070 month : {}, 12071 start: MONTH, 12072 depth: MONTH, 12073 animation: { 12074 horizontal: { 12075 effects: SLIDE, 12076 duration: 500, 12077 divisor: 2 12078 }, 12079 vertical: { 12080 effects: "zoomIn", 12081 duration: 400 12082 } 12083 } 12084 }, 12085 12086 events: [ 12087 /** 12088 * Fires when the selected date is changed 12089 * @name kendo.ui.Calendar#change 12090 * @event 12091 * @param {Event} e 12092 * @example 12093 * $("#calendar").kendoCalendar({ 12094 * change: function(e) { 12095 * // handle event 12096 * }); 12097 * }); 12098 * @exampleTitle To set after initialization 12099 * @example 12100 * // get a reference to the Kendo UI calendar widget 12101 * var calendar = $("#calendar").data("kendoCalendar"); 12102 * // bind to the change event 12103 * calendar.bind("change", function(e) { 12104 * // handle event 12105 * }); 12106 */ 12107 /** 12108 * Fires when navigate 12109 * @name kendo.ui.Calendar#navigate 12110 * @event 12111 * @param {Event} e 12112 * @example 12113 * $("#calendar").kendoCalendar({ 12114 * navigate: function(e) { 12115 * // handle event 12116 * } 12117 * }); 12118 * @exampleTitle To set after initialization 12119 * @example 12120 * // get a reference to the Kendo UI calendar widget 12121 * var calendar = $("#calendar").data("kendoCalendar"); 12122 * // bind to the change event 12123 * calendar.bind("navigate", function(e) { 12124 * // handle event 12125 * }); 12126 */ 12127 CHANGE, 12128 NAVIGATE 12129 ], 12130 12131 setOptions: function(options) { 12132 validate(options); 12133 12134 Widget.fn.setOptions.call(this, options); 12135 }, 12136 12137 /** 12138 * Gets/Sets the min value of the calendar. 12139 * @param {Date|String} value The min date to set. 12140 * @returns {Date} The min value of the calendar. 12141 * @example 12142 * // get a reference to the calendar widget 12143 * var calendar = $("#calendar").data("kendoCalendar"); 12144 * 12145 * // get the min value of the calendar. 12146 * var min = calendar.min(); 12147 * 12148 * // set the min value of the calendar. 12149 * calendar.min(new Date(1900, 0, 1)); 12150 */ 12151 min: function(value) { 12152 return this._option(MIN, value); 12153 }, 12154 12155 /** 12156 * Gets/Sets the max value of the calendar. 12157 * @param {Date | String} value The max date to set. 12158 * @returns {Date} The max value of the calendar. 12159 * @example 12160 * // get a reference to the calendar widget 12161 * var calendar = $("#calendar").data("kendoCalendar"); 12162 * 12163 * // get the max value of the calendar. 12164 * var max = calendar.max();
12165 * 12166 * // set the max value of the calendar. 12167 * calendar.max(new Date(2100, 0, 1)); 12168 */ 12169 max: function(value) { 12170 return this._option("max", value); 12171 }, 12172 12173 /** 12174 * Navigates to the past 12175 * @example 12176 * // get a reference to the calendar widget 12177 * var calendar = $("#calendar").data("kendoCalendar"); 12178 * // navigate to past 12179 * calendar.navigateToPast(); 12180 */ 12181 navigateToPast: function() { 12182 this._navigate(PREVARROW, -1); 12183 }, 12184 12185 /** 12186 * Navigates to the future 12187 * @example 12188 * // get a reference to the calendar widget 12189 * var calendar = $("#calendar").data("kendoCalendar"); 12190 * // navigate to future 12191 * calendar.navigateToFuture(); 12192 */ 12193 navigateToFuture: function() { 12194 this._navigate(NEXTARROW, 1); 12195 }, 12196 12197 /** 12198 * Navigates to the upper view 12199 * @example 12200 * // get a reference to the calendar widget 12201 * var calendar = $("#calendar").data("kendoCalendar"); 12202 * // navigate up 12203 * calendar.navigateUp(); 12204 */ 12205 navigateUp: function() { 12206 var that = this, 12207 index = that._index; 12208 12209 if (that._title.hasClass(DISABLED)) { 12210 return; 12211 } 12212 12213 that.navigate(that._current, ++index); 12214 }, 12215 12216 /** 12217 * Navigates to the lower view 12218 * @param {Date} value Desired date 12219 * @example 12220 * // get a reference to the calendar widget 12221 * var calendar = $("#calendar").data("kendoCalendar"); 12222 * // navigate down 12223 * calendar.navigateDown(value); 12224 */ 12225 navigateDown: function(value) { 12226 var that = this, 12227 index = that._index, 12228 depth = that.options.depth; 12229 12230 if (!value) { 12231 return; 12232 } 12233 12234 if (index === views[depth]) { 12235 if (+that._value != +value) { 12236 that.value(value); 12237 that.trigger(CHANGE); 12238 } 12239 return; 12240 } 12241 12242 that.navigate(value, --index); 12243 }, 12244 12245 /** 12246 * Navigates to view 12247 * @param {Date} value Desired date 12248 * @param {String} view Desired view 12249 * @example 12250 * // get a reference to the calendar widget 12251 * var calendar = $("#calendar").data("kendoCalendar"); 12252 * // navigate to the desired date 12253 * calendar.navigate(value, view); 12254 */ 12255 navigate: function(value, view) { 12256 view = isNaN(view) ? views[view] : view; 12257 12258 var that = this, 12259 options = that.options, 12260 min = options.min, 12261 max = options.max, 12262 title = that._title, 12263 from = that._table, 12264 selectedValue = that._value, 12265 currentValue = that._current, 12266 future = value && +value > +currentValue, 12267 vertical = view !== undefined && view !== that._index, 12268 to, currentView, compare; 12269 12270 //do not navigate if the view is still animating 12271 if (from && from.parent().data("animating")) { 12272 return; 12273 } 12274 12275 if (!value) { 12276 value = currentValue; 12277 } else { 12278 that._current = value = new DATE(restrictValue(value, min, max)); 12279 } 12280 12281 if (view === undefined) { 12282 view = that._index; 12283 } else { 12284 that._index = view; 12285 } 12286 12287 that._view = currentView = calendar.views[view]; 12288 compare = currentView.compare; 12289 12290 title.toggleClass(DISABLED, view === views[CENTURY]); 12291 that[PREVARROW].toggleClass(DISABLED, compare(value, min) < 1); 12292 that[NEXTARROW].toggleClass(DISABLED, compare(value, max) > -1); 12293 12294 if (!from || that._changeView) { 12295 title.html(currentView.title(value)); 12296 12297 that._table = to = $(currentView.content(extend({ 12298 min: min, 12299 max: max, 12300 date: value 12301 }, that[currentView.name]))); 12302 12303 makeUnselectable(to); 12304 12305 that._animate({ 12306 from: from, 12307 to: to, 12308 vertical: vertical, 12309 future: future 12310 }); 12311 12312 that.trigger(NAVIGATE); 12313 } 12314 12315 if (view === views[options.depth] && selectedValue) { 12316 that._class("k-state-selected", currentView.toDateString(selectedValue)); 12317 } 12318 12319 that._changeView = true; 12320 }, 12321 12322 /** 12323 * Gets/Sets the value of the calendar. 12324 * @param {Date|String} value The date to set. 12325 * @returns {Date} The value of the calendar. 12326 * @example 12327 * // get a reference to the calendar widget 12328 * var calendar = $("#calendar").data("kendoCalendar"); 12329 * 12330 * // get the value of the calendar. 12331 * var value = calendar.value(); 12332 * 12333 * // set the value of the calendar. 12334 * calendar.value(new Date()); 12335 */ 12336 value: function(value) { 12337 var that = this, 12338 view = that._view, 12339 options = that.options, 12340 min = options.min, 12341 max = options.max; 12342 12343 if (value === undefined) { 12344 return that._value; 12345 } 12346 12347 value = parse(value, options.format); 12348 12349 if (value !== null) { 12350 value = new DATE(value); 12351 12352 if (!isInRange(value, min, max)) { 12353 value = null; 12354 } 12355 } 12356 12357 that._value = value; 12358 that._changeView = !value || view && view.compare(value, that._current) !== 0; 12359 12360 that.navigate(value); 12361 }, 12362 12363 _animate: function(options) { 12364 var that = this, 12365 from = options.from, 12366 to = options.to; 12367 12368 if (!from) { 12369 to.insertAfter(that.element[0].firstChild); 12370 } else if (!from.is(":visible") || that.options.animation === false) { 12371 to.insertAfter(from); 12372 from.remove(); 12373 } else { 12374 that[options.vertical ? "_vertical" : "_horizontal"](from, to, options.future); 12375 } 12376 }, 12377 12378 _horizontal: function(from, to, future) { 12379 var that = this, 12380 horizontal = that.options.animation.horizontal, 12381 effects = horizontal.effects, 12382 viewWidth = from.outerWidth(); 12383 12384 if (effects && effects.indexOf(SLIDE) != -1) { 12385 from.add(to).css({ width: viewWidth }); 12386 12387 from.wrap("<div/>"); 12388 12389 from.parent() 12390 .css({ 12391 position: "relative", 12392 width: viewWidth * 2, 12393 "float": LEFT, 12394 left: future ? 0 : -viewWidth 12395 }); 12396 12397 to[future ? "insertAfter" : "insertBefore"](from); 12398 12399 extend(horizontal, { 12400 effects: SLIDE + ":" + (future ? LEFT : "right"), 12401 complete: function() { 12402 from.remove(); 12403 to.unwrap(); 12404 } 12405 }); 12406 12407 from.parent().kendoStop(true, true).kendoAnimate(horizontal); 12408 } 12409 }, 12410 12411 _vertical: function(from, to) { 12412 var that = this, 12413 vertical = that.options.animation.vertical, 12414 effects = vertical.effects, 12415 cell, position; 12416 12417 if (effects && effects.indexOf("zoom") != -1) { 12418 to.css({ 12419 position: "absolute",
12420 top: from.prev().outerHeight(), 12421 left: 0 12422 }).insertBefore(from); 12423 12424 if (transitionOrigin) { 12425 cell = that._cellByDate(that._view.toDateString(that._current)); 12426 position = cell.position(); 12427 position = (position.left + parseInt(cell.width() / 2, 10)) + "px" + " " + (position.top + parseInt(cell.height() / 2, 10) + "px"); 12428 to.css(transitionOrigin, position); 12429 } 12430 12431 from.kendoStop(true, true).kendoAnimate({ 12432 effects: "fadeOut", 12433 duration: 600, 12434 complete: function() { 12435 from.remove(); 12436 to.css({ 12437 position: "static", 12438 top: 0, 12439 left: 0 12440 }); 12441 } 12442 }); 12443 12444 to.kendoStop(true, true).kendoAnimate(vertical); 12445 } 12446 }, 12447 12448 _cellByDate: function(value) { 12449 return this._table.find("td:not(." + OTHERMONTH + ")") 12450 .filter(function() { 12451 return $(this.firstChild).attr(kendo.attr(VALUE)) === value; 12452 }); 12453 }, 12454 12455 _class: function(className, value) { 12456 this._table.find("td:not(." + OTHERMONTH + ")") 12457 .removeClass(className) 12458 .filter(function() { 12459 return $(this.firstChild).attr(kendo.attr(VALUE)) === value; 12460 }) 12461 .addClass(className); 12462 }, 12463 12464 _click: function(e) { 12465 var that = this, 12466 options = that.options, 12467 currentValue = that._current, 12468 link = $(e.currentTarget.firstChild), 12469 value = link.attr(kendo.attr(VALUE)).split("/"); 12470 12471 //Safari cannot create corretly date from "1/1/2090" 12472 value = new DATE(value[0], value[1], value[2]); 12473 12474 e.preventDefault(); 12475 12476 if (link.parent().hasClass(OTHERMONTH)) { 12477 currentValue = value; 12478 } else { 12479 that._view.setDate(currentValue, value); 12480 } 12481 12482 that.navigateDown(restrictValue(currentValue, options.min, options.max)); 12483 }, 12484 12485 _focus: function(value) { 12486 var that = this, 12487 view = that._view; 12488 12489 if (view.compare(value, that._current) !== 0) { 12490 that.navigate(value); 12491 } else { 12492 that._current = value; 12493 } 12494 12495 that._class("k-state-focused", view.toDateString(value)); 12496 }, 12497 12498 _footer: function(template) { 12499 var that = this, 12500 element = that.element, 12501 today = new DATE(), 12502 footer = element.find(".k-footer"); 12503 12504 if (!template) { 12505 that._toggle(false); 12506 footer.hide(); 12507 return; 12508 } 12509 12510 if (!footer[0]) { 12511 footer = $('<div class="k-footer"><a href="#" class="k-link k-nav-today"></a></div>').appendTo(element); 12512 } 12513 12514 that._today = footer.show() 12515 .find(".k-link") 12516 .html(template(today)) 12517 .attr("title", kendo.toString(today, "D")); 12518 12519 that._toggle(); 12520 }, 12521 12522 _header: function() { 12523 var that = this, 12524 element = that.element, 12525 links; 12526 12527 if (!element.find(".k-header")[0]) { 12528 element.html('<div class="k-header">' + 12529 '<a href="#" class="k-link k-nav-prev"><span class="k-icon k-arrow-prev"></span></a>' + 12530 '<a href="#" class="k-link k-nav-fast"></a>' + 12531 '<a href="#" class="k-link k-nav-next"><span class="k-icon k-arrow-next"></span></a>' + 12532 '</div>'); 12533 } 12534 12535 links = element.find(".k-link") 12536 .bind(MOUSEENTER, mouseenter) 12537 .bind(MOUSELEAVE, mouseleave) 12538 .click(false); 12539 12540 that._title = links.eq(1).bind(CLICK, proxy(that.navigateUp, that)); 12541 that[PREVARROW] = links.eq(0).bind(CLICK, proxy(that.navigateToPast, that)); 12542 that[NEXTARROW] = links.eq(2).bind(CLICK, proxy(that.navigateToFuture, that)); 12543 }, 12544 12545 _navigate: function(arrow, modifier) { 12546 var that = this, 12547 index = that._index + 1, 12548 currentValue = new DATE(that._current); 12549 12550 arrow = that[arrow]; 12551 12552 if (!arrow.hasClass(DISABLED)) { 12553 if (index > 3) { 12554 currentValue.setFullYear(currentValue.getFullYear() + 100 * modifier); 12555 } else { 12556 calendar.views[index].setDate(currentValue, modifier); 12557 } 12558 12559 that.navigate(currentValue); 12560 } 12561 }, 12562 12563 _option: function(option, value) { 12564 var that = this, 12565 options = that.options, 12566 selectedValue = +that._value, 12567 bigger, navigate; 12568 12569 if (value === undefined) { 12570 return options[option]; 12571 } 12572 12573 value = parse(value, options.format); 12574 12575 if (!value) { 12576 return; 12577 } 12578 12579 options[option] = new DATE(value); 12580 12581 navigate = that._view.compare(value, that._current); 12582 12583 if (option === MIN) { 12584 bigger = +value > selectedValue; 12585 navigate = navigate > -1; 12586 } else { 12587 bigger = selectedValue > +value; 12588 navigate = navigate < 1; 12589 } 12590 12591 if (bigger) { 12592 that.value(null); 12593 } else if (navigate) { 12594 that.navigate(); 12595 } 12596 12597 that._toggle(); 12598 }, 12599 12600 _toggle: function(toggle) { 12601 var that = this, 12602 options = that.options, 12603 link = that._today; 12604 12605 if (toggle === undefined) { 12606 toggle = isInRange(new DATE(), options.min, options.max); 12607 } 12608 12609 if (link) { 12610 link.unbind(CLICK);
12611 12612 if (toggle) { 12613 link.addClass(TODAY) 12614 .removeClass(DISABLED) 12615 .bind(CLICK, proxy(that._todayClick, that)); 12616 } else { 12617 link.removeClass(TODAY) 12618 .addClass(DISABLED) 12619 .bind(CLICK, prevent); 12620 } 12621 } 12622 }, 12623 12624 _todayClick: function(e) { 12625 var that = this, 12626 depth = views[that.options.depth], 12627 today = new DATE(); 12628 12629 e.preventDefault(); 12630 12631 if (that._view.compare(that._current, today) === 0 && that._index == depth) { 12632 that._changeView = false; 12633 } 12634 12635 that._value = today; 12636 that.navigate(today, depth); 12637 12638 that.trigger(CHANGE); 12639 }, 12640 12641 _templates: function() { 12642 var that = this, 12643 options = that.options, 12644 footer = options.footer, 12645 month = options.month, 12646 content = month.content, 12647 empty = month.empty; 12648 12649 that.month = { 12650 content: template('<td#=data.cssClass#><a class="k-link" href="\\#" ' + kendo.attr("value") + '="#=data.dateString#" title="#=data.title#">' + (content || "#=data.value#") + '</a></td>', { useWithBlock: !!content }), 12651 empty: template("<td>" + (empty || " ") + "</td>", { useWithBlock: !!empty }) 12652 }; 12653 12654 if (footer) { 12655 that.footer = template(footer, { useWithBlock: false }); 12656 } 12657 } 12658 }); 12659 12660 ui.plugin(Calendar); 12661 12662 var calendar = { 12663 firstDayOfMonth: function (date) { 12664 return new DATE( 12665 date.getFullYear(), 12666 date.getMonth(), 12667 1 12668 ); 12669 }, 12670 12671 firstVisibleDay: function (date) { 12672 var firstDay = kendo.culture().calendar.firstDay, 12673 firstVisibleDay = new DATE(date.getFullYear(), date.getMonth(), 0, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()); 12674 12675 while (firstVisibleDay.getDay() != firstDay) { 12676 calendar.setTime(firstVisibleDay, -1 * MS_PER_DAY); 12677 } 12678 12679 return firstVisibleDay; 12680 }, 12681 12682 setTime: function (date, time) { 12683 var tzOffsetBefore = date.getTimezoneOffset(), 12684 resultDATE = new DATE(date.getTime() + time), 12685 tzOffsetDiff = resultDATE.getTimezoneOffset() - tzOffsetBefore; 12686 12687 date.setTime(resultDATE.getTime() + tzOffsetDiff * MS_PER_MINUTE); 12688 }, 12689 views: [{ 12690 name: MONTH, 12691 title: function(date) { 12692 return kendo.culture().calendar.months.names[date.getMonth()] + " " + date.getFullYear(); 12693 }, 12694 content: function(options) { 12695 var that = this, 12696 idx = 0, 12697 min = options.min, 12698 max = options.max, 12699 date = options.date, 12700 currentCalendar = kendo.culture().calendar, 12701 firstDayIdx = currentCalendar.firstDay, 12702 days = currentCalendar.days, 12703 names = shiftArray(days.names, firstDayIdx), 12704 short = shiftArray(days.namesShort, firstDayIdx), 12705 start = calendar.firstVisibleDay(date), 12706 firstDayOfMonth = that.first(date), 12707 lastDayOfMonth = that.last(date), 12708 toDateString = that.toDateString, 12709 today = new DATE(), 12710 html = '<table class="k-content" cellspacing="0"><thead><tr>'; 12711 12712 for (; idx < 7; idx++) { 12713 html += '<th scope="col" title="' + names[idx] + '">' + short[idx] + '</th>'; 12714 } 12715 12716 today = +new DATE(today.getFullYear(), today.getMonth(), today.getDate()); 12717 12718 return view({ 12719 cells: 42, 12720 perRow: 7, 12721 html: html += "</tr></thead><tbody><tr>", 12722 start: new DATE(start.getFullYear(), start.getMonth(), start.getDate()), 12723 min: new DATE(min.getFullYear(), min.getMonth(), min.getDate()), 12724 max: new DATE(max.getFullYear(), max.getMonth(), max.getDate()), 12725 content: options.content, 12726 empty: options.empty, 12727 setter: that.setDate, 12728 build: function(date, idx) { 12729 var cssClass = [], 12730 day = date.getDay(); 12731 12732 if (date < firstDayOfMonth || date > lastDayOfMonth) { 12733 cssClass.push(OTHERMONTH); 12734 } 12735 12736 if (+date === today) { 12737 cssClass.push("k-today"); 12738 } 12739 12740 if (day === 0 || day === 6) { 12741 cssClass.push("k-weekend"); 12742 } 12743 12744 return { 12745 date: date, 12746 ns: kendo.ns, 12747 title: kendo.toString(date, "D"), 12748 value: date.getDate(), 12749 dateString: toDateString(date),
12750 cssClass: cssClass[0] ? ' class="' + cssClass.join(" ") + '"' : "" 12751 }; 12752 } 12753 }); 12754 }, 12755 first: function(date) { 12756 return calendar.firstDayOfMonth(date); 12757 }, 12758 last: function(date) { 12759 return new DATE(date.getFullYear(), date.getMonth() + 1, 0); 12760 }, 12761 compare: function(date1, date2) { 12762 var result, 12763 month1 = date1.getMonth(), 12764 year1 = date1.getFullYear(), 12765 month2 = date2.getMonth(), 12766 year2 = date2.getFullYear(); 12767 12768 if (year1 > year2) { 12769 result = 1; 12770 } else if (year1 < year2) { 12771 result = -1; 12772 } else { 12773 result = month1 == month2 ? 0 : month1 > month2 ? 1 : -1; 12774 } 12775 12776 return result; 12777 }, 12778 setDate: function(date, value) { 12779 if (value instanceof DATE) { 12780 date.setFullYear(value.getFullYear(), value.getMonth(), value.getDate()); 12781 } else { 12782 calendar.setTime(date, value * MS_PER_DAY); 12783 } 12784 }, 12785 toDateString: function(date) { 12786 return date.getFullYear() + "/" + date.getMonth() + "/" + date.getDate(); 12787 } 12788 }, 12789 { 12790 name: "year", 12791 title: function(date) { 12792 return date.getFullYear(); 12793 }, 12794 content: function(options) { 12795 var namesAbbr = kendo.culture().calendar.months.namesAbbr, 12796 toDateString = this.toDateString, 12797 min = options.min, 12798 max = options.max; 12799 12800 return view({ 12801 min: new DATE(min.getFullYear(), min.getMonth(), 1), 12802 max: new DATE(max.getFullYear(), max.getMonth(), 1), 12803 start: new DATE(options.date.getFullYear(), 0, 1), 12804 setter: this.setDate, 12805 build: function(date) { 12806 return { 12807 value: namesAbbr[date.getMonth()], 12808 ns: kendo.ns, 12809 dateString: toDateString(date), 12810 cssClass: "" 12811 }; 12812 } 12813 }); 12814 }, 12815 first: function(date) { 12816 return new DATE(date.getFullYear(), 0, date.getDate()); 12817 }, 12818 last: function(date) { 12819 return new DATE(date.getFullYear(), 11, date.getDate()); 12820 }, 12821 compare: function(date1, date2){ 12822 return compare(date1, date2); 12823 }, 12824 setDate: function(date, value) { 12825 if (value instanceof DATE) { 12826 date.setFullYear(value.getFullYear(), 12827 value.getMonth(), 12828 date.getDate()); 12829 } else { 12830 var month = date.getMonth() + value; 12831 12832 date.setMonth(month); 12833 12834 if (month > 11) { 12835 month -= 12; 12836 } 12837 12838 if (month > 0 && date.getMonth() != month) { 12839 date.setDate(0); 12840 } 12841 } 12842 }, 12843 toDateString: function(date) { 12844 return date.getFullYear() + "/" + date.getMonth() + "/1"; 12845 } 12846 }, 12847 { 12848 name: "decade", 12849 title: function(date) { 12850 var start = date.getFullYear(); 12851 12852 start = start - start % 10; 12853 12854 return start + "-" + (start + 9); 12855 }, 12856 content: function(options) { 12857 var year = options.date.getFullYear(), 12858 toDateString = this.toDateString; 12859 12860 return view({ 12861 start: new DATE(year - year % 10 - 1, 0, 1), 12862 min: new DATE(options.min.getFullYear(), 0, 1), 12863 max: new DATE(options.max.getFullYear(), 0, 1), 12864 setter: this.setDate, 12865 build: function(date, idx) { 12866 return { 12867 value: date.getFullYear(), 12868 ns: kendo.ns, 12869 dateString: toDateString(date), 12870 cssClass: idx === 0 || idx == 11 ? OTHERMONTHCLASS : "" 12871 }; 12872 } 12873 }); 12874 }, 12875 first: function(date) { 12876 var year = date.getFullYear(); 12877 return new DATE(year - year % 10, date.getMonth(), date.getDate()); 12878 }, 12879 last: function(date) { 12880 var year = date.getFullYear(); 12881 return new DATE(year - year % 10 + 9, date.getMonth(), date.getDate()); 12882 }, 12883 compare: function(date1, date2) { 12884 return compare(date1, date2, 10); 12885 }, 12886 setDate: function(date, value) { 12887 setDate(date, value, 1); 12888 }, 12889 toDateString: function(date) { 12890 return date.getFullYear() + "/0/1"; 12891 } 12892 }, 12893 { 12894 name: CENTURY, 12895 title: function(date) { 12896 var start = date.getFullYear(); 12897 12898 start = start - start % 100; 12899 12900 return start + "-" + (start + 99); 12901 }, 12902 content: function(options) { 12903 var year = options.date.getFullYear(), 12904 minYear = options.min.getFullYear(), 12905 maxYear = options.max.getFullYear(), 12906 toDateString = this.toDateString; 12907 12908 minYear = minYear - minYear % 10; 12909 maxYear = maxYear - maxYear % 10; 12910 12911 if (maxYear - minYear < 10) { 12912 maxYear = minYear + 9; 12913 } 12914 12915 return view({ 12916 start: new DATE(year - year % 100 - 10, 0, 1), 12917 min: new DATE(minYear, 0, 1), 12918 max: new DATE(maxYear, 0, 1), 12919 setter: this.setDate, 12920 build: function(date, idx) { 12921 var year = date.getFullYear(); 12922 return { 12923 value: year + " - " + (year + 9), 12924 ns: kendo.ns, 12925 dateString: toDateString(date), 12926 cssClass: idx === 0 || idx == 11 ? OTHERMONTHCLASS : "" 12927 }; 12928 } 12929 }); 12930 }, 12931 first: function(date) { 12932 var year = date.getFullYear(); 12933 return new DATE(year - year % 100, date.getMonth(), date.getDate()); 12934 }, 12935 last: function(date) { 12936 var year = date.getFullYear(); 12937 return new DATE(year - year % 100 + 99, date.getMonth(), date.getDate()); 12938 }, 12939 compare: function(date1, date2) { 12940 return compare(date1, date2, 100); 12941 }, 12942 setDate: function(date, value) { 12943 setDate(date, value, 10); 12944 }, 12945 toDateString: function(date) { 12946 var year = date.getFullYear(); 12947 return (year - year % 10) + "/0/1"; 12948 } 12949 }] 12950 }; 12951 12952 function view(options) { 12953 var idx = 0, 12954 data, 12955 min = options.min, 12956 max = options.max, 12957 start = options.start, 12958 setter = options.setter, 12959 build = options.build, 12960 length = options.cells || 12, 12961 cellsPerRow = options.perRow || 4, 12962 content = options.content || cellTemplate, 12963 empty = options.empty || emptyCellTemplate, 12964 html = options.html || '<table class="k-content k-meta-view" cellspacing="0"><tbody><tr>'; 12965 12966 for(; idx < length; idx++) { 12967 if (idx > 0 && idx % cellsPerRow === 0) { 12968 html += "</tr><tr>"; 12969 } 12970 12971 data = build(start, idx); 12972 12973 html += isInRange(start, min, max) ? content(data) : empty(data); 12974 12975 setter(start, 1); 12976 } 12977 12978 return html + "</tr></tbody></table>"; 12979 } 12980 12981 function compare(date1, date2, modifier) { 12982 var year1 = date1.getFullYear(), 12983 start = date2.getFullYear(), 12984 end = start, 12985 result = 0; 12986 12987 if (modifier) { 12988 start = start - start % modifier; 12989 end = start - start % modifier + modifier - 1; 12990 } 12991 12992 if (year1 > end) { 12993 result = 1; 12994 } else if (year1 < start) { 12995 result = -1; 12996 } 12997 12998 return result; 12999 } 13000 13001 function restrictValue (value, min, max) { 13002 var today = new DATE(); 13003 13004 today = new DATE(today.getFullYear(), today.getMonth(), today.getDate()); 13005 13006 if (value) { 13007 today = new DATE(value); 13008 } 13009 13010 if (min > today) { 13011 today = new DATE(min); 13012 } else if (max < today) { 13013 today = new DATE(max); 13014 } 13015 return today; 13016 } 13017 13018 function isInRange(date, min, max) { 13019 return +date >
13019= +min && +date <= +max; 13020 } 13021 13022 function shiftArray(array, idx) { 13023 return array.slice(idx).concat(array.slice(0, idx)); 13024 } 13025 13026 function setDate(date, value, multiplier) { 13027 value = value instanceof DATE ? value.getFullYear() : date.getFullYear() + multiplier * value; 13028 date.setFullYear(value); 13029 } 13030 13031 function mouseenter() { 13032 $(this).addClass(HOVER); 13033 } 13034 13035 function mouseleave() { 13036 $(this).removeClass(HOVER); 13037 } 13038 13039 function prevent (e) { 13040 e.preventDefault(); 13041 } 13042 13043 function validate(options) { 13044 var start = views[options.start], 13045 depth = views[options.depth], 13046 format = options.format || kendo.culture().calendar.patterns.d; 13047 13048 if (isNaN(start)) { 13049 start = 0; 13050 options.start = MONTH; 13051 } 13052 13053 if (depth === undefined || depth > start) { 13054 options.depth = MONTH; 13055 } 13056 13057 if (format.slice(0,3) === "{0:") { 13058 format = format.slice(3, format.length - 1); 13059 } 13060 13061 options.format = format; 13062 } 13063 13064 function makeUnselectable(element) { 13065 if (isIE8) { 13066 element.find("*").attr("unselectable", "on"); 13067 } 13068 } 13069 13070 calendar.makeUnselectable = makeUnselectable; 13071 calendar.restrictValue = restrictValue; 13072 calendar.isInRange = isInRange; 13073 calendar.validate = validate; 13074 calendar.viewsEnum = views; 13075 13076 kendo.calendar = calendar; 13077})(jQuery); 13078(function($, undefined) { 13079 /** 13080 * @name kendo.ui.DatePicker.Description 13081 * 13082 * @section 13083 * <p> 13084 * The <b>DatePicker</b> allows the end user to select a date from a 13085 * calendar or by direct input. It supports custom templates for "month" 13086 * view, configurable options for min and max date, start view and the 13087 * depth of the navigation. 13088 * </p> 13089 * <h3>Getting Started</h3> 13090 * 13091 * @exampleTitle Creating a DatePicker from existing input element 13092 * @example 13093 * <input id="datePicker" /> 13094 * 13095 * @exampleTitle DatePicker initialization 13096 * @example 13097 * $(document).ready(function(){ 13098 * $("#datePicker").kendoDatePicker(); 13099 * }); 13100 * 13101 * @section 13102 * <p> 13103 * When a <b>DatePicker</b> is initialized, it will be displayed at the 13104 * location of the target HTML element. 13105 * </p> 13106 * <h3>Configuring DatePicker Behaviors</h3> 13107 * <p> 13108 * The <b>DatePicker</b> provides configuration options that can be set 13109 * during initialization. Among the properties that can be controlled: 13110 * </p> 13111 * <ul> 13112 * <li>Selected date</li> 13113 * <li>Minimum and/or maximum date</li> 13114 * <li>Define format</li> 13115 * <li>Start view</li> 13116 * <li>Navigation depth (last view to which end user can navigate)</li> 13117 * </ul> 13118 * 13119 * @exampleTitle Create DatePicker with a selected date and a defined 13120 * minimum and maximum date 13121 * @example 13122 * $(document).ready(function(){ 13123 * $("#datePicker").kendoDatePicker({ 13124 * value: new Date(), 13125 * min: new Date(1950, 0, 1), 13126 * max: new Date(2049, 11, 31) 13127 * }) 13128 * }); 13129 * 13130 * @section 13131 * <p> 13132 * DatePicker will set the value only if the entered date is valid and 13133 * within the defined range. 13134 * </p> 13135 * @section 13136 * <h3>Defining a Start View and Navigation Depth</h3> 13137 * <p> 13138 * The first rendered view can be defined with "start" option. 13139 * Navigation depth can be controlled with "depth" option. Predefined 13140 * views are: 13141 * </p> 13142 * <ul> 13143 * <li>"month" - shows the days from the month</li> 13144 * <li>"year" - shows the months of the year</li> 13145 * <li>"decade" - shows the years from the decade</li> 13146 * <li>"century" - shows the decades from the century</li> 13147 * </ul> 13148 * 13149 * @exampleTitle Create a DatePicker for selecting a month 13150 * @example 13151 * $("#datePicker").kendoDatePicker({ 13152 * start: "year", 13153 * depth: "year" 13154 * }); 13155 * 13156 * @section 13157 * <h3>Accessing an Existing DatePicker</h3> 13158 * <p> 13159 * You can reference an existing <b>DatePicker</b> instance via 13160 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. 13161 * Once a reference has been established, you can use the API to control 13162 * its behavior. 13163 * </p> 13164 * 13165 * @exampleTitle Accessing an existing DatePicker instance 13166 * @example 13167 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13168 * 13169 */ 13170 var kendo = window.kendo, 13171 ui = kendo.ui, 13172 touch = kendo.support.touch, 13173 Widget = ui.Widget, 13174 parse = kendo.parseDate, 13175 keys = kendo.keys, 13176 template = kendo.template, 13177 DIV = "<div />", 13178 SPAN = "<span />", 13179 CLICK = (touch ? "touchend" : "click"), 13180 CLICK_DATEPICKER = CLICK + ".datepicker", 13181 OPEN = "open", 13182 CLOSE = "close", 13183 CHANGE = "change", 13184 NAVIGATE = "navigate", 13185 DATEVIEW = "dateView", 13186 DISABLED = "disabled", 13187 DEFAULT = "k-state-default", 13188 FOCUSED = "k-state-focused", 13189 SELECTED = "k-state-selected", 13190 STATEDISABLED = "k-state-disabled", 13191 HOVER = "k-state-hover", 13192 HOVEREVENTS = "mouseenter mouseleave", 13193 MOUSEDOWN = (touch ? "touchstart" : "mousedown"), 13194 MIN = "min", 13195 MAX = "max", 13196 MONTH = "month", 13197 FIRST = "first", 13198 calendar = kendo.calendar, 13199 isInRange = calendar.isInRange, 13200 restrictValue = calendar.restrictValue, 13201 extend = $.extend, 13202 proxy = $.proxy, 13203 DATE = Date; 13204 13205 var DateView = function(options) { 13206 var that = this, 13207 body = document.body, 13208 sharedCalendar = DatePicker.sharedCalendar; 13209 13210 if (!sharedCalendar) { 13211 sharedCalendar = DatePicker.sharedCalendar = new ui.Calendar($(DIV).hide().appendTo(body)); 13212 calendar.makeUnselectable(sharedCalendar.element); 13213 } 13214 13215 that.calendar = sharedCalendar; 13216 that.options = options = options || {}; 13217 that.popup = new ui.Popup($(DIV).addClass("k-calendar-container").appendTo(body), extend(options.popup, options)); 13218 13219 that._templates(); 13220 13221 that.value(options.value); 13222 }; 13223 13224 DateView.prototype = { 13225 _calendar: function() { 13226 var that = this, 13227 popup = that.popup, 13228 options = that.options, 13229 calendar = that.calendar, 13230 element = calendar.element; 13231 13232 if (element.data(DATEVIEW) !== that) { 13233 13234 element.appendTo(popup.element) 13235 .data(DATEVIEW, that) 13236 .undelegate(CLICK_DATEPICKER) 13237 .delegate("td:has(.k-link)", CLICK_DATEPICKER, proxy(that._click, that)) 13238 .unbind(MOUSEDOWN) 13239 .bind(MOUSEDOWN, function(e) { e.preventDefault(); }) 13240 .show(); 13241 13242 calendar.unbind(CHANGE) 13243 .bind(CHANGE, options); 13244 13245 if (!touch) { 13246 calendar.unbind(NAVIGATE) 13247 .bind(NAVIGATE, proxy(that._navigate, that)); 13248 } 13249 13250 calendar.month = that.month; 13251 calendar.options.depth = options.depth; 13252 13253 calendar._footer(that.footer); 13254 13255 calendar.min(options.min); 13256 calendar.max(options.max); 13257 13258 calendar.navigate(that._value, options.start); 13259 that.value(that._value); 13260 } 13261 }, 13262 13263 open: function() { 13264 var that = this; 13265
13266 that._calendar(); 13267 that.popup.open(); 13268 }, 13269 13270 close: function() { 13271 this.popup.close(); 13272 }, 13273 13274 min: function(value) { 13275 this._option(MIN, value); 13276 }, 13277 13278 max: function(value) { 13279 this._option(MAX, value); 13280 }, 13281 13282 toggle: function() { 13283 var that = this; 13284 13285 that[that.popup.visible() ? CLOSE : OPEN](); 13286 }, 13287 13288 move: function(e) { 13289 var that = this, 13290 options = that.options, 13291 currentValue = new DATE(that._current), 13292 calendar = that.calendar, 13293 index = calendar._index, 13294 view = calendar._view, 13295 key = e.keyCode, 13296 value, prevent, method; 13297 13298 if (key == keys.ESC) { 13299 that.close(); 13300 return; 13301 } 13302 13303 if (e.altKey) { 13304 if (key == keys.DOWN) { 13305 that.open(); 13306 prevent = true; 13307 } else if (key == keys.UP) { 13308 that.close(); 13309 prevent = true; 13310 } 13311 return; 13312 } 13313 13314 if (!that.popup.visible() || calendar._table.parent().data("animating")) { 13315 return; 13316 } 13317 13318 if (e.ctrlKey) { 13319 if (key == keys.RIGHT) { 13320 calendar.navigateToFuture(); 13321 prevent = true; 13322 } else if (key == keys.LEFT) { 13323 calendar.navigateToPast(); 13324 prevent = true; 13325 } else if (key == keys.UP) { 13326 calendar.navigateUp(); 13327 prevent = true; 13328 } else if (key == keys.DOWN) { 13329 that._navigateDown(); 13330 prevent = true; 13331 } 13332 } else { 13333 if (key == keys.RIGHT) { 13334 value = 1; 13335 prevent = true; 13336 } else if (key == keys.LEFT) { 13337 value = -1; 13338 prevent = true; 13339 } else if (key == keys.UP) { 13340 value = index === 0 ? -7 : -4; 13341 prevent = true; 13342 } else if (key == keys.DOWN) { 13343 value = index === 0 ? 7 : 4; 13344 prevent = true; 13345 } else if (key == keys.ENTER) { 13346 that._navigateDown(); 13347 prevent = true; 13348 } else if (key == keys.HOME || key == keys.END) { 13349 method = key == keys.HOME ? FIRST : "last"; 13350 currentValue = view[method](currentValue); 13351 prevent = true; 13352 } else if (key == keys.PAGEUP) { 13353 prevent = true; 13354 calendar.navigateToPast(); 13355 } else if (key == keys.PAGEDOWN) { 13356 prevent = true; 13357 calendar.navigateToFuture(); 13358 } 13359 13360 if (value || method) { 13361 if (!method) { 13362 view.setDate(currentValue, value); 13363 } 13364 13365 that._current = currentValue = restrictValue(currentValue, options.min, options.max); 13366 calendar._focus(currentValue); 13367 } 13368 } 13369 13370 if (prevent) { 13371 e.preventDefault(); 13372 } 13373 }, 13374 13375 value: function(value) { 13376 var that = this, 13377 calendar = that.calendar, 13378 options = that.options; 13379 13380 that._value = value; 13381 that._current = new DATE(restrictValue(value, options.min, options.max)); 13382 13383 if (calendar.element.data(DATEVIEW) === that) { 13384 calendar._focus(that._current); 13385 calendar.value(value); 13386 } 13387 }, 13388 13389 _click: function(e) { 13390 if (e.currentTarget.className.indexOf(SELECTED) !== -1) { 13391 this.close(); 13392 } 13393 }, 13394 13395 _navigate: function() { 13396 var that = this, 13397 calendar = that.calendar; 13398 13399 that._current = new DATE(calendar._current); 13400 calendar._focus(calendar._current); 13401 }, 13402 13403 _navigateDown: function() { 13404 var that = this, 13405 calendar = that.calendar, 13406 currentValue = calendar._current, 13407 cell = calendar._table.find("." + FOCUSED), 13408 value = cell.children(":" + FIRST).attr(kendo.attr("value")).split("/"); 13409 13410 //Safari cannot create corretly date from "1/1/2090" 13411 value = new DATE(value[0], value[1], value[2]); 13412 13413 if (!cell[0] || cell.hasClass(SELECTED)) { 13414 that.close(); 13415 return; 13416 } 13417 13418 calendar._view.setDate(currentValue, value); 13419 calendar.navigateDown(currentValue); 13420 }, 13421 13422 _option: function(option, value) { 13423 var that = this, 13424 options = that.options, 13425 calendar = that.calendar; 13426 13427 options[option] = value; 13428 13429 if (calendar.element.data(DATEVIEW) === that) { 13430 calendar[option](value); 13431 } 13432 }, 13433 13434 _templates: function() { 13435 var that = this, 13436 options = that.options, 13437 footer = options.footer,
13438 month = options.month || {}, 13439 content = month.content, 13440 empty = month.empty; 13441 13442 that.month = { 13443 content: template('<td#=data.cssClass#><a class="k-link" href="\\#" ' + kendo.attr("value") + '="#=data.dateString#" title="#=data.title#">' + (content || "#=data.value#") + '</a></td>', { useWithBlock: !!content }), 13444 empty: template("<td>" + (empty || " ") + "</td>", { useWithBlock: !!empty }) 13445 }; 13446 13447 if (footer) { 13448 that.footer = template(footer, { useWithBlock: false }); 13449 } 13450 } 13451 }; 13452 13453 kendo.DateView = DateView; 13454 13455 var DatePicker = Widget.extend(/** @lends kendo.ui.DatePicker.prototype */{ 13456 /** 13457 * @constructs 13458 * @extends kendo.ui.Widget 13459 * @param {DomElement} element DOM element 13460 * @param {Object} options Configuration options. 13461 * @option {Date} [value] <null> Specifies the selected date. 13462 * _example 13463 * // set the selected value to January 1st, 2011 13464 * $("#datePicker").kendoDatePicker({ 13465 * value: new Date(2011, 0, 1) 13466 * }); 13467 * _exampleTitle To set after initialization 13468 * _example 13469 * // get a reference to the datePicker widget 13470 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13471 * // set the selected date on the datePicker to January 1st, 2011 13472 * datePicker.value(new Date(2011, 0, 1)); 13473 * @option {Date} [min] <Date(1900, 0, 1)> Specifies the minimum date that the calendar can show. 13474 * _example 13475 * // set the min date to Jan 1st, 2011 13476 * $("#datePicker").kendoDatePicker({ 13477 * min: new Date(2011, 0, 1) 13478 * }); 13479 * _exampleTitle To set after initialization 13480 * _example 13481 * // get a reference to the datePicker widget 13482 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13483 * // set the min date to Jan 1st, 2011 13484 * datePicker.min(new Date(2011, 0, 1)); 13485 * @option {Date} [max] <Date(2099, 11, 31)> Specifies the maximum date, which the calendar can show. 13486 * _example 13487 * $("#datePicker").kendoDatePicker({ 13488 * max: new Date(2013, 0, 1) // sets max date to Jan 1st, 2013 13489 * }); 13490 * _exampleTitle To set after initialization 13491 * _example 13492 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13493 * // set the max date to Jan 1st, 2013 13494 * datePicker.max(new Date(2013,0, 1)); 13495 * @option {String} [format] <MM/dd/yyyy> Specifies the format, which is used to parse value set with value() method. 13496 * _example 13497 * $("#datePicker").kendoDatePicker({ 13498 * format: "yyyy/MM/dd" 13499 * }); 13500 * @option {String} [start] <month> Specifies the start view. 13501 * The following settings are available for the <b>start</b> value: 13502 * <div class="details-list"> 13503 * <dl> 13504 * <dt> 13505 * <code>"month"</code> 13506 * </dt> 13507 * <dd> 13508 * shows the days of the month 13509 * </dd> 13510 * <dt> 13511 * <code>"year"</code> 13512 * </dt> 13513 * <dd> 13514 * shows the months of the year 13515 * </dd> 13516 * <dt> 13517 * <code>"decade"</code> 13518 * </dt> 13519 * <dd> 13520 * shows the years of the decade 13521 * </dd> 13522 * <dt> 13523 * <code>"century"</code> 13524 * </dt> 13525 * <dd> 13526 * shows the decades from the centery 13527 * </dd> 13528 * </dl> 13529 * </div> 13530 * _example 13531 * $("#datePicker").kendoDatePicker({ 13532 * start: "decade" // the datePicker will start with a decade display 13533 * }); 13534 * @option {String} [depth] Specifies the navigation depth. The following 13535 * settings are available for the <b>depth</b> value: 13536 * <div class="details-list"> 13537 * <dl> 13538 * <dt> 13539 * <code>"month"</code> 13540 * </dt> 13541 * <dd> 13542 * shows the days of the month 13543 * </dd> 13544 * <dt> 13545 * <code>"year"</code> 13546 * </dt> 13547 * <dd> 13548 * shows the months of the year 13549 * </dd> 13550 * <dt> 13551 * <code>"decade"</code> 13552 * </dt> 13553 * <dd> 13554 * shows the years of the decade 13555 * </dd> 13556 * <dt> 13557 * <code>"century"</code> 13558 * </dt> 13559 * <dd> 13560 * shows the decades from the centery 13561 * </dd> 13562 * </dl> 13563 * </div> 13564 * _example 13565 * $("#datePicker").kendoDatePicker({ 13566 * start: "decade", 13567 * depth: "year" // the datePicker will only go to the year level 13568 * }); 13569 * @option {Function} [footer] <> Template to be used for rendering the footer of the calendar. 13570 * _example 13571 * // DatePicker initialization 13572 * <script> 13573 * $("#datePicker").kendoDatePicker({ 13574 * footer: kendo.template("Today - #=kendo.toString(data, 'd') #") 13575 * }); 13576 * </script> 13577 * @option {Object} [month] <> Templates for the cells rendered in the calendar "month" view. 13578 * @option {Function} [month.content] <> Template to be used for rendering the cells in the calendar "month" view, which are in range. 13579 * _example 13580 * //template 13581 * <script id="cellTemplate" type="text/x-kendo-tmpl"> 13582 * <div class="${ data.value < 10 ? exhibition : party }"> 13583 * </div> 13584 * ${ data.value } 13585 * </script> 13586 * 13587 * //datePicker initialization 13588 * <script> 13589 * $("#datePicker").kendoDatePicker({ 13590 * month: { 13591 * content: kendo.template($("#cellTemplate").html()), 13592 * } 13593 * }); 13594 * </script> 13595 * 13596 * @option {Function} [month.empty] 13597 * The template used for rendering the cells in the calendar "month" view, which are not in the range between 13598 * the minimum and maximum values. 13599 * 13600 * @option {Object} [animation] 13601 * The animation(s) used for opening and/or closing the pop-up. Setting this value to <strong>false</strong> 13602 * will disable the animation(s). 13603 * 13604 * @option {Object} [animation.open] 13605 * The animation(s) used for displaying of the pop-up. 13606 * 13607 * _exampleTitle Fade-in the pop-up over 300 milliseconds 13608 * _example 13609 * $("#datePicker").kendoDatePicker({ 13610 * animation: { 13611 * open: { 13612 * effects: "fadeIn", 13613 * duration: 300, 13614 * show: true 13615 * } 13616 * } 13617 * }); 13618 * 13619 * @option {Object} [animation.close] 13620 * The animation(s) used for hiding of the pop-up. 13621 * 13622 * _exampleTitle Fade-out the pop-up over 300 milliseconds 13623 * _example 13624 * $("#datePicker").kendoDatePicker({ 13625 * animation: { 13626 * close: { 13627 * effects: "fadeOut", 13628 * duration: 300, 13629 * show: false, 13630 * hide: true 13631 * } 13632 * } 13633 * }); 13634 * 13635 */ 13636 init: function(element, options) { 13637 var that = this, 13638 dateView; 13639 13640 Widget.fn.init.call(that, element, options); 13641 element = that.element; 13642 options = that.options; 13643 13644 calendar.validate(options); 13645 13646 that._wrapper(); 13647 13648 that.dateView = dateView = new DateView(extend({}, options, { 13649 anchor: that.wrapper, 13650 change: function() { 13651 // calendar is the current scope 13652 that._change(this.value()); 13653 that.close(); 13654 }, 13655 close: function(e) { 13656 if (that.trigger(CLOSE)) { 13657 e.preventDefault(); 13658 } 13659 }, 13660 open: function(e) { 13661 if (that.trigger(OPEN)) { 13662 e.preventDefault(); 13663 } 13664 } 13665 })); 13666 13667 that._icon(); 13668 13669 element 13670 .addClass("k-input") 13671 .bind({ 13672 keydown: proxy(that._keydown, that), 13673 focus: function(e) { 13674 that._inputWrapper.addClass(FOCUSED); 13675 }, 13676 blur: proxy(that._blur, that) 13677 }) 13678 .closest("form") 13679 .bind("reset", function() { 13680 that.value(element[0].defaultValue); 13681 }); 13682 13683 that.enable(!element.is('[disabled]')); 13684 that.value(options.value || that.element.val()); 13685 13686 kendo.notify(that); 13687 }, 13688 events: [ 13689 /** 13690 * Fires when the selected date is changed 13691 * @name kendo.ui.DatePicker#change 13692 * @event 13693 * @param {Event} e 13694 * @example 13695 * $("#datePicker").kendoDatePicker({ 13696 * change: function(e) { 13697 * // handle event 13698 * } 13699 * }); 13700 * @exampleTitle To set after initialization 13701 * @example 13702 * // get a reference to the datePicker widget 13703 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13704 * // bind to the change event 13705 * datePicker.bind("change", function(e) { 13706 * // handle event 13707 * }); 13708 */ 13709 /** 13710 * Fires when the calendar is opened 13711 * @name kendo.ui.DatePicker#open 13712 * @event 13713 * @param {Event} e 13714 * @example 13715 * $("#datePicker").kendoDatePicker({ 13716 * open: function(e) { 13717 * // handle event 13718 * } 13719 * }); 13720 * @exampleTitle To set after initialization 13721 * @example 13722 * // get a reference to the datePicker widget 13723 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13724 * // bind to the open event 13725 * datePicker.bind("open", function(e) { 13726 * // handle event 13727 * }); 13728 */ 13729 /** 13730 * Fires when the calendar is closed 13731 * @name kendo.ui.DatePicker#close 13732 * @event 13733 * @param {Event} e 13734 * @example 13735 * $("#datePicker").kendoDatePicker({ 13736 * close: function(e) { 13737 * // handle event 13738 * } 13739 * }); 13740 * @exampleTitle To set after initialization 13741 * @example 13742 * // get a reference to the datePicker widget 13743 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13744 * // bind to the close event 13745 * datePicker.bind("close", function(e) { 13746 * // handle event 13747 * }); 13748 */ 13749 OPEN, 13750 CLOSE, 13751 CHANGE], 13752 options: { 13753 name: "DatePicker", 13754 value: null, 13755 format: "", 13756 min: new Date(1900, 0, 1), 13757 max: new Date(2099, 11, 31), 13758 footer: '#= kendo.toString(data,"D") #', 13759 start: MONTH, 13760 depth: MONTH, 13761 animation: {}, 13762 month : {} 13763 }, 13764 13765 setOptions: function(options) { 13766 var that = this; 13767 13768 Widget.fn.setOptions.call(that, options); 13769 13770 calendar.validate(that.options); 13771 13772 extend(that.dateView.options, that.options); 13773 }, 13774 13775 /** 13776 * Enable/Disable the datePicker widget. 13777 * @param {Boolean} enable The argument, which defines whether to enable/disable the datePicker. 13778 * @example 13779 * // get a reference to the datepicker widget 13780 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13781 * 13782 * // disables the datePicker 13783 * datePicker.enable(false); 13784 * 13785 * // enables the datePicker 13786 * datePicker.enable(true); 13787 */ 13788 enable: function(enable) { 13789 var that = this, 13790 icon = that._icon.unbind(CLICK + " " + MOUSEDOWN), 13791 wrapper = that._inputWrapper.unbind(HOVEREVENTS), 13792 element = that.element; 13793 13794 if (enable === false) { 13795 wrapper 13796 .removeClass(DEFAULT) 13797 .addClass(STATEDISABLED); 13798 13799 element.attr(DISABLED, DISABLED); 13800 } else { 13801 wrapper 13802 .addClass(DEFAULT) 13803 .removeClass(STATEDISABLED) 13804 .bind(HOVEREVENTS, that._toggleHover); 13805 13806 element 13807 .removeAttr(DISABLED); 13808 13809 icon.bind(CLICK, proxy(that._click, that)) 13810 .bind(MOUSEDOWN, function(e) { e.preventDefault(); }); 13811 } 13812 }, 13813 13814 /** 13815 * Opens the calendar. 13816 * @name kendo.ui.DatePicker#open 13817 * @function 13818 * @example 13819 * // get a reference to the datepicker widget 13820 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13821 * // open the datepicker 13822 * datePicker.open(); 13823 */ 13824 open: function() { 13825 this.dateView.open(); 13826 }, 13827 13828 /** 13829 * Closes the calendar. 13830 * @name kendo.ui.DatePicker#close 13831 * @function 13832 * @example 13833 * // get a reference to the datepicker widget 13834 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13835 * // close the datepicker 13836 * datePicker.close(); 13837 */ 13838 close: function() { 13839 this.dateView.close(); 13840 }, 13841 13842 /** 13843 * Gets/Sets the min value of the datePicker. 13844 * @param {Date | String} value The min date to set. 13845 * @returns {Date} The min value of the datePicker. 13846 * @example 13847 * // get a reference to the datepicker widget 13848 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13849 * 13850 * // get the min value of the datePicker. 13851 * var min = datePicker.min(); 13852 * 13853 * // set the min value of the datePicker. 13854 * datePicker.min(new Date(1900, 0, 1)); 13855 */ 13856 min: function(value) { 13857 return this._option(MIN, value); 13858 }, 13859 13860 /** 13861 * Gets/Sets the max value of the datePicker. 13862 * @param {Date | String} value The max date to set. 13863 * @returns {Date} The max value of the datePicker. 13864 * @example 13865 * // get a reference to the datepicker widget 13866 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13867 * 13868 * // get the max value of the datePicker. 13869 * var max = datePicker.max();
13870 * 13871 * // set the max value of the datePicker. 13872 * datePicker.max(new Date(1900, 0, 1)); 13873 */ 13874 max: function(value) { 13875 return this._option(MAX, value); 13876 }, 13877 13878 /** 13879 * Gets/Sets the value of the datePicker. 13880 * @param {Date | String} value The value to set. 13881 * @returns {Date} The value of the datePicker. 13882 * @example 13883 * // get a reference to the datepicker widget 13884 * var datePicker = $("#datePicker").data("kendoDatePicker"); 13885 * 13886 * // get the value of the datePicker. 13887 * var value = datePicker.value(); 13888 * 13889 * // set the value of the datePicker. 13890 * datePicker.value("10/10/2000"); //parse "10/10/2000" date and selects it in the calendar. 13891 */ 13892 value: function(value) { 13893 var that = this; 13894 13895 if (value === undefined) { 13896 return that._value; 13897 } 13898 13899 that._old = that._update(value); 13900 }, 13901 13902 _toggleHover: function(e) { 13903 if (!touch) { 13904 $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter"); 13905 } 13906 }, 13907 13908 _blur: function() { 13909 var that = this; 13910 13911 that.close(); 13912 that._change(that.element.val()); 13913 that._inputWrapper.removeClass(FOCUSED); 13914 }, 13915 13916 _click: function() { 13917 var that = this, 13918 element = that.element; 13919 13920 if (!touch && element[0] !== document.activeElement) { 13921 element.focus(); 13922 } 13923 13924 that.dateView.toggle(); 13925 }, 13926 13927 _change: function(value) { 13928 var that = this; 13929 13930 value = that._update(value); 13931 13932 if (+that._old != +value) { 13933 that._old = value; 13934 that.trigger(CHANGE); 13935 13936 // trigger the DOM change event so any subscriber gets notified 13937 that.element.trigger(CHANGE); 13938 } 13939 }, 13940 13941 _keydown: function(e) { 13942 var that = this, 13943 dateView = that.dateView; 13944 13945 if (!dateView.popup.visible() && e.keyCode == keys.ENTER) { 13946 that._change(that.element.val()); 13947 } else { 13948 dateView.move(e); 13949 } 13950 }, 13951 13952 _icon: function() { 13953 var that = this, 13954 element = that.element, 13955 icon; 13956 13957 icon = element.next("span.k-select"); 13958 13959 if (!icon[0]) { 13960 icon = $('<span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-icon-calendar">select</span></span>').insertAfter(element); 13961 } 13962 13963 that._icon = icon; 13964 }, 13965 13966 _option: function(option, value) { 13967 var that = this, 13968 options = that.options; 13969 13970 if (value === undefined) { 13971 return options[option]; 13972 } 13973 13974 value = parse(value, options.format); 13975 13976 if (!value) { 13977 return; 13978 } 13979 13980 options[option] = new DATE(value); 13981 that.dateView[option](value); 13982 }, 13983 13984 _update: function(value) { 13985 var that = this, 13986 options = that.options, 13987 format = options.format, 13988 date = parse(value, format); 13989 13990 if (!isInRange(date, options.min, options.max)) { 13991 date = null; 13992 } 13993 13994 that._value = date; 13995 that.dateView.value(date); 13996 that.element.val(date ? kendo.toString(date, format) : value); 13997 13998 return date; 13999 }, 14000 14001 _wrapper: function() { 14002 var that = this, 14003 element = that.element, 14004 wrapper; 14005 14006 wrapper = element.parents(".k-datepicker"); 14007 14008 if (!wrapper[0]) { 14009 wrapper = element.wrap(SPAN).parent().addClass("k-picker-wrap k-state-default"); 14010 wrapper = wrapper.wrap(SPAN).parent(); 14011 } 14012 14013 wrapper[0].style.cssText = element[0].style.cssText; 14014 element.css({ 14015 width: "100%", 14016 height: element[0].style.height 14017 }); 14018 14019 that.wrapper = wrapper.addClass("k-widget k-datepicker k-header"); 14020 that._inputWrapper = $(wrapper[0].firstChild); 14021 } 14022 });
14023 14024 ui.plugin(DatePicker); 14025 14026})(jQuery); 14027(function ($, undefined) { 14028 /** 14029 * @name kendo.ui.AutoComplete.Description 14030 * 14031 * @section 14032 * <p> 14033 * The <b>AutoComplete</b> provides suggestions depending on the typed 14034 * text. It also allows multiple value entries. The suggestions shown by 14035 * the <b>AutoComplete</b> can come from a local Array or from a remote 14036 * data service. 14037 * </p> 14038 * <h3>Getting Started</h3> 14039 * 14040 * @exampleTitle Create a HTML input element 14041 * @example 14042 * <input id="autoComplete" /> 14043 * 14044 * @exampleTitle Initialize the AutoComplete using a jQuery selector 14045 * @example 14046 * $(document).ready(function() { 14047 * $("#autoComplete").kendoAutoComplete(["Item1", "Item2"]); 14048 * }); 14049 * 14050 * @section <h3>AutoComplete Suggestions</h3> 14051 * <p> 14052 * There are two primary ways to provide the <b>AutoComplete</b> 14053 * suggestions: 14054 * </p> 14055 * <ol> 14056 * <li>From a local array</li> 14057 * <li>From a remote data service</li> 14058 * </ol> 14059 * <p> 14060 * Locally defined values are best for small, fixed sets of suggestions. 14061 * Remote suggestions should be used for larger data sets. When used 14062 * with the <a href="../datasource/index.html">DataSource</a> component, 14063 * filtering large remote data services can be pushed to the server as 14064 * well, maximizing client-side performance. 14065 * </p> 14066 * <h3>Local Suggestions</h3> 14067 * <p> 14068 * To configure and provide <b>AutoComplete</b> suggestions locally, you 14069 * can either pass an array directly to its constructor or you can set 14070 * the dataSource property to an local array. 14071 * </p> 14072 * 14073 * @exampleTitle Directly initialize suggestions in constructor 14074 * @example 14075 * $("#autoComplete").kendoAutoComplete(["Item1", "Item2", "Item3"]); 14076 * 14077 * @exampleTitle Using dataSource property to bind to local Array 14078 * @example 14079 * var data = ["Item1", "Item2", "Item3"]; 14080 * $("#autoComplete").kendoAutoComplete({ 14081 * dataSource: data 14082 * }); 14083 * 14084 * @section 14085 * <h3>Remote Suggestions</h3> 14086 * <p> 14087 * The easiest way to bind an <b>AutoComplete</b> to remote 14088 * suggestions is to use the 14089 * <a href="../datasource/index.html">DataSource</a> component; an 14090 * abstraction for local and remote data. The <b>DataSource</b> 14091 * component can be used to serve data from a variety of data services, 14092 * such as 14093 * <a href="http://en.wikipedia.org/wiki/XML">XML</a>, 14094 * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a>, and 14095 * <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>. 14096 * </p> 14097 * 14098 * @exampleTitle Using the Kendo UI Web DataSource component to bind to 14099 * remote suggestions with OData 14100 * @example 14101 * $(document).ready(function(){ 14102 * $("#autoComplete").kendoAutoComplete({ 14103 * minLength: 3, 14104 * dataTextField: "Name", // JSON property name to use 14105 * dataSource: new kendo.data.DataSource({ 14106 * type: "odata", // specifies data protocol 14107 * pageSize: 10, // limits result set 14108 * transport: { 14109 * read: "http://odata.netflix.com/Catalog/Titles" 14110 * } 14111 * }) 14112 * }) 14113 * }); 14114 * 14115 * @exampleTitle Using the Kendo UI Web DataSource to bind to JSONP 14116 * suggestions 14117 * @example 14118 * $(document).ready(function(){ 14119 * $("#autoComplete").kendoAutoComplete({ 14120 * minLength:6, 14121 * dataTextField:"title", 14122 * filter: "contains", 14123 * dataSource: new kendo.data.DataSource({ 14124 * transport: { 14125 * read: { 14126 * url: "http://api.geonames.org/wikipediaSearchJSON", 14127 * data: { 14128 * q: function(){ 14129 * return $("#autoComplete").data("kendoAutoComplete").value(); 14130 * }, 14131 * maxRows: 10, 14132 * username: "demo" 14133 * } 14134 * } 14135 * }, 14136 * schema: { 14137 * data:"geonames" 14138 * } 14139 * }), 14140 * change: function(){ 14141 * this.dataSource.read(); 14142 * } 14143 * }) 14144 * }); 14145 * 14146 * @section 14147 * <h3>Accessing an Existing AutoComplete</h3> 14148 * <p> 14149 * You can reference an existing <b>AutoComplete</b> instance via 14150 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. 14151 * Once a reference has been established, you can use the API to control 14152 * its behavior. 14153 * </p> 14154 * 14155 * @exampleTitle Accessing an existing AutoComplete instance 14156 * @example 14157 * var autoComplete = $("#autoComplete").data("kendoAutoComplete"); 14158 * 14159 */ 14160 var kendo = window.kendo, 14161 support = kendo.support, 14162 placeholderSupported = support.placeholder, 14163 ui = kendo.ui, 14164 keys = kendo.keys, 14165 DataSource = kendo.data.DataSource, 14166 List = ui.List, 14167 CHANGE = "change", 14168 DEFAULT = "k-state-default", 14169 DISABLED = "disabled", 14170 FOCUSED = "k-state-focused", 14171 SELECTED = "k-state-selected", 14172 STATEDISABLED = "k-state-disabled", 14173 HOVER = "k-state-hover", 14174 HOVEREVENTS = "mouseenter mouseleave", 14175 caretPosition = List.caret, 14176 selectText = List.selectText, 14177 proxy = $.proxy; 14178 14179 function indexOfWordAtCaret(caret, text, separator) { 14180 return separator ? text.substring(0, caret).split(separator).length - 1 : 0; 14181 } 14182 14183 function wordAtCaret(caret, text, separator) { 14184 return text.split(separator)[indexOfWordAtCaret(caret, text, separator)]; 14185 } 14186 14187 function replaceWordAtCaret(caret, text, word, separator) { 14188 var words = text.split(separator); 14189 14190 words.splice(indexOfWordAtCaret(caret, text, separator), 1, word); 14191 14192 if (separator && words[words.length - 1] !== "") { 14193 words.push(""); 14194 } 14195 14196 return words.join(separator); 14197 } 14198 14199 function moveCaretAtEnd(element) { 14200 var length = element.value.length; 14201 14202 selectText(element, length, length); 14203 } 14204 14205 var AutoComplete = List.extend/** @lends kendo.ui.AutoComplete.prototype */({ 14206 /** 14207 * @constructs 14208 * @extends kendo.ui.List 14209 * @param {DomElement} element DOM element 14210 * @param {Object} options Configuration options. 14211 * @option {Object | kendo.data.DataSource } [dataSource] The set of data that the AutoComplete will be bound to. 14212 * Either a local JavaScript object, or an instance of the Kendo UI DataSource. 14213 * _exampleTitle Bind to local array 14214 * _example 14215 * var items = [ { Name: "Item 1" }, { Name: "Item 2"} ]; 14216 * $("#autoComplete").kendoAutoComplete({ dataSource: items }); 14217 * _exampleTitle Bind to a remote URL 14218 * _example 14219 * $("#autocomplete").kendoAutoComplete({ 14220 * dataSource: new kendo.data.DataSource({ 14221 * transport: { 14222 * read: "Items/GetData" // url to server method which returns data 14223 * } 14224 * }); 14225 * }); 14226 * @option {Boolean} [enable] <true> Controls whether the AutoComplete should be initially enabled. 14227 * _example 14228 * // disable the autocomplete when it is created (enabled by default) 14229 * $("#autoComplete").kendoAutoComplete({ 14230 * enable: false 14231 * }); 14232 * @option {Boolean} [highlightFirst] <true> Controls whether the first item will be automatically highlighted. 14233 * _example 14234 * $("#autocomplete").kendoAutoComplete({ 14235 * highlightFirst: false //no of the suggested items will be highlighted 14236 * }); 14237 * @option {Boolean} [suggest] <false> Controls whether the AutoComplete should automatically auto-type the rest of text. 14238 * _example 14239 * // turn on auto-typing (off by default) 14240 * $("#autoComplete").kendoAutoComplete({ 14241 * suggest: true 14242 * }); 14243 * @option {Number} [delay] <200> Specifies the delay in ms after which the AutoComplete will start filtering the dataSource. 14244 * _example 14245 * // set the delay to 500 milliseconds 14246 * $("#autoComplete").kendoAutoComplete({ 14247 * delay: 500 14248 * }); 14249 * @option {Number} [minLength] <1> Specifies the minimum number of characters that should be typed before the AutoComplete queries 14250 * the dataSource. 14251 * _example 14252 * // wait until the user types 3 characters before querying the server 14253 * $("#autoComplete").kendoAutoComplete({ 14254 * minLength: 3 14255 * }); 14256 * @option {String} [dataTextField] <null> Sets the field of the data item that provides the text content of the list items. 14257 * _example 14258 * var items = [ { ID: 1, Name: "Item 1" }, { ID: 2, Name: "Item 2"} ]; 14259 * $("#autoComplete").kendoAutoComplete({ 14260 * dataSource: items, 14261 * dataTextField: "Name" 14262 * }); 14263 * @option {String} [filter] <"startswith"> Defines the type of filtration. This value is handled by the remote data source. 14264 * _example 14265 * // send a filter value of 'contains' to the server 14266 * $("#autoComplete").kendoAutoComplete({ 14267 * filter: 'contains' 14268 * }); 14269 * @option {String} [ignoreCase] <true> Defines whether the filtration should be case sensitive. 14270 * _example 14271 * $("#autoComplete").kendoAutoComplete({ 14272 * filter: 'contains', 14273 * ignoreCase: false //now filtration will be case sensitive 14274 * }); 14275 * @option {Number} [height] <200> Sets the height of the drop-down list in pixels. 14276 * _example 14277 * // set the height of the drop-down list that appears when the autocomplete is activated to 500px 14278 * $("#autoComplete").kendoAutoComplete({ 14279 * height: 500 14280 * }); 14281 * @option {String} [separator] <""> Sets the separator for completion. Empty by default, allowing for only one completion. 14282 * _example 14283 * // set completion separator to , 14284 * $("#autoComplete").kendoAutoComplete({ 14285 * separator: ", " 14286 * }); 14287 * @option {Function} [template] Template to be used for rendering the items in the list. 14288 * _example 14289 * //template 14290 * <script id="template" type="text/x-kendo-tmpl"> 14291 * # if (data.BoxArt.SmallUrl) { # 14292 * <img src="${ data.BoxArt.SmallUrl }" alt="${ data.Name }" />Title:${ data.Name }, Year: ${ data.Name } 14293 * # } else { # 14294 * <img alt="${ data.Name }" />Title:${ data.Name }, Year: ${ data.Name } 14295 * # } # 14296 * </script> 14297 * 14298 * //autocomplete initialization 14299 * <script> 14300 * $("#autocomplete").kendoAutoComplete({ 14301 * dataSource: dataSource, 14302 * dataTextField: "Name", 14303 * dataValueField: "Id", 14304 * template: kendo.template($("#template").html()) 14305 * }); 14306 * </script> 14307 * @option {Object} [animation] <> Animations to be used for opening/closing the popup. Setting to false will turn of the animation. 14308 * @option {Object} [animation.open] <> Animation to be used for opening of the popup. 14309 * _example 14310 * //autocomplete initialization 14311 * <script> 14312 * $("#autocomplete").kendoAutoComplete({ 14313 * dataSource: dataSource, 14314 * animation: { 14315 * open: { 14316 * effects: "fadeIn", 14317 * duration: 300, 14318 * show: true 14319 * } 14320 * } 14321 * }); 14322 * </script> 14323 * @option {Object} [animation.close] <> Animation to be used for closing of the popup. 14324 * _example 14325 * //autocomplete initialization 14326 * <script> 14327 * $("#autocomplete").kendoAutoComplete({ 14328 * dataSource: dataSource, 14329 * animation: { 14330 * close: { 14331 * effects: "fadeOut", 14332 * duration: 300, 14333 * hide: true 14334 * show: false 14335 * } 14336 * } 14337 * }); 14338 * </script> 14339 * @option {String} [placeholder] <""> A string that appears in the textbox when it has no value. 14340 * _example 14341 * //autocomplete initialization 14342 * <script> 14343 * $("#autocomplete").kendoAutoComplete({ 14344 * dataSource: dataSource, 14345 * placeholder: "Enter value..." 14346 * }); 14347 * </script> 14348 * _example 14349 * <input id="autocomplete" placeholder="Enter value..." /> 14350 * 14351 * //combobox initialization 14352 * <script> 14353 * $("#autocomplete").kendoAutoComplete({ 14354 * dataSource: dataSource 14355 * }); 14356 * </script> 14357 */ 14358 init: function (element, options) { 14359 var that = this, wrapper; 14360 14361 options = $.isArray(options) ? { dataSource: options} : options; 14362 14363 List.fn.init.call(that, element, options); 14364 14365 element = that.element; 14366 options = that.options; 14367 14368 options.placeholder = options.placeholder || element.attr("placeholder"); 14369 if (placeholderSupported) { 14370 element.attr("placeholder", options.placeholder); 14371 } 14372 14373 that._wrapper(); 14374 14375 that._accessors(); 14376 14377 that._dataSource(); 14378 14379 element[0].type = "text"; 14380 wrapper = that.wrapper; 14381 14382 element 14383 .attr("autocomplete", "off") 14384 .addClass("k-input") 14385 .bind({ 14386 keydown: proxy(that._keydown, that), 14387 paste: proxy(that._search, that), 14388 focus: function () { 14389 that._prev = that.value(); 14390 that._placeholder(false); 14391 wrapper.addClass(FOCUSED); 14392 clearTimeout(that._bluring); 14393 }, 14394 blur: function () { 14395 that._change(); 14396 that._placeholder(); 14397 wrapper.removeClass(FOCUSED); 14398 } 14399 }); 14400 14401 that._enable(); 14402 14403 that._popup(); 14404 14405 that._old = that.value(); 14406 14407 that._placeholder(); 14408 14409 kendo.notify(that); 14410 }, 14411 14412 options: { 14413 name: "AutoComplete",
14414 suggest: false, 14415 template: "", 14416 dataTextField: "", 14417 minLength: 1, 14418 delay: 200, 14419 height: 200, 14420 filter: "startswith", 14421 ignoreCase: true, 14422 highlightFirst: false, 14423 separator: null, 14424 placeholder: "", 14425 animation: {} 14426 }, 14427 14428 _dataSource: function() { 14429 var that = this; 14430 14431 if (that.dataSource && that._refreshHandler) { 14432 that.dataSource.unbind(CHANGE, that._refreshHandler); 14433 } else { 14434 that._refreshHandler = proxy(that.refresh, that); 14435 } 14436 14437 that.dataSource = DataSource.create(that.options.dataSource) 14438 .bind(CHANGE, that._refreshHandler); 14439 }, 14440 14441 setDataSource: function(dataSource) { 14442 this.options.dataSource = dataSource; 14443 14444 this._dataSource(); 14445 }, 14446 14447 events: [ 14448 /** 14449 * Fires when the drop-down list is opened 14450 * @name kendo.ui.AutoComplete#open 14451 * @event 14452 * @param {Event} e 14453 * @example 14454 * $("#autoComplete").kendoAutoComplete({ 14455 * open: function(e) { 14456 * // handle event 14457 * } 14458 * }); 14459 * @example 14460 * var autoComplete = $("#autoComplete").data("kendoAutoComplete"); 14461 * autoComplete.bind("open", function(e) { 14462 * // handle event 14463 * }); 14464 */ 14465 "open", 14466 /** 14467 * Fires when the drop-down list is closed 14468 * @name kendo.ui.AutoComplete#close 14469 * @event 14470 * @param {Event} e 14471 * @example 14472 * $("#autoComplete").kendoAutoComplete({ 14473 * close: function(e) { 14474 * // handle event 14475 * } 14476 * }); 14477 * @exampleTitle To set after initialization 14478 * @example 14479 * var autoComplete = $("#autoComplete").data("kendoAutoComplete"); 14480 * autoComplete.bind("close", function(e) { 14481 * // handle event 14482 * }); 14483 */ 14484 "close", 14485 /** 14486 * Fires when the value has been changed. 14487 * @name kendo.ui.AutoComplete#change 14488 * @event 14489 * @param {Event} e 14490 * $("#autoComplete").kendoAutoComplete({ 14491 * change: function(e) { 14492 * // handle event 14493 * } 14494 * }); 14495 * @exampleTitle To set after initialization 14496 * @example 14497 * var autoComplete = $("#autoComplete").data("kendoAutoComplete"); 14498 * $("#autoComplete").data("kendoAutoComplete").bind("change", function(e) { 14499 * // handle event 14500 * }); 14501 */ 14502 CHANGE, 14503 /** 14504 * 14505 * Triggered when a Li element is selected. 14506 * 14507 * @name kendo.ui.AutoComplete#select 14508 * @event 14509 * 14510 * @param {Event} e 14511 * 14512 * @param {jQuery} e.item 14513 * The selected item chosen by a user. 14514 * 14515 * @exampleTitle Attach select event handler during initialization; detach via unbind() 14516 * @example 14517 * // event handler for select 14518 * var onSelect = function(e) { 14519 * // access the selected item via e.item (jQuery object) 14520 * }; 14521 * 14522 * // attach select event handler during initialization 14523 * var autocomplete = $("#autocomplete").kendoAutoComplete({ 14524 * select: onSelect 14525 * }); 14526 * 14527 * // detach select event handler via unbind() 14528 * autocomplete.data("kendoAutoComplete").unbind("select", onSelect); 14529 * 14530 * @exampleTitle Attach select event handler via bind(); detach via unbind() 14531 * @example 14532 * // event handler for select 14533 * var onSelect = function(e) { 14534 * // access the selected item via e.item (jQuery object) 14535 * }; 14536 * 14537 * // attach select event handler via bind() 14538 * $("#autocomplete").data("kendoAutoComplete").bind("select", onSelect); 14539 * 14540 * // detach select event handler via unbind() 14541 * $("#autocomplete").data("kendoAutoComplete").unbind("select", onSelect); 14542 * 14543 */ 14544 "select", 14545 "dataBinding", 14546 "dataBound" 14547 ], 14548 setOptions: function(options) { 14549 List.fn.setOptions.call(this, options); 14550 14551 this._template(); 14552 this._accessors(); 14553 }, 14554 14555 /** 14556 * Returns the raw data record at the specified index 14557 * @name kendo.ui.AutoComplete#dataItem 14558 * @function 14559 * @param {Number} index The zero-based index of the data record 14560 * @returns {Object} The raw data record. Returns <i>undefined</i> if no data. 14561 * @example 14562 * var autocomplete = $("#autocomplete").data("kendoAutoComplete"); 14563 * 14564 * // get the dataItem corresponding to the passed index. 14565 * var dataItem = autocomplete.dataItem(1); 14566 */ 14567 14568 /** 14569 * Enable/Disable the autocomplete widget. 14570 * @param {Boolean} enable The argument, which defines whether to enable/disable the autocomplete. 14571 * @example 14572 * // get a reference to the autocomplete widget 14573 * var autocomplete = $("autocomplete").data("kendoAutoComplete"); 14574 * 14575 * // disables the autocomplete 14576 * autocomplete.enable(false); 14577 * 14578 * // enables the autocomplete 14579 * autocomplete.enable(true); 14580 */ 14581 enable: function(enable) { 14582 var that = this, 14583 element = that.element, 14584 wrapper = that.wrapper; 14585 14586 if (enable === false) { 14587 wrapper 14588 .removeClass(DEFAULT) 14589 .addClass(STATEDISABLED) 14590 .unbind(HOVEREVENTS); 14591 14592 element.attr(DISABLED, DISABLED); 14593 } else { 14594 wrapper 14595 .removeClass(STATEDISABLED) 14596 .addClass(DEFAULT) 14597 .bind(HOVEREVENTS, that._toggleHover); 14598 14599 element 14600 .removeAttr(DISABLED); 14601 } 14602 }, 14603 14604 /** 14605 * Closes the drop-down list. 14606 * @example 14607 * // get a reference to the autocomplete widget 14608 * var autocomplete = $("autocomplete").data("kendoAutoComplete"); 14609 * 14610 * autocomplete.close(); 14611 */ 14612 close: function () { 14613 var that = this; 14614 that._current = null; 14615 that.popup.close(); 14616 }, 14617 14618 /** 14619 * Re-render the items in drop-down list. 14620 * @name kendo.ui.AutoComplete#refresh 14621 * @function 14622 * @example 14623 * // get a referenence to the Kendo UI AutoComplete 14624 * var autocomplete = $("autocomplete").data("kendoAutoComplete"); 14625 * // re-render the items in drop-down list. 14626 * autocomplete.refresh();
14627 */ 14628 refresh: function () { 14629 var that = this, 14630 ul = that.ul[0], 14631 popup = that.popup, 14632 options = that.options, 14633 data = that._data(), 14634 length = data.length; 14635 14636 that.trigger("dataBinding"); 14637 14638 ul.innerHTML = kendo.render(that.template, data); 14639 14640 that._height(length); 14641 14642 if (length) { 14643 if (options.highlightFirst) { 14644 that.current($(ul.firstChild)); 14645 } 14646 14647 if (options.suggest) { 14648 that.suggest($(ul.firstChild)); 14649 } 14650 } 14651 14652 if (that._open) { 14653 that._open = false; 14654 popup[length ? "open" : "close"](); 14655 } 14656 14657 if (that._touchScroller) { 14658 that._touchScroller.reset(); 14659 } 14660 14661 that._makeUnselectable(); 14662 14663 that.trigger("dataBound"); 14664 }, 14665 14666 /** 14667 * Selects drop-down list item and sets the text of the autocomplete. 14668 * @param {jQuery Object} li The LI element. 14669 * @example 14670 * // get a reference to the autocomplete widget 14671 * var autocomplete = $("autocomplete").data("kendoAutoComplete"); 14672 * 14673 * // selects by jQuery object 14674 * autocomplete.select(autocomplete.ul.children().eq(0)); 14675 */ 14676 select: function (li) { 14677 this._select(li); 14678 }, 14679 14680 /** 14681 * Filters dataSource using the provided parameter and rebinds drop-down list. 14682 * @param {string} word The filter value. 14683 * @example 14684 * // get a reference to the autocomplete widget 14685 * var autocomplete = $("autocomplete").data("kendoAutoComplete"); 14686 * 14687 * // Searches for item which has "Inception" in the name. 14688 * autocomplete.search("Inception"); 14689 */ 14690 search: function (word) { 14691 var that = this, 14692 options = that.options, 14693 ignoreCase = options.ignoreCase, 14694 separator = options.separator, 14695 length; 14696 14697 word = word || that.value(); 14698 14699 that._current = null; 14700 14701 clearTimeout(that._typing); 14702 14703 if (separator) { 14704 word = wordAtCaret(caretPosition(that.element[0]), word, separator); 14705 } 14706 14707 length = word.length; 14708 14709 if (!length) { 14710 that.popup.close(); 14711 } else if (length >= that.options.minLength) { 14712 that._open = true; 14713 14714 that.dataSource.filter({ 14715 value: ignoreCase ? word.toLowerCase() : word, 14716 operator: options.filter, 14717 field: options.dataTextField, 14718 ignoreCase: ignoreCase 14719 }); 14720 } 14721 }, 14722 14723 /** 14724 * Forces a suggestion onto the text of the AutoComplete. 14725 * @param {string} value Characters to force a suggestion. 14726 * @example 14727 * // note that this suggest is not the same as the configuration method 14728 * // suggest which enables/disables auto suggesting for the AutoComplete 14729 * // 14730 * // get a referenence to the Kendo UI AutoComplete 14731 * var autoComplete = $("#autoComplete").data("kendoAutoComplete"); 14732 * 14733 * // force a suggestion to the item with the name "Inception" 14734 * autoComplete.suggest("Inception"); 14735 */ 14736 suggest: function (word) { 14737 var that = this, 14738 key = that._last, 14739 value = that.value(), 14740 element = that.element[0], 14741 caret = caretPosition(element), 14742 separator = that.options.separator, 14743 words = value.split(separator), 14744 wordIndex = indexOfWordAtCaret(caret, value, separator), 14745 selectionEnd = caret, 14746 idx; 14747 14748 if (key == keys.BACKSPACE || key == keys.DELETE) { 14749 that._last = undefined; 14750 return; 14751 } 14752 14753 word = word || ""; 14754 14755 if (typeof word !== "string") { 14756 idx = List.inArray(word[0], that.ul[0]); 14757 14758 if (idx > -1) { 14759 word = that._text(that._data()[idx]); 14760 } else { 14761 word = ""; 14762 } 14763 } 14764 14765 if (caret <= 0) { 14766 caret = value.toLowerCase().indexOf(word.toLowerCase()) + 1; 14767 } 14768 14769 idx = value.substring(0, caret).lastIndexOf(separator); 14770 idx = idx > -1 ? caret - (idx + separator.length) : caret; 14771 value = words[wordIndex].substring(0, idx); 14772 14773 if (word) { 14774 idx = word.toLowerCase().indexOf(value.toLowerCase()); 14775 if (idx > -1) { 14776 word = word.substring(idx + value.length); 14777 14778 selectionEnd = caret + word.length; 14779 14780 value += word; 14781 } 14782 14783 if (separator && words[words.length - 1] !== "") { 14784 words.push(""); 14785 } 14786 14787 } 14788 14789 words[wordIndex] = value; 14790 14791 that.value(words.join(separator || "")); 14792 14793 selectText(element, caret, selectionEnd); 14794 }, 14795 14796 /** 14797 * Gets/Sets the value of the autocomplete. 14798 * @param {String} value The value to set. 14799 * @returns {String} The value of the autocomplete. 14800 * @example 14801 * // get a reference to the autocomplete widget 14802 * var autocomplete = $("autocomplete").data("kendoAutoComplete"); 14803 * 14804 * // get the text of the autocomplete. 14805 * var value = autocomplete.value(); 14806 */ 14807 value: function (value) { 14808 var that = this, 14809 element = that.element[0]; 14810 14811 if (value !== undefined) { 14812 element.value = value; 14813 } else { 14814 value = element.value; 14815 14816 if (element.className.indexOf("k-readonly") > -1) { 14817 if (value === that.options.placeholder) { 14818 return ""; 14819 } else { 14820 return value; 14821 } 14822 } 14823 14824 return value; 14825 } 14826 }, 14827 14828 _accept: function (li) { 14829 var that = this; 14830 14831 that._focus(li); 14832 moveCaretAtEnd(that.element[0]); 14833 }, 14834 14835 _keydown: function (e) { 14836 var that = this, 14837 ul = that.ul[0], 14838 key = e.keyCode,
14839 current = that._current, 14840 visible = that.popup.visible(); 14841 14842 that._last = key; 14843 14844 if (key === keys.DOWN) { 14845 if (visible) { 14846 that._move(current ? current.next() : $(ul.firstChild)); 14847 } 14848 e.preventDefault(); 14849 } else if (key === keys.UP) { 14850 if (visible) { 14851 that._move(current ? current.prev() : $(ul.lastChild)); 14852 } 14853 e.preventDefault(); 14854 } else if (key === keys.ENTER || key === keys.TAB) { 14855 14856 if (that.popup.visible()) { 14857 e.preventDefault(); 14858 } 14859 14860 that._accept(current); 14861 } else if (key === keys.ESC) { 14862 that.close(); 14863 } else { 14864 that._search(); 14865 } 14866 }, 14867 14868 _move: function (li) { 14869 var that = this; 14870 14871 li = li[0] ? li : null; 14872 14873 that.current(li); 14874 14875 if (that.options.suggest) { 14876 that.suggest(li); 14877 } 14878 }, 14879 14880 _placeholder: function(show) { 14881 if (placeholderSupported) { 14882 return; 14883 } 14884 14885 var that = this, 14886 element = that.element, 14887 placeholder = that.options.placeholder, 14888 value; 14889 14890 if (placeholder) { 14891 value = element.val(); 14892 14893 if (show === undefined) { 14894 show = !value; 14895 } 14896 14897 if (!show) { 14898 if (value !== placeholder) { 14899 placeholder = value; 14900 } else { 14901 placeholder = ""; 14902 } 14903 } 14904 14905 if (value === that._old && !show) { 14906 return; 14907 } 14908 14909 element.toggleClass("k-readonly", show) 14910 .val(placeholder); 14911 } 14912 }, 14913 14914 _search: function () { 14915 var that = this; 14916 clearTimeout(that._typing); 14917 14918 that._typing = setTimeout(function () { 14919 if (that._prev !== that.value()) { 14920 that._prev = that.value(); 14921 that.search(); 14922 } 14923 }, that.options.delay); 14924 }, 14925 14926 _select: function (li) { 14927 var that = this, 14928 separator = that.options.separator, 14929 data = that._data(), 14930 text, 14931 idx; 14932 14933 li = $(li); 14934 14935 if (li[0] && !li.hasClass(SELECTED)) { 14936 idx = List.inArray(li[0], that.ul[0]); 14937 14938 if (idx > -1) { 14939 data = data[idx]; 14940 text = that._text(data); 14941 14942 if (separator) { 14943 text = replaceWordAtCaret(caretPosition(that.element[0]), that.value(), text, separator); 14944 } 14945 14946 that.value(text); 14947 that.current(li.addClass(SELECTED)); 14948 } 14949 } 14950 }, 14951 14952 _toggleHover: function(e) { 14953 if (!support.touch) { 14954 $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter"); 14955 } 14956 }, 14957 14958 _wrapper: function () { 14959 var that = this, 14960 element = that.element, 14961 DOMelement = element[0], 14962 wrapper; 14963 14964 wrapper = element.parent(); 14965 14966 if (!wrapper.is("span.k-widget")) { 14967 wrapper = element.wrap("<span />").parent(); 14968 } 14969 14970 wrapper[0].style.cssText = DOMelement.style.cssText; 14971 element.css({ 14972 width: "100%", 14973 height: DOMelement.style.height 14974 }); 14975 14976 that._focused = that.element; 14977 that.wrapper = wrapper 14978 .addClass("k-widget k-autocomplete k-header") 14979 .addClass(DOMelement.className); 14980 } 14981 });
14982 14983 ui.plugin(AutoComplete); 14984})(jQuery); 14985/** 14986 * @fileOverview Provides a DropDownList implementation which can be used to display a list of values and allows the 14987 * selection of a single value from the list. 14988 */ 14989 14990(function($, undefined) { 14991 /** 14992 * @name kendo.ui.DropDownList.Description 14993 * 14994 * @section 14995 * <p> 14996 * A <strong>DropDownList</strong> displays a list of values and allows the selection of a single value from the 14997 * list.Custom values may not be entered via keyboard input.If you wish permit keyboard input - that is, custom 14998 * values are allowed - use the <a href="../combobox/index.html" title="ComboBox">ComboBox</a>. 14999 * </p> 15000 * <h3>Getting Started</h3> 15001 * <p>There are two ways to create a <strong>DropDownList</strong>:</p> 15002 * <ol> 15003 * <li>From a <select> element with HTML to define the list items</li> 15004 * <li>From an <input> element with databinding to define the listitems</li> 15005 * </ol> 15006 * <p> 15007 * A <strong>DropDownList</strong> will look and operate consistently regardless of the way in which it was 15008 * created. 15009 * </p> 15010 * 15011 * @exampleTitle Creating a DropDownList from existing <input> element 15012 * @example 15013 * <input id="dropDownList" /> 15014 * 15015 * @section 15016 * <p></p> 15017 * <p> 15018 * Initialization of a <strong>DropDownList</strong> should occur after the DOM is fully loaded. It is recommended 15019 * that initialization the <strong>DropDownList</strong> occur within a handler is provided to 15020 * $(document).ready(). 15021 * </p> 15022 * 15023 * @exampleTitle Initialize a DropDownList using a selector within $(document).ready() 15024 * @example 15025 * $(document).ready(function() { 15026 * $("#dropDownList").kendoDropDownList({ 15027 * dataTextField: "text", 15028 * dataValueField: "value", 15029 * dataSource: [ 15030 * { text: "Item1", value: "1" }, 15031 * { text: "Item2", value: "2" } 15032 * ] 15033 * }); 15034 * }); 15035 * 15036 * @exampleTitle Create a DropDownList from existing <select> element with a pre-defined structure 15037 * @example 15038 * <select id="dropDownList"> 15039 * <option>Item 1</option> 15040 * <option>Item 2</option> 15041 * <option>Item 3</option> 15042 * </select> 15043 * 15044 * <script> 15045 * $(document).ready(function(){ 15046 * $("#dropDownList").kendoDropDownList(); 15047 * }); 15048 * </script> 15049 * 15050 * @section 15051 * <h3>Binding to Local or Remote Data</h3> 15052 * <p> 15053 * The <strong>DropDownList</strong> can be bound to both local arrays and remote data via the 15054 * <a href="../datasource/index.html" title="DataSource">DataSource</a> component; an abstraction for local and 15055 * remote data. Local arrays are appropriate for limited value options, while remote data binding is better for 15056 * larger data sets. With remote data-binding, items will be loaded on-demand; when they are displayed. 15057 * </p> 15058 * 15059 * @exampleTitle Binding to a remote OData service 15060 * @example 15061 * $(document).ready(function() { 15062 * $("#titles").kendoDropDownList({ 15063 * index: 0, 15064 * dataTextField: "Name", 15065 * dataValueField: "Id", 15066 * filter: "contains", 15067 * dataSource: { 15068 * type: "odata", 15069 * serverFiltering: true, 15070 * serverPaging: true, 15071 * pageSize: 20, 15072 * transport: { 15073 * read: "http://odata.netflix.com/Catalog/Titles" 15074 * } 15075 * } 15076 * }); 15077 * }); 15078 * 15079 * @section 15080 * @section 15081 * <h3>Customizing Item Templates</h3> 15082 * <p> 15083 * The <strong>DropDownList</strong> uses Kendo UI templates to enable you to control how items are rendered. For 15084 * a detailed description of the capabilities and syntax of the Kendo UI templates, please refer to the 15085 * <a href="../templates/index.html" title="Kendo UI Template">demos and documentation</a>. 15086 * </p> 15087 * 15088 * @exampleTitle Basic item template customization 15089 * @example 15090 * <!-- HTML --> 15091 * <input id="titles" /> 15092 * 15093 * <!-- Template --> 15094 * <script id="scriptTemplate" type="text/x-kendo-template"> 15095 * # if (data.BoxArt.SmallUrl) { # 15096 * <img src="${ data.BoxArt.SmallUrl }" alt="${ data.Name }" /> 15097 * Title:${ data.Name }, Year: ${ data.Name } 15098 * # } else { # 15099 * <img alt="${ data.Name }" /> 15100 * Title:${ data.Name }, Year: ${ data.Name } 15101 * # } # 15102 * </script> 15103 * 15104 * <!-- DropDownList initialization --> 15105 * <script type="text/javascript"> 15106 * $(document).ready(function() { 15107 * $("#titles").kendoDropDownList({ 15108 * autoBind: false, 15109 * dataTextField: "Name", 15110 * dataValueField: "Id", 15111 * template: $("#scriptTemplate").html(), 15112 * dataSource: { 15113 * type: "odata", 15114 * serverFiltering: true, 15115 * serverPaging: true, 15116 * pageSize: 20, 15117 * transport: { 15118 * read: "http://odata.netflix.com/Catalog/Titles" 15119 * } 15120 * } 15121 * }); 15122 * }); 15123 * </script> 15124 * 15125 * @section 15126 * <h3>Accessing an Existing DropDownList</h3> 15127 * <p> 15128 * You can reference an existing <b>DropDownList</b> instance via 15129 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Once a reference has been established, you can 15130 * use the API to control its behavior. 15131 * </p> 15132 * 15133 * @exampleTitle Accessing an existing DropDownList instance 15134 * @example 15135 * var dropDownList = $("#dropDownList").data("kendoDropDownList"); 15136 * 15137 */ 15138 var kendo = window.kendo, 15139 ui = kendo.ui, 15140 Select = ui.Select, 15141 os = kendo.support.mobileOS, 15142 ATTRIBUTE = "disabled", 15143 CHANGE = "change", 15144 SELECT = "select", 15145 FOCUSED = "k-state-focused", 15146 DEFAULT = "k-state-default", 15147 DISABLED = "k-state-disabled", 15148 SELECTED = "k-state-selected", 15149 HOVEREVENTS = "mouseenter mouseleave", 15150 trimRegExp = /^\s/, 15151 proxy = $.proxy; 15152 15153 var DropDownList = Select.extend( /** @lends kendo.ui.DropDownList.prototype */ { 15154 /** 15155 * @constructs 15156 * @extends kendo.ui.Select 15157 * @param {DomElement} element DOM element 15158 * @param {Object} options Configuration options. 15159 * @option {kendo.data.DataSource | Object} [dataSource] Instance of DataSource or the data that the DropDownList will be bound to. 15160 * _exampleTitle Bind to a local array 15161 * _example 15162 * // bind to local data 15163 * var items = [ { Id: 0, Title: "Manager" }, { Id: 1, Title: "Developer" }, { Id: 2, Title: "Vice President" } ]; 15164 * $("#dropdownlist").kendoDropDownList({ 15165 * dataSource: items, 15166 * dataTextField: "Title", 15167 * dataValueField: "Id" 15168 * }); 15169 * _exampleTitle Bind to a remote URL 15170 * _example 15171 * $("#dropdownlist").kendoDropDownList({ 15172 * dataSource: { 15173 * transport: { 15174 * read: "titles.json" 15175 * } 15176 * }, 15177 * dataTextField: "Title", 15178 * dataValueField: "Id" 15179 * }); 15180 * @option {Boolean} [enable] <true> Controls whether the DropDownList should be initially enabled. 15181 * _example 15182 * $("#dropdownlist").kendoDropDownList({ 15183 * enabled: false // dropdown list will not be enabled 15184 * }); 15185 * _exampleTitle To set after initialization 15186 * _example 15187 * // get a reference to the dropdown list 15188 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15189 * // disable the dropdown 15190 * dropdownlist.enable(false); 15191 * @option {Number} [index] <0> Defines the initial selected item. 15192 * _example 15193 * $("#dropdownlist").kendoDropDownList({ 15194 * index: 1 // selects the second item in the dropdown list 15195 * }); 15196 * @option {Boolean} [autoBind] <true> Controls whether to bind the widget on initialization. 15197 * _example 15198 * $("#dropdownlist").kendoDropDownList({ 15199 * autoBind: false 15200 * }); 15201 * @option {Number} [delay] <500> Specifies the delay in ms before the search text typed by the end user is cleared. 15202 * _example 15203 * $("#dropdownlist").kendoDropDownList({ 15204 * delay: 1000 // wait 1 second before clearing the user input 15205 * }); 15206 * @option {String} [dataTextField] <""> Sets the field of the data item that provides the text content of the list items. 15207 * _example 15208 * var items = [ { Id: 0, Title: "Manager" }, { Id: 1, Title: "Developer" }, { Id: 2, Title: "Vice President" } ]; 15209 * $("#dropdownlist").kendoDropDownList({ 15210 * dataSource: items, 15211 * dataTextField: "Title", 15212 * dataValueField: "Id" 15213 * }); 15214 * @option {String} [dataValueField] <""> Sets the field of the data item that provides the value content of the list items. 15215 * _example 15216 * var items = [ { Id: 0, Title: "Manager" }, { Id: 1, Title: "Developer" }, { Id: 2, Title: "Vice President" } ]; 15217 * $("#dropdownlist").kendoDropDownList({ 15218 * dataSource: items, 15219 * dataTextField: "Title", 15220 * dataValueField: "Id" 15221 * }); 15222 * @option {Number} [height] <200> Define the height of the drop-down list in pixels. 15223 * _example 15224 * $("#dropdownlist").kendoDropDownList({ 15225 * height: 400 15226 * }); 15227 * @option {String} [optionLabel] Define the text of the default empty item. 15228 * _example 15229 * $("#dropdownlist").kendoDropDownList({ 15230 * optionLabel: "Select An Option" 15231 * }); 15232 * @option {Function} [template] Template to be used for rendering the items in the list. 15233 * _example 15234 * //template 15235 * <script id="template" type="text/x-kendo-tmpl"> 15236 * # if (data.BoxArt.SmallUrl) { # 15237 * <img src="${ data.BoxArt.SmallUrl }" alt="${ data.Name }" />Title:${ data.Name }, Year: ${ data.Name } 15238 * # } else { # 15239 * <img alt="${ data.Name }" />Title:${ data.Name }, Year: ${ data.Name } 15240 * # } # 15241 * </script> 15242 * 15243 * //dropdownlist initialization 15244 * <script> 15245 * $("#dropdownlist").kendoDropDownList({ 15246 * dataSource: dataSource, 15247 * dataTextField: "Name", 15248 * dataValueField: "Id", 15249 * template: kendo.template($("#template").html()) 15250 * }); 15251 * </script> 15252 * @option {Object} [animation] <> Animations to be used for opening/closing the popup. Setting to false will turn of the animation. 15253 * @option {Object} [animation.open] <> Animation to be used for opening of the popup. 15254 * _example 15255 * //dropdownlist initialization 15256 * <script> 15257 * $("#dropdownlist").kendoDropDownList({ 15258 * dataSource: dataSource, 15259 * animation: { 15260 * open: { 15261 * effects: "fadeIn", 15262 * duration: 300, 15263 * show: true 15264 * } 15265 * } 15266 * }); 15267 * </script> 15268 * @option {Object} [animation.close] <> Animation to be used for closing of the popup. 15269 * _example 15270 * //dropdownlist initialization 15271 * <script> 15272 * $("#dropdownlist").kendoDropDownList({ 15273 * dataSource: dataSource, 15274 * animation: { 15275 * close: { 15276 * effects: "fadeOut", 15277 * duration: 300, 15278 * hide: true 15279 * show: false 15280 * } 15281 * } 15282 * }); 15283 * </script> 15284 * @option {String} [ignoreCase] <true> Controls whether the search should be case sensitive. 15285 * _example 15286 * $("#dropdownlist").kendoDropDownList({ 15287 * ignoreCase: false //now search will be case sensitive 15288 * }); 15289 */ 15290 init: function(element, options) { 15291 var that = this; 15292 15293 options = $.isArray(options) ? { dataSource: options } : options; 15294 15295 Select.fn.init.call(that, element, options); 15296 15297 options = that.options; 15298 element = that.element.focus(function() {
15299 that.wrapper.focus(); 15300 }); 15301 15302 that._reset(); 15303 15304 that._word = ""; 15305 15306 that._wrapper(); 15307 15308 that._span(); 15309 15310 that._popup(); 15311 15312 that._mobile(); 15313 15314 that._accessors(); 15315 15316 that._dataSource(); 15317 15318 that._enable(); 15319 15320 that.selectedIndex = -1; 15321 15322 if (options.autoBind) { 15323 that._selectItem(); 15324 } else { 15325 if (element.is(SELECT)) { 15326 that.text(element.children(":selected").text()); 15327 } 15328 15329 if (!that.text().replace(trimRegExp, "") && options.optionLabel) { 15330 that.text(options.optionLabel); 15331 } 15332 } 15333 15334 kendo.notify(that); 15335 }, 15336 15337 options: { 15338 name: "DropDownList", 15339 enable: true, 15340 index: 0, 15341 autoBind: true, 15342 template: "", 15343 delay: 500, 15344 height: 200, 15345 dataTextField: "", 15346 dataValueField: "", 15347 optionLabel: "", 15348 ignoreCase: true, 15349 animation: {} 15350 }, 15351 events: [ 15352 /** 15353 * Fires when the drop-down list is opened 15354 * @name kendo.ui.DropDownList#open 15355 * @event 15356 * @param {Event} e 15357 * @example 15358 * $("#dropdownlist").kendoDropDownList({ 15359 * open: function(e) { 15360 * // handle event 15361 * } 15362 * }); 15363 * @exampleTitle To set after initialization 15364 * @example 15365 * // get a reference to the dropdown list 15366 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15367 * // bind to the open event 15368 * dropdownlist.bind("open", function(e) { 15369 * // handle event 15370 * }); 15371 */ 15372 "open", 15373 15374 /** 15375 * Fires when the drop-down list is closed 15376 * @name kendo.ui.DropDownList#close 15377 * @event 15378 * @param {Event} e 15379 * @example 15380 * $("#dropdownlist").kendoDropDownList({ 15381 * close: function(e) { 15382 * // handle event 15383 * } 15384 * }); 15385 * @exampleTitle To set after initialization 15386 * @example 15387 * // get a reference to the dropdown list 15388 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15389 * // bind to the close event 15390 * dropdownlist.bind("close", function(e) { 15391 * // handle event 15392 * }); 15393 */ 15394 "close", 15395 15396 /** 15397 * Fires when the value has been changed. 15398 * @name kendo.ui.DropDownList#change 15399 * @event 15400 * @param {Event} e 15401 * @example 15402 * $("#dropdownlist").kendoDropDownList({ 15403 * change: function(e) { 15404 * // handle event 15405 * } 15406 * }); 15407 * @exampleTitle To set after initialization 15408 * @example 15409 * // get a reference to the dropdown list 15410 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15411 * // bind to the change event 15412 * dropdownlist.bind("change", function(e) { 15413 * // handle event 15414 * }); 15415 */ 15416 CHANGE, 15417 /** 15418 * 15419 * Triggered when a Li element is selected. 15420 * 15421 * @name kendo.ui.DropDownList#select 15422 * @event 15423 * 15424 * @param {Event} e 15425 * 15426 * @param {jQuery} e.item 15427 * The selected item chosen by a user. 15428 * 15429 * @exampleTitle Attach select event handler during initialization; detach via unbind() 15430 * @example 15431 * // event handler for select 15432 * var onSelect = function(e) { 15433 * // access the selected item via e.item (jQuery object) 15434 * }; 15435 * 15436 * // attach select event handler during initialization 15437 * var dropdownlist = $("#dropdownlist").kendoDropDownList({ 15438 * select: onSelect 15439 * }); 15440 * 15441 * // detach select event handler via unbind() 15442 * dropdownlist.data("kendoDropDownList").unbind("select", onSelect); 15443 * 15444 * @exampleTitle Attach select event handler via bind(); detach via unbind() 15445 * @example 15446 * // event handler for select 15447 * var onSelect = function(e) { 15448 * // access the selected item via e.item (jQuery object) 15449 * }; 15450 * 15451 * // attach select event handler via bind() 15452 * $("#dropdownlist").data("kendoDropDownList").bind("select", onSelect); 15453 * 15454 * // detach select event handler via unbind() 15455 * $("#dropdownlist").data("kendoDropDownList").unbind("select", onSelect); 15456 * 15457 */ 15458 "select", 15459 "dataBinding", 15460 "dataBound" 15461 ], 15462 15463 setOptions: function(options) { 15464 Select.fn.setOptions.call(this, options); 15465 15466 this._template(); 15467 this._accessors(); 15468 }, 15469 15470 /** 15471 * Closes the drop-down list. 15472 * @name kendo.ui.DropDownList#close 15473 * @function 15474 * @example 15475 * // get a reference to the dropdown widget 15476 * var dropdownList = $("#dropdownList").data("kendoDropDownList"); 15477 * // close the dropdown 15478 * dropdownlist.close(); 15479 */ 15480 15481 /** 15482 * Returns the raw data record at the specified index. If the index is not specified, the selected index will be used. 15483 * @name kendo.ui.DropDownList#dataItem 15484 * @function 15485 * @param {Number} index The zero-based index of the data record 15486 * @returns {Object} The raw data record. Returns <i>undefined</i> if no data. 15487 * @example 15488 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15489 * 15490 * // get the dataItem corresponding to the selectedIndex. 15491 * var dataItem = dropdownlist.dataItem(); 15492 * 15493 * // get the dataItem corresponding to the passed index. 15494 * var dataItem = dropdownlist.dataItem(1); 15495 */ 15496 15497 /** 15498 * Enables/disables the dropdownlist widget 15499 * @param {Boolean} enable Desired state 15500 * @example 15501 * // get a reference to the dropdown list 15502 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15503 * // disable the dropdown list 15504 * dropdownlist.enable(false); 15505 */ 15506 enable: function(enable) { 15507 var that = this, 15508 element = that.element, 15509 wrapper = that.wrapper.unbind(".dropdownlist"), 15510 dropDownWrapper = that._inputWrapper.unbind(HOVEREVENTS); 15511 15512 if (enable === false) { 15513 element.attr(ATTRIBUTE, ATTRIBUTE); 15514 15515 dropDownWrapper 15516 .removeClass(DEFAULT) 15517 .addClass(DISABLED); 15518 } else { 15519 element.removeAttr(ATTRIBUTE, ATTRIBUTE); 15520 15521 dropDownWrapper 15522 .addClass(DEFAULT) 15523 .removeClass(DISABLED) 15524 .bind(HOVEREVENTS, that._toggleHover); 15525 15526 wrapper 15527 .bind({ 15528 "click.dropdownlist": function(e) { 15529 e.preventDefault();
15530 that.toggle(); 15531 }, 15532 "keydown.dropdownlist": proxy(that._keydown, that), 15533 "keypress.dropdownlist": proxy(that._keypress, that), 15534 "focusin.dropdownlist": function() { 15535 dropDownWrapper.addClass(FOCUSED); 15536 }, 15537 "focusout.dropdownlist": function(e) { 15538 that._blur(); 15539 dropDownWrapper.removeClass(FOCUSED); 15540 } 15541 }); 15542 } 15543 }, 15544 15545 /** 15546 * Opens the drop-down list. 15547 * @example 15548 * // get a reference to the dropdown list 15549 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15550 * // open the drop down 15551 * dropdownlist.open(); 15552 */ 15553 open: function() { 15554 var that = this; 15555 15556 if (!that.ul[0].firstChild) { 15557 that._open = true; 15558 that._selectItem(); 15559 } else { 15560 that.popup.open(); 15561 that._scroll(that._current); 15562 } 15563 }, 15564 15565 /** 15566 * Toggles the drop-down list between opened and closed state. 15567 * @param {Boolean} toggle Defines the whether to open/close the drop-down list. 15568 * @example 15569 * // get a reference to the dropdown list 15570 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15571 * 15572 * // toggles the open state of the drop-down list. 15573 * dropdownlist.toggle(); 15574 */ 15575 toggle: function(toggle) { 15576 this._toggle(toggle); 15577 }, 15578 15579 /** 15580 * Re-render the items in drop-down list. 15581 * @name kendo.ui.DropDownList#refresh 15582 * @function 15583 * @example 15584 * // get a referenence to the Kendo UI DropDownList 15585 * var dropdownlist = $("dropdownlist").data("kendoDropDownList"); 15586 * // re-render the items in drop-down list. 15587 * dropdownlist.refresh(); 15588 */ 15589 refresh: function() { 15590 var that = this, 15591 data = that._data(), 15592 length = data.length, 15593 optionLabel = that.options.optionLabel; 15594 15595 that.trigger("dataBinding"); 15596 if (that._current) { 15597 that.current(null); 15598 } 15599 15600 that.ul[0].innerHTML = kendo.render(that.template, data); 15601 that._height(length); 15602 15603 if (that.element.is(SELECT)) { 15604 if (optionLabel && length) { 15605 optionLabel = '<option value="">' + optionLabel + "</option>"; 15606 } 15607 15608 that._options(data, optionLabel); 15609 } 15610 15611 if (that._open) { 15612 that._open = false; 15613 that.toggle(!!length); 15614 } 15615 15616 that._hideBusy(); 15617 that._makeUnselectable(); 15618 15619 that.trigger("dataBound"); 15620 }, 15621 15622 15623 15624 /** 15625 * Selects item, which starts with the provided parameter. 15626 * @param {string} word The search value. 15627 * @example 15628 * // get a reference to the dropdown list 15629 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15630 * 15631 * // Selects item which starts with "In". 15632 * dropdownlist.search("In"); 15633 */ 15634 search: function(word) { 15635 if (word) { 15636 var that = this, 15637 ignoreCase = that.options.ignoreCase; 15638 15639 if (ignoreCase) { 15640 word = word.toLowerCase(); 15641 } 15642 15643 that._select(function(dataItem) { 15644 var text = that._text(dataItem); 15645 15646 if (text !== undefined) { 15647 text = (text + ""); 15648 if (ignoreCase) { 15649 text = text.toLowerCase(); 15650 } 15651 15652 return text.indexOf(word) === 0; 15653 } 15654 }); 15655 } 15656 }, 15657 15658 /** 15659 * Selects drop-down list item and sets the value and the text of the dropdownlist. 15660 * @param {jQueryObject | Number | Function} li LI element or index of the item or predicate function, which defines the item that should be selected. 15661 * @returns {Integer} The index of the selected LI element. 15662 * @example 15663 * // get a reference to the dropdown list 15664 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15665 * 15666 * // selects by jQuery object 15667 * dropdownlist.select(dropdownlist.ul.children().eq(0)); 15668 * 15669 * // selects by index 15670 * dropdownlist.select(1); 15671 * 15672 * // selects item if its text is equal to "test" using predicate function 15673 * dropdownlist.select(function(dataItem) { 15674 * return dataItem.text === "test"; 15675 * }); 15676 */ 15677 select: function(li) { 15678 var that = this; 15679 15680 if (li === undefined) { 15681 return that.selectedIndex; 15682 } else { 15683 that._select(li); 15684 that._old = that._accessor(); 15685 that._oldIndex = that.selectedIndex; 15686 } 15687 }, 15688 15689 /** 15690 * Gets/Sets the text of the dropdownlist. 15691 * @param {String} text The text to set. 15692 * @returns {String} The text of the dropdownlist. 15693 * @example 15694 * // get a reference to the dropdown list 15695 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15696 * 15697 * // get the text of the dropdownlist. 15698 * var text = dropdownlist.text(); 15699 */ 15700 text: function (text) { 15701 var span = this.span; 15702 15703 if (text !== undefined) { 15704 span.text(text); 15705 } else { 15706 return span.text(); 15707 } 15708 }, 15709 15710 /** 15711 * Gets/Sets the value of the dropdownlist. The value will not be set if there is no item with such value. If value is undefined, text of the data item is used. 15712 * @param {String} value The value to set. 15713 * @returns {String} The value of the dropdownlist. 15714 * @example 15715 * // get a reference to the dropdown list 15716 * var dropdownlist = $("#dropdownlist").data("kendoDropDownList"); 15717 * 15718 * // get the value of the dropdownlist. 15719 * var value = dropdownlist.value(); 15720 * 15721 * // set the value of the dropdownlist. 15722 * dropdownlist.value("1"); //looks for item which has value "1" 15723 */ 15724 value: function(value) { 15725 var that = this, 15726 idx; 15727 15728 if (value !== undefined) { 15729 if (value && that._valueOnFetch(value)) { 15730 return; 15731 } 15732 15733 idx = that._index(value); 15734 15735 that.select(idx > -1 ? idx : 0); 15736 } else { 15737 return that._accessor(); 15738 } 15739 }, 15740 15741 _selectItem: function() { 15742 var that = this; 15743 15744 that.dataSource.one(CHANGE, function() { 15745 var value = that.value(); 15746 if (value) { 15747 that.value(value); 15748 } else { 15749 that.select(that.options.index); 15750 } 15751 }).fetch();
15752 }, 15753 15754 _accept: function(li) { 15755 this._focus(li); 15756 }, 15757 15758 _data: function() { 15759 var that = this, 15760 options = that.options, 15761 optionLabel = options.optionLabel, 15762 textField = options.dataTextField, 15763 valueField = options.dataValueField, 15764 data = that.dataSource.view(), 15765 length = data.length, 15766 first = optionLabel, 15767 idx = 0; 15768 15769 if (optionLabel && length) { 15770 if (textField) { 15771 first = {}; 15772 15773 textField = textField.split("."); 15774 valueField = valueField.split("."); 15775 15776 assign(first, valueField, ""); 15777 assign(first, textField, optionLabel); 15778 } 15779 15780 first = new kendo.data.ObservableArray([first]); 15781 15782 for (; idx < length; idx++) { 15783 first.push(data[idx]); 15784 } 15785 data = first; 15786 } 15787 15788 return data; 15789 }, 15790 15791 _keydown: function(e) { 15792 var that = this, 15793 key = e.keyCode,
15794 keys = kendo.keys, 15795 ul = that.ul[0]; 15796 15797 that._move(e); 15798 15799 if (key === keys.HOME) { 15800 e.preventDefault(); 15801 that._select(ul.firstChild); 15802 } else if (key === keys.END) { 15803 e.preventDefault(); 15804 that._select(ul.lastChild); 15805 } 15806 }, 15807 15808 _keypress: function(e) { 15809 var that = this; 15810 15811 setTimeout(function() { 15812 that._word += String.fromCharCode(e.keyCode || e.charCode); 15813 that._search(); 15814 }); 15815 }, 15816 15817 _popup: function() { 15818 Select.fn._popup.call(this); 15819 this.popup.one("open", function() { 15820 this.wrapper = kendo.wrap(this.element) 15821 .addClass("km-popup"); 15822 }); 15823 }, 15824 15825 _search: function() { 15826 var that = this; 15827 clearTimeout(that._typing); 15828 15829 that._typing = setTimeout(function() { 15830 that._word = ""; 15831 }, that.options.delay); 15832 15833 that.search(that._word); 15834 }, 15835 15836 _select: function(li) { 15837 var that = this, 15838 current = that._current, 15839 data = that._data(), 15840 value, 15841 text, 15842 idx; 15843 15844 li = that._get(li); 15845 15846 if (li && li[0] && !li.hasClass(SELECTED)) { 15847 if (current) { 15848 current.removeClass(SELECTED); 15849 } 15850 15851 idx = ui.List.inArray(li[0], that.ul[0]); 15852 if (idx > -1) { 15853 data = data[idx]; 15854 text = that._text(data); 15855 value = that._value(data); 15856 that.selectedIndex = idx; 15857 15858 that.text(text); 15859 that._accessor(value !== undefined ? value : text, idx); 15860 that.current(li.addClass(SELECTED)); 15861 } 15862 } 15863 }, 15864 15865 _mobile: function() { 15866 var that = this, 15867 popup = that.popup, 15868 root = popup.element.parents(".km-root").eq(0); 15869 15870 if (root.length && os) { 15871 popup.options.animation.open.effects = (os.android || os.meego) ? "fadeIn" : os.ios ? "slideIn:up" : popup.options.animation.open.effects; 15872 } 15873 }, 15874 15875 _span: function() { 15876 var that = this, 15877 wrapper = that.wrapper, 15878 SELECTOR = ".k-input", 15879 span; 15880 15881 span = wrapper.find(SELECTOR); 15882 15883 if (!span[0]) { 15884 wrapper.append('<span unselectable="on" class="k-dropdown-wrap k-state-default"><span unselectable="on" class="k-input"> </span><span class="k-select"><span class="k-icon k-arrow-down">select</span></span></span>') 15885 .append(that.element); 15886 15887 span = wrapper.find(SELECTOR); 15888 } 15889 15890 that.span = span; 15891 that._inputWrapper = $(wrapper[0].firstChild); 15892 that._arrow = wrapper.find(".k-icon").mousedown(function(e) { e.preventDefault(); }); 15893 }, 15894 15895 _wrapper: function() { 15896 var that = this, 15897 element = that.element, 15898 DOMelement = element[0], 15899 TABINDEX = "tabIndex", 15900 wrapper; 15901 15902 wrapper = element.parent(); 15903 15904 if (!wrapper.is("span.k-widget")) { 15905 wrapper = element.wrap("<span />").parent(); 15906 } 15907 15908 if (!wrapper.attr(TABINDEX)) { 15909 wrapper.attr(TABINDEX, 0); 15910 } 15911 15912 wrapper[0].style.cssText = DOMelement.style.cssText; 15913 element.hide(); 15914 15915 that._focused = that.wrapper = wrapper 15916 .attr("unselectable", "on") 15917 .addClass("k-widget k-dropdown k-header") 15918 .addClass(DOMelement.className); 15919 } 15920 });
15921 15922 function assign(instance, fields, value) { 15923 var idx = 0, 15924 lastIndex = fields.length - 1, 15925 field; 15926 15927 for (; idx < lastIndex; ++idx) { 15928 field = fields[idx]; 15929 15930 if (!(field in instance)) { 15931 instance[field] = {}; 15932 } 15933 15934 instance = instance[field]; 15935 } 15936 15937 instance[fields[lastIndex]] = value; 15938 } 15939 15940 ui.plugin(DropDownList); 15941})(jQuery); 15942(function($, undefined) { 15943 /** 15944 * @name kendo.ui.ComboBox.Description 15945 * 15946 * @section 15947 * <p> 15948 * The <strong>ComboBox</strong> displays a list of values and allows the selection of a single value from this 15949 * list. Custom values may also be entered via keyboard input. If you do not wish permit keyboard input - that is, 15950 * custom values are not permitted - use the 15951 * <a href="../dropdownlist/index.html" title="DropDownList">DropDownList</a>. 15952 * </p> 15953 * <p> 15954 * The <strong>ComboBox</strong> represents a richer version of a <select> element, providing support for 15955 * local and remote data binding, item templates, and configurable options for controlling the list behavior. 15956 * </p> 15957 * <h3>Getting Started</h3> 15958 * <p>There are two ways to create a <strong>ComboBox</strong>:</p> 15959 * <ol> 15960 * <li>From a <select> element with HTML to define the list items</li> 15961 * <li>From an <input> element with databinding to define the listitems</li> 15962 * </ol> 15963 * <p> 15964 * A <strong>ComboBox</strong> will look and operate consistently regardless of the way in which it was created. 15965 * </p> 15966 * 15967 * @exampleTitle Creating a ComboBox from an existing <input> element 15968 * @example 15969 * <input id="comboBox" /> 15970 * 15971 * @section 15972 * <p></p> 15973 * <p> 15974 * Initialization of a <strong>ComboBox</strong> should occur after the DOM is fully loaded. It is recommended 15975 * that initialization the <strong>ComboBox</strong> occur within a handler is provided to 15976 * $(document).ready(). 15977 * </p> 15978 * 15979 * @exampleTitle Initialize a ComboBox using a selector within $(document).ready() 15980 * @example 15981 * $(document).ready(function(){ 15982 * $("#comboBox").kendoComboBox({ 15983 * dataTextField: "text", 15984 * dataValueField: "value", 15985 * dataSource: [ 15986 * { text: "Item1", value: "1" }, 15987 * { text: "Item2", value: "2" } 15988 * ] 15989 * }); 15990 * }); 15991 * 15992 * @exampleTitle Creating a ComboBox from existing <select> element with a pre-defined structure 15993 * @example 15994 * <select id="comboBox"> 15995 * <option>Item 1</option> 15996 * <option>Item 2</option> 15997 * <option>Item 3</option> 15998 * </select> 15999 * 16000 * <script> 16001 * $(document).ready(function(){ 16002 * $("#comboBox").kendoComboBox(); 16003 * }); 16004 * </script> 16005 * 16006 * @section 16007 * <h3>Binding to Local or Remote Data</h3> 16008 * <p> 16009 * The <strong>ComboBox</strong> can be bound to both local arrays and remote data via the 16010 * <a href="../datasource/index.html" title="DataSource">DataSource</a> component; an abstraction for local and 16011 * remote data. Local arrays are appropriate for limited value options, while remote data binding is better for 16012 * larger data sets. With remote data-binding, items will be loaded on-demand; when they are displayed. 16013 * </p> 16014 * 16015 * @exampleTitle Binding to a remote OData service 16016 * @example 16017 * $(document).ready(function() { 16018 * $("#comboBox").kendoComboBox({ 16019 * index: 0, 16020 * dataTextField: "Name", 16021 * dataValueField: "Id", 16022 * filter: "contains", 16023 * dataSource: { 16024 * type: "odata", 16025 * serverFiltering: true, 16026 * serverPaging: true, 16027 * pageSize: 20, 16028 * transport: { 16029 * read: "http://odata.netflix.com/Catalog/Titles" 16030 * } 16031 * } 16032 * }); 16033 * }); 16034 * 16035 * @section 16036 * <h3>Customizing Item Templates</h3> 16037 * <p> 16038 * The <strong>ComboBox</strong> uses Kendo UI templates to enable you to control how items are rendered. For a 16039 * detailed description of the capabilities and syntax of the Kendo UI templates, please refer to the 16040 * <a href="../templates/index.html" title="Kendo UI Template">demos and documentation</a>. 16041 * </p> 16042 * 16043 * @exampleTitle Basic item template customization 16044 * @example 16045 * <input id="comboBox" /> 16046 * <!-- Template --> 16047 * <script id="scriptTemplate" type="text/x-kendo-template"> 16048 * # if (data.BoxArt.SmallUrl) { # 16049 * <img src="${ data.BoxArt.SmallUrl }" alt="${ data.Name }" /> 16050 * Title:${ data.Name }, Year: ${ data.Name } 16051 * # } else { # 16052 * <img alt="${ data.Name }" /> 16053 * Title:${ data.Name }, Year: ${ data.Name } 16054 * # } # 16055 * </script> 16056 * 16057 * <!-- ComboBox initialization --> 16058 * <script> 16059 * $(document).ready(function() { 16060 * $("#comboBox").kendoComboBox({ 16061 * autoBind: false, 16062 * dataTextField: "Name", 16063 * dataValueField: "Id", 16064 * template: $("#scriptTemplate").html(), 16065 * dataSource: { 16066 * type: "odata", 16067 * serverFiltering: true, 16068 * serverPaging: true, 16069 * pageSize: 20, 16070 * transport: { 16071 * read: "http://odata.netflix.com/Catalog/Titles" 16072 * } 16073 * } 16074 * }); 16075 * }); 16076 * </script> 16077 * 16078 * @section 16079 * <h3>Accessing an Existing ComboBox</h3> 16080 * <p> 16081 * You can reference an existing <b>ComboBox</b> instance via 16082 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Objectnce a reference has been established, you 16083 * can use the API to control its behavior. 16084 * </p> 16085 * 16086 * @exampleTitle Accessing an existing ComboBox instance 16087 * @example 16088 * var comboBox = $("#comboBox").data("kendoComboBox"); 16089 * 16090 */ 16091 var kendo = window.kendo, 16092 ui = kendo.ui, 16093 List = ui.List, 16094 Select = ui.Select, 16095 support = kendo.support, 16096 placeholderSupported = support.placeholder, 16097 keys = kendo.keys, 16098 CLICK = support.touch ? "touchend" : "click", 16099 ATTRIBUTE = "disabled", 16100 CHANGE = "change", 16101 DEFAULT = "k-state-default", 16102 DISABLED = "k-state-disabled", 16103 FOCUSED = "k-state-focused", 16104 MOUSEDOWN = "mousedown", 16105 SELECT = "select", 16106 STATE_SELECTED = "k-state-selected", 16107 STATE_FILTER = "filter", 16108 STATE_ACCEPT = "accept", 16109 HOVEREVENTS = "mouseenter mouseleave", 16110 NULL = null, 16111 proxy = $.proxy; 16112 16113 var ComboBox = Select.extend(/** @lends kendo.ui.ComboBox.prototype */{ 16114 /** 16115 * @constructs 16116 * @extends kendo.ui.Select 16117 * @param {DomElement} element DOM element 16118 * @param {Object} options Configuration options. 16119 * @option {Object | kendo.data.DataSource} [dataSource] A local JavaScript object or instance of DataSource or the data that the ComboBox will be bound to. 16120 * _example 16121 * var items = [{ text: "Item 1", value: "1" }, { text: "Item 2", value: "2" }]; 16122 * $("#comboBox").kendoComboBox({ 16123 * dataTextField: "text", 16124 * dataValueField: "value", 16125 * dataSource: items 16126 * }); 16127 * _exampleTitle To set after initialization 16128 * _example 16129 * $("#comboBox").kendoComboBox({ 16130 * dataSource: new kendo.data.DataSource({ 16131 * transport: { 16132 * read: { 16133 * url: "Get/Items" // url to remote data source (simple list of strings) 16134 * } 16135 * } 16136 * }); 16137 * }); 16138 * @option {Boolean} [enable] <true> Controls whether the ComboBox should be initially enabled. 16139 * _example 16140 * $("#comboBox").kendoComboBox({ 16141 * enable: false 16142 * }); 16143 * _exampleTitle To set after initialization 16144 * _example 16145 * // get a reference to the ComboBox widget 16146 * var comboBox = $("#comboBox").data("kendoComboBox"); 16147 * comboBox.enable(false); 16148 * @option {Number} [index] <-1> Defines the initial selected item. 16149 * _example 16150 * var items = [{ text: "Item 1", value: "1" }, { text: "Item 2", value: "2" }]; 16151 * $("#comboBox").kendoComboBox({ 16152 * dataSource: items, 16153 * index: 1 // 0 based from the start of the collection of objects. this selects "Item 2". 16154 * }); 16155 * @option {Boolean} [autoBind] <true> Controls whether to bind the widget to the DataSource on initialization. 16156 * _example 16157 * $("#comboBox").kendoComboBox({ 16158 * autoBind: false 16159 * }); 16160 * @option {Boolean} [highlightFirst] <true> Controls whether the first item will be automatically highlighted. 16161 * _example 16162 * $("#comboBox").kendoComboBox({ 16163 * highLightFirst: true 16164 * }); 16165 * @option {Boolean} [suggest] <false> Controls whether the ComboBox should automatically auto-type the rest of text. 16166 * _example 16167 * $("#comboBox").kendoComboBox({ 16168 * suggest: false 16169 * }); 16170 * @option {Number} [delay] <200>
16170 Specifies the delay in ms after which the ComboBox will start filtering dataSource. 16171 * _example 16172 * $("#comboBox").kendoComboBox({ 16173 * delay: 500 16174 * }); 16175 * @option {Number} [minLength] <1> Specifies the minimum characters that should be typed before the ComboBox activates 16176 * _example 16177 * $("#comboBox").kendoComboBox({ 16178 * minLength: 3 16179 * }); 16180 * @option {String} [dataTextField] <""> Sets the field of the data item that provides the text content of the list items. 16181 * _example 16182 * $("#comboBox").kendoComboBox({ 16183 * dataTextField: "Name", 16184 * dataValueField: "ID" 16185 * }); 16186 * @option {String} [dataValueField] <""> Sets the field of the data item that provides the value content of the list items. 16187 * _example 16188 * $("#comboBox").kendoComboBox({ 16189 * dataTextField: "Name", 16190 * dataValueField: "ID" 16191 * }); 16192 * @option {String} [filter] <"none"> Defines the type of filtration. If "none" the ComboBox will not filter the items. 16193 * _example 16194 * $("#comboBox").kendoComboBox({ 16195 * filter: "startswith" 16196 * }); 16197 * @option {String} [ignoreCase] <true> Defines whether the filtration should be case sensitive. 16198 * _example 16199 * $("#combobox").kendoComboBox({ 16200 * filter: 'contains', 16201 * ignoreCase: false //now filtration will be case sensitive 16202 * }); 16203 * @option {Number} [height] <200> Define the height of the drop-down list in pixels. 16204 * _example 16205 * $("#comboBox").kendoComboBox({ 16206 * height: 500 16207 * }); 16208 * @option {Function} [template] Template to be used for rendering the items in the list. 16209 * _example 16210 * //template 16211 * <script id="template" type="text/x-kendo-tmpl"> 16212 * # if (data.BoxArt.SmallUrl) { # 16213 * <img src="${ data.BoxArt.SmallUrl }" alt="${ data.Name }" />Title:${ data.Name }, Year: ${ data.Name } 16214 * # } else { # 16215 * <img alt="${ data.Name }" />Title:${ data.Name }, Year: ${ data.Name } 16216 * # } # 16217 * </script> 16218 * 16219 * //combobox initialization 16220 * <script> 16221 * $("#combobox").kendoComboBox({ 16222 * dataSource: dataSource, 16223 * dataTextField: "Name", 16224 * dataValueField: "Id", 16225 * template: kendo.template($("#template").html()) 16226 * }); 16227 * </script> 16228 * @option {Object} [animation] <> Animations to be used for opening/closing the popup. Setting to false will turn off the animation. 16229 * _exampleTitle Turn of animation 16230 * _example 16231 * $("#comboBox").kendoComboBox({ 16232 * animation: false 16233 * }); 16234 * @option {Object} [animation.open] <> Animation to be used for opening of the popup. 16235 * _example 16236 * //combobox initialization 16237 * <script> 16238 * $("#combobox").kendoComboBox({ 16239 * dataSource: dataSource, 16240 * animation: { 16241 * open: { 16242 * effects: "fadeIn", 16243 * duration: 300, 16244 * show: true 16245 * } 16246 * } 16247 * }); 16248 * </script> 16249 * 16250 * @option {Object} [animation.close] <> Animation to be used for closing of the popup. 16251 * _example 16252 * //combobox initialization 16253 * <script> 16254 * $("#combobox").kendoComboBox({ 16255 * dataSource: dataSource, 16256 * animation: { 16257 * close: { 16258 * effects: "fadeOut", 16259 * duration: 300, 16260 * hide: true 16261 * show: false 16262 * } 16263 * } 16264 * }); 16265 * </script> 16266 * @option {String} [placeholder] <""> A string that appears in the textbox when the combobox has no value. 16267 * _example 16268 * //combobox initialization 16269 * <script> 16270 * $("#combobox").kendoComboBox({ 16271 * dataSource: dataSource, 16272 * placeholder: "Select..." 16273 * }); 16274 * </script> 16275 * _example 16276 * <input id="combobox" placeholder="Select..." /> 16277 * 16278 * //combobox initialization 16279 * <script> 16280 * $("#combobox").kendoComboBox({ 16281 * dataSource: dataSource 16282 * }); 16283 * </script> 16284 */ 16285 init: function(element, options) { 16286 var that = this, wrapper; 16287 16288 options = $.isArray(options) ? { dataSource: options } : options; 16289 16290 Select.fn.init.call(that, element, options); 16291 16292 options = that.options; 16293 element = that.element.focus(function() { 16294 that.input.focus(); 16295 }); 16296 16297 options.placeholder = options.placeholder || element.attr("placeholder"); 16298 16299 that._reset(); 16300 16301 that._wrapper(); 16302 16303 that._input(); 16304 16305 that._popup(); 16306 16307 that._accessors(); 16308 16309 that._dataSource(); 16310 16311 that._enable(); 16312 16313 wrapper = that._inputWrapper; 16314 16315 that.input.bind({ 16316 keydown: proxy(that._keydown, that), 16317 focus: function() { 16318 wrapper.addClass(FOCUSED); 16319 that._placeholder(false); 16320 }, 16321 blur: function() { 16322 wrapper.removeClass(FOCUSED); 16323 clearTimeout(that._typing); 16324 that.text(that.text()); 16325 that._placeholder(); 16326 that._blur(); 16327 } 16328 }); 16329 16330 that._oldIndex = that.selectedIndex = -1; 16331 that._old = that.value(); 16332 16333 if (options.autoBind) { 16334 that._selectItem(); 16335 } else if (element.is(SELECT)) { 16336 that.input.val(element.children(":selected").text()); 16337 } 16338 16339 that._placeholder(); 16340 16341 kendo.notify(that); 16342 }, 16343 16344 options: { 16345 name: "ComboBox", 16346 enable: true, 16347 index: -1, 16348 autoBind: true, 16349 delay: 200, 16350 dataTextField: "", 16351 dataValueField: "", 16352 minLength: 0, 16353 height: 200, 16354 highlightFirst: true, 16355 template: "", 16356 filter: "none", 16357 placeholder: "", 16358 suggest: false, 16359 ignoreCase: true, 16360 animation: {} 16361 }, 16362 16363 events:[ 16364 /** 16365 * Fires when the drop-down list is opened 16366 * @name kendo.ui.ComboBox#open 16367 * @event 16368 * @param {Event} e 16369 * @example 16370 * $("#comboBox").kendoComboBox({ 16371 * open: function(e) { 16372 * // handle event 16373 * } 16374 * }); 16375 * @exampleTitle To set after initialization 16376 * @example 16377 * // get a reference to instance of the Kendo UI ComboBox 16378 * var combobox = $("#comboBox").data("kendoComboBox"); 16379 * // bind to the open event 16380 * combobox.bind("open", function(e) { 16381 * // handle event 16382 * }); 16383 */ 16384 "open", 16385 16386 /** 16387 * Fires when the drop-down list is closed 16388 * @name kendo.ui.ComboBox#close 16389 * @event 16390 * @param {Event} e 16391 * @example 16392 * $("#comboBox").kendoComboBox({ 16393 * close: function(e) { 16394 * // handle event 16395 * } 16396 * }); 16397 * @exampleTitle To set after initialization 16398 * @example 16399 * // get a reference to instance of the Kendo UI ComboBox 16400 * var combobox = $("#comboBox").data("kendoComboBox"); 16401 * // bind to the close event 16402 * combobox.bind("close", function(e) { 16403 * // handle event 16404 * }); 16405 */ 16406 "close", 16407 16408 /** 16409 * Fires when the value has been changed. 16410 * @name kendo.ui.ComboBox#change 16411 * @event 16412 * @param {Event} e 16413 * @example 16414 * $("#comboBox").kendoComboBox({ 16415 * change: function(e) { 16416 * // handle event 16417 * } 16418 * }); 16419 * @exampleTitle To set after initialization 16420 * @example 16421 * // get a reference to instance of the Kendo UI ComboBox 16422 * var combobox = $("#comboBox").data("kendoComboBox"); 16423 * // bind to the change event 16424 * combobox.bind("change", function(e) { 16425 * // handle event 16426 * }); 16427 */ 16428 CHANGE, 16429 /** 16430 * 16431 * Triggered when a Li element is selected. 16432 * 16433 * @name kendo.ui.ComboBox#select 16434 * @event 16435 * 16436 * @param {Event} e 16437 * 16438 * @param {jQuery} e.item 16439 * The selected item chosen by a user. 16440 * 16441 * @exampleTitle Attach select event handler during initialization; detach via unbind() 16442 * @example 16443 * // event handler for select 16444 * var onSelect = function(e) { 16445 * // access the selected item via e.item (jQuery object) 16446 * }; 16447 * 16448 * // attach select event handler during initialization 16449 * var combobox = $("#combobox").kendoComboBox({ 16450 * select: onSelect 16451 * }); 16452 * 16453 * // detach select event handler via unbind() 16454 * combobox.data("kendoComboBox").unbind("select", onSelect); 16455 * 16456 * @exampleTitle Attach select event handler via bind(); detach via unbind() 16457 * @example 16458 * // event handler for select 16459 * var onSelect = function(e) { 16460 * // access the selected item via e.item (jQuery object) 16461 * }; 16462 * 16463 * // attach select event handler via bind() 16464 * $("#combobox").data("kendoComboBox").bind("select", onSelect); 16465 * 16466 * // detach select event handler via unbind() 16467 * $("#combobox").data("kendoComboBox").unbind("select", onSelect); 16468 * 16469 */ 16470 "select", 16471 "dataBinding", 16472 "dataBound" 16473 ], 16474 setOptions: function(options) { 16475 Select.fn.setOptions.call(this, options); 16476 16477 this._template(); 16478 this._accessors(); 16479 }, 16480 16481 current: function(li) { 16482 var that = this, 16483 current = that._current; 16484 16485 if (li === undefined) { 16486 return current; 16487 } 16488 16489 if (current) { 16490 current.removeClass(STATE_SELECTED); 16491 } 16492 16493 Select.fn.current.call(that, li); 16494 }, 16495 16496 /** 16497 * Returns the raw data record at the specified index. If the index is not specified, the selected index will be used. 16498 * @name kendo.ui.ComboBox#dataItem 16499 * @function 16500 * @param {Number} index The zero-based index of the data record 16501 * @returns {Object} The raw data record. Returns <i>undefined</i> if no data. 16502 * @example 16503 * var combobox = $("#combobox").data("kendoComboBox"); 16504 * 16505 * // get the dataItem corresponding to the selectedIndex. 16506 * var dataItem = combobox.dataItem(); 16507 * 16508 * // get the dataItem corresponding to the passed index. 16509 * var dataItem = combobox.dataItem(1); 16510 */ 16511 16512 /** 16513 * Closes the drop-down list. 16514 * @name kendo.ui.ComboBox#close 16515 * @function 16516 * @example 16517 * // get a reference to instance of the Kendo UI ComboBox 16518 * var combobox = $("#comboBox").data("kendoComboBox"); 16519 * combobox.close(); 16520 */ 16521 /** 16522 * Enables/disables the combobox widget 16523 * @param {Boolean} enable Desired state 16524 * @example 16525 * // get a reference to instance of the Kendo UI ComboBox 16526 * var combobox = $("#comboBox").data("kendoComboBox"); 16527 * // disables the combobox 16528 * combobox.enable(false); 16529 */ 16530 enable: function(enable) { 16531 var that = this, 16532 input = that.input.add(that.element), 16533 wrapper = that._inputWrapper.unbind(HOVEREVENTS), 16534 arrow = that._arrow.parent().unbind(CLICK + " " + MOUSEDOWN); 16535 16536 if (enable === false) { 16537 wrapper 16538 .removeClass(DEFAULT) 16539 .addClass(DISABLED); 16540 16541 input.attr(ATTRIBUTE, ATTRIBUTE); 16542 } else { 16543 wrapper 16544 .removeClass(DISABLED) 16545 .addClass(DEFAULT) 16546 .bind(HOVEREVENTS, that._toggleHover); 16547 16548 input.removeAttr(ATTRIBUTE);
16549 arrow.bind(CLICK, function() { that.toggle(); }) 16550 .bind(MOUSEDOWN, function(e) { e.preventDefault(); }); 16551 } 16552 }, 16553 16554 /** 16555 * Opens the drop-down list. 16556 * @example 16557 * // get a reference to instance of the Kendo UI ComboBox 16558 * var combobox = $("#comboBox").data("kendoComboBox"); 16559 * combobox.open(); 16560 */ 16561 open: function() { 16562 var that = this, 16563 serverFiltering = that.dataSource.options.serverFiltering; 16564 16565 if (that.popup.visible()) { 16566 return; 16567 } 16568 16569 if (!that.ul[0].firstChild || (that._state === STATE_ACCEPT && !serverFiltering)) { 16570 that._open = true; 16571 that._state = ""; 16572 that._selectItem(); 16573 } else { 16574 that.popup.open(); 16575 that._scroll(that._current); 16576 } 16577 }, 16578 16579 /** 16580 * Re-render the items of the drop-down list. 16581 * @name kendo.ui.ComboBox#refresh 16582 * @function 16583 * @example 16584 * // get a referenence to the Kendo UI ComboBox 16585 * var combobox = $("#combobox").data("kendoComboBox"); 16586 * // re-render the items of the drop-down list. 16587 * combobox.refresh(); 16588 */ 16589 refresh: function() { 16590 var that = this, 16591 ul = that.ul[0], 16592 options = that.options, 16593 data = that._data(), 16594 length = data.length; 16595 16596 that.trigger("dataBinding"); 16597 16598 ul.innerHTML = kendo.render(that.template, data); 16599 that._height(length); 16600 16601 if (that.element.is(SELECT)) { 16602 that._options(data); 16603 } 16604 16605 if (length) { 16606 if (options.highlightFirst) { 16607 that.current($(ul.firstChild)); 16608 } 16609 16610 if (options.suggest && that.input.val()) { 16611 that.suggest($(ul.firstChild)); 16612 } 16613 } 16614 16615 if (that._open) { 16616 that._open = false; 16617 that.toggle(!!length); 16618 } 16619 16620 if (that._touchScroller) { 16621 that._touchScroller.reset(); 16622 } 16623 16624 that._makeUnselectable(); 16625 16626 that._hideBusy(); 16627 that.trigger("dataBound"); 16628 }, 16629 16630 /** 16631 * Selects drop-down list item and sets the value and the text of the combobox. 16632 * @param {jQueryObject | Number | Function} li LI element or index of the item or predicate function, which defines the item that should be selected. 16633 * @returns {Integer} The index of the selected LI element. 16634 * @example 16635 * var combobox = $("#combobox").data("kendoComboBox"); 16636 * 16637 * // selects by jQuery object 16638 * combobox.select(combobox.ul.children().eq(0)); 16639 * 16640 * // selects by index 16641 * combobox.select(1); 16642 * 16643 * // selects item if its text is equal to "test" using predicate function 16644 * combobox.select(function(dataItem) { 16645 * return dataItem.text === "test"; 16646 * }); 16647 */ 16648 select: function(li) { 16649 var that = this; 16650 16651 if (li === undefined) { 16652 return that.selectedIndex; 16653 } else { 16654 that._select(li); 16655 that._old = that._accessor(); 16656 that._oldIndex = that.selectedIndex; 16657 } 16658 }, 16659 16660 /** 16661 * Filters dataSource using the provided parameter and rebinds drop-down list. 16662 * @param {string} word The filter value. 16663 * @example 16664 * var combobox = $("#combobox").data("kendoComboBox"); 16665 * 16666 * // Searches for item which has "In" in the name. 16667 * combobox.search("In"); 16668 */ 16669 search: function(word) { 16670 word = typeof word === "string" ? word : this.text(); 16671 var that = this, 16672 length = word.length, 16673 options = that.options, 16674 ignoreCase = options.ignoreCase, 16675 filter = options.filter, 16676 field = options.dataTextField, 16677 filters, expression; 16678 16679 clearTimeout(that._typing); 16680 16681 if (length >= options.minLength) { 16682 if (filter === "none") { 16683 that._filter(word); 16684 } else { 16685 that._open = true; 16686 that._state = STATE_FILTER; 16687 16688 expression = that.dataSource.filter() || {}; 16689 removeFiltersForField(expression, field); 16690 16691 filters = expression.filters || []; 16692 filters.push({ 16693 value: ignoreCase ? word.toLowerCase() : word, 16694 field: field, 16695 operator: filter, 16696 ignoreCase: ignoreCase 16697 }); 16698 16699 that.dataSource.filter(filters); 16700 } 16701 } 16702 }, 16703 16704 /** 16705 * Forces a suggestion onto the text of the ComboBox. 16706 * @param {string} value Characters to force a suggestion. 16707 * @example 16708 * // note that this suggest is not the same as the configuration method 16709 * // suggest which enables/disables auto suggesting for the ComboBox 16710 * // 16711 * // get a referenence to the Kendo UI ComboBox 16712 * var combobox = $("#combobox").data("kendoComboBox"); 16713 * // force a suggestion to the item with the name "Inception" 16714 * combobox.suggest("Inception"); 16715 */ 16716 suggest: function(word) { 16717 var that = this, 16718 element = that.input[0], 16719 value = that.text(), 16720 caret = List.caret(element), 16721 key = that._last, 16722 idx; 16723 16724 if (key == keys.BACKSPACE || key == keys.DELETE) { 16725 that._last = undefined; 16726 return; 16727 } 16728 16729 word = word || ""; 16730 16731 if (typeof word !== "string") { 16732 idx = List.inArray(word[0], that.ul[0]); 16733 16734 if (idx > -1) { 16735 word = that._text(that.dataSource.view()[idx]); 16736 } else { 16737 word = ""; 16738 } 16739 } 16740 16741 if (caret <= 0) { 16742 caret = value.toLowerCase().indexOf(word.toLowerCase()) + 1; 16743 } 16744 16745 if (word) { 16746 idx = word.toLowerCase().indexOf(value.toLowerCase()); 16747 if (idx > -1) { 16748 value += word.substring(idx + value.length); 16749 } 16750 } else { 16751 value = value.substring(0, caret); 16752 } 16753 16754 if (value.length !== caret || !word) { 16755 element.value = value; 16756 List.selectText(element, caret, value.length); 16757 } 16758 }, 16759 16760 /** 16761 * Gets/Sets the text of the ComboBox. 16762 * @param {String} text The text to set. 16763 * @returns {String} The text of the combobox. 16764 * @example 16765 * var combobox = $("#combobox").data("kendoComboBox"); 16766 * 16767 * // get the text of the combobox. 16768 * var text = combobox.text(); 16769 */ 16770 text: function (text) { 16771 text = text === null ? "" : text; 16772 16773 var that = this, 16774 textAccessor = that._text, 16775 input = that.input[0], 16776 ignoreCase = that.options.ignoreCase, 16777 loweredText = text, 16778 dataItem; 16779 16780 if (text !== undefined) { 16781 dataItem = that.dataItem(); 16782 16783 if (dataItem && textAccessor(dataItem) === text) { 16784 return; 16785 } 16786 16787 if (ignoreCase) { 16788 loweredText = loweredText.toLowerCase(); 16789 } 16790 16791 that._select(function(data) { 16792 data = textAccessor(data); 16793 16794 if (ignoreCase) { 16795 data = (data + "").toLowerCase(); 16796 } 16797 16798 return data === loweredText; 16799 }); 16800 16801 if (that.selectedIndex < 0) { 16802 that._custom(text); 16803 input.value = text; 16804 } 16805 16806 } else { 16807 return input.value; 16808 } 16809 }, 16810 16811 /** 16812 * Toggles the drop-down list between opened and closed state. 16813 * @param {Boolean} toggle Defines the whether to open/close the drop-down list. 16814 * @example 16815 * var combobox = $("#combobox").data("kendoComboBox"); 16816 * 16817 * // toggles the open state of the drop-down list. 16818 * combobox.toggle(); 16819 */ 16820 toggle: function(toggle) { 16821 var that = this; 16822 16823 that._toggle(toggle); 16824 }, 16825 16826 /** 16827 * Gets/Sets the value of the combobox. If the value is undefined, text of the data item will be used. 16828 * @param {String} value The value to set. 16829 * @returns {String} The value of the combobox. 16830 * @example 16831 * var combobox = $("#combobox").data("kendoComboBox"); 16832 * 16833 * // get the value of the combobox.
16834 * var value = combobox.value(); 16835 * 16836 * // set the value of the combobox. 16837 * combobox.value("1"); //looks for item which has value "1" 16838 */ 16839 value: function(value) { 16840 var that = this, 16841 idx; 16842 16843 if (value !== undefined) { 16844 if (value && that._valueOnFetch(value)) { 16845 return; 16846 } 16847 16848 idx = that._index(value); 16849 16850 if (idx > -1) { 16851 that.select(idx); 16852 } else { 16853 that.current(NULL); 16854 that._custom(value); 16855 that.text(value); 16856 } 16857 16858 that._old = that._accessor(); 16859 that._oldIndex = that.selectedIndex; 16860 } else { 16861 return that._accessor(); 16862 } 16863 }, 16864 16865 _accept: function(li) { 16866 var that = this; 16867 16868 if (li && that.popup.visible()) { 16869 16870 if (that._state === STATE_FILTER) { 16871 that._state = STATE_ACCEPT; 16872 } 16873 16874 that._focus(li); 16875 } else { 16876 that.text(that.text()); 16877 that._change(); 16878 } 16879 }, 16880 16881 _custom: function(value) { 16882 var that = this, 16883 element = that.element, 16884 custom = that._option; 16885 16886 if (element.is(SELECT)) { 16887 if (!custom) { 16888 custom = that._option = $("<option/>"); 16889 element.append(custom); 16890 } 16891 custom.text(value); 16892 custom[0].selected = true; 16893 } else { 16894 element.val(value); 16895 } 16896 }, 16897 16898 _filter: function(word) { 16899 var that = this, 16900 options = that.options, 16901 dataSource = that.dataSource, 16902 ignoreCase = options.ignoreCase, 16903 predicate = function (dataItem) { 16904 var text = that._text(dataItem); 16905 if (text !== undefined) { 16906 text = text + ""; 16907 if (text !== "" && word === "") { 16908 return false; 16909 } 16910 16911 if (ignoreCase) { 16912 text = text.toLowerCase(); 16913 } 16914 16915 return text.indexOf(word) === 0; 16916 } 16917 }; 16918 16919 if (ignoreCase) { 16920 word = word.toLowerCase(); 16921 } 16922 16923 if (!that.ul[0].firstChild) { 16924 dataSource.one(CHANGE, function () { that.search(word); }).fetch(); 16925 return; 16926 } 16927 16928 if (that._highlight(predicate) !== -1) { 16929 if (options.suggest && that._current) { 16930 that.suggest(that._current); 16931 } 16932 that.open(); 16933 } 16934 16935 that._hideBusy(); 16936 }, 16937 16938 _highlight: function(li) { 16939 var that = this, idx; 16940 16941 if (li === undefined || li === null) { 16942 return -1; 16943 } 16944 16945 li = that._get(li); 16946 idx = List.inArray(li[0], that.ul[0]); 16947 16948 if (idx == -1) { 16949 if (that.options.highlightFirst && !that.text()) { 16950 li = $(that.ul[0].firstChild); 16951 } else { 16952 li = NULL; 16953 } 16954 } 16955 16956 that.current(li); 16957 16958 return idx; 16959 }, 16960 16961 _input: function() { 16962 var that = this, 16963 element = that.element[0], 16964 wrapper = that.wrapper, 16965 SELECTOR = ".k-input", 16966 input; 16967 16968 input = wrapper.find(SELECTOR); 16969 16970 if (!input[0]) { 16971 wrapper.append('<span unselectable="on" class="k-dropdown-wrap k-state-default"><input class="k-input" type="text" autocomplete="off"/><span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-arrow-down">select</span></span></span>') 16972 .append(that.element); 16973 16974 input = wrapper.find(SELECTOR); 16975 } 16976 16977 input[0].style.cssText = element.style.cssText; 16978 input.addClass(element.className) 16979 .val(element.value) 16980 .css({ 16981 width: "100%", 16982 height: element.style.height 16983 }) 16984 .show(); 16985 16986 if (placeholderSupported) { 16987 input.attr("placeholder", that.options.placeholder); 16988 } 16989 16990 that._focused = that.input = input; 16991 that._arrow = wrapper.find(".k-icon"); 16992 that._inputWrapper = $(wrapper[0].firstChild); 16993 }, 16994 16995 _keydown: function(e) { 16996 var that = this, 16997 key = e.keyCode,
16998 input = that.input; 16999 17000 that._last = key; 17001 17002 clearTimeout(that._typing); 17003 17004 if (key == keys.TAB) { 17005 that.text(input.val()); 17006 17007 if (that._state === STATE_FILTER && that.selectedIndex > -1) { 17008 that._state = STATE_ACCEPT; 17009 } 17010 } else if (!that._move(e)) { 17011 that._search(); 17012 } 17013 }, 17014 17015 _placeholder: function(show) { 17016 if (placeholderSupported) { 17017 return; 17018 } 17019 17020 var that = this, 17021 input = that.input, 17022 placeholder = that.options.placeholder, 17023 value; 17024 17025 if (placeholder) { 17026 value = that.value(); 17027 17028 if (show === undefined) { 17029 show = !value; 17030 } 17031 17032 input.toggleClass("k-readonly", show); 17033 17034 if (!show) { 17035 if (!value) { 17036 placeholder = ""; 17037 } else { 17038 return; 17039 } 17040 } 17041 17042 input.val(placeholder); 17043 } 17044 }, 17045 17046 _search: function() { 17047 var that = this; 17048 17049 that._typing = setTimeout(function() { 17050 var value = that.text(); 17051 if (that._prev !== value) { 17052 that._prev = value; 17053 that.search(value); 17054 } 17055 }, that.options.delay); 17056 }, 17057 17058 _select: function(li) { 17059 var that = this, 17060 text, 17061 value, 17062 idx = that._highlight(li), 17063 data = that._data(); 17064 17065 that.selectedIndex = idx; 17066 17067 if (idx !== -1) { 17068 that._current.addClass(STATE_SELECTED); 17069 17070 data = data[idx]; 17071 text = that._text(data); 17072 value = that._value(data); 17073 17074 that._prev = that.input[0].value = text; 17075 that._accessor(value !== undefined ? value : text, idx); 17076 that._placeholder(); 17077 } 17078 }, 17079 17080 _selectItem: function() { 17081 var that = this, 17082 dataSource = that.dataSource, 17083 expression = dataSource.filter() || {}; 17084 17085 removeFiltersForField(expression, that.options.dataTextField); 17086 17087 that.dataSource.one(CHANGE, function() { 17088 var value = that.value(); 17089 if (value) { 17090 that.value(value); 17091 } else { 17092 that.select(that.options.index); 17093 } 17094 }).filter(expression); 17095 }, 17096 17097 _wrapper: function() { 17098 var that = this, 17099 element = that.element, 17100 wrapper; 17101 17102 wrapper = element.parent(); 17103 17104 if (!wrapper.is("span.k-widget")) { 17105 wrapper = element.hide().wrap("<span />").parent(); 17106 } 17107 17108 wrapper[0].style.cssText = element[0].style.cssText; 17109 that.wrapper = wrapper.addClass("k-widget k-combobox k-header") 17110 .addClass(element[0].className) 17111 .show(); 17112 } 17113 }); 17114 17115 function removeFiltersForField(expression, field) { 17116 if (expression.filters) { 17117 expression.filters = $.grep(expression.filters, function(filter) { 17118 removeFiltersForField(filter, field); 17119 if (filter.filters) { 17120 return filter.filters.length; 17121 } else { 17122 return filter.field != field; 17123 } 17124 }); 17125 } 17126 } 17127 17128 ui.plugin(ComboBox); 17129})(jQuery); 17130(function($, undefined) { 17131 var kendo = window.kendo, 17132 ui = kendo.ui, 17133 DataSource = kendo.data.DataSource, 17134 Groupable = ui.Groupable, 17135 tbodySupportsInnerHtml = kendo.support.tbodyInnerHtml, 17136 Widget = ui.Widget, 17137 keys = kendo.keys, 17138 isPlainObject = $.isPlainObject, 17139 extend = $.extend, 17140 map = $.map, 17141 isArray = $.isArray, 17142 proxy = $.proxy, 17143 isFunction = $.isFunction, 17144 isEmptyObject = $.isEmptyObject, 17145 math = Math, 17146 REQUESTSTART = "requestStart", 17147 ERROR = "error", 17148 ROW_SELECTOR = "tbody>tr:not(.k-grouping-row,.k-detail-row,.k-group-footer):visible", 17149 DATA_CELL = ":not(.k-group-cell,.k-hierarchy-cell):visible", 17150 SELECTION_CELL_SELECTOR = "tbody>tr:not(.k-grouping-row,.k-detail-row,.k-group-footer) > td:not(.k-group-cell,.k-hierarchy-cell)", 17151 CELL_SELECTOR = ROW_SELECTOR + ">td" + DATA_CELL, 17152 FIRST_CELL_SELECTOR = CELL_SELECTOR + ":first", 17153 EDIT = "edit", 17154 SAVE = "save", 17155 REMOVE = "remove", 17156 DETAILINIT = "detailInit", 17157 CHANGE = "change", 17158 SAVECHANGES = "saveChanges", 17159 DATABOUND = "dataBound", 17160 DETAILEXPAND = "detailExpand", 17161 DETAILCOLLAPSE = "detailCollapse", 17162 FOCUSED = "k-state-focused", 17163 FOCUSABLE = "k-focusable", 17164 SELECTED = "k-state-selected", 17165 COLUMNRESIZE = "columnResize", 17166 COLUMNREORDER = "columnReorder", 17167 CLICK = "click", 17168 HEIGHT = "height", 17169 TABINDEX = "tabIndex", 17170 FUNCTION = "function", 17171 STRING = "string", 17172 DELETECONFIRM = "Are you sure you want to delete this record?", 17173 formatRegExp = /\}/ig, 17174 indicatorWidth = 3,
17175 templateHashRegExp = /#/ig, 17176 COMMANDBUTTONTEMP = '<a class="k-button k-button-icontext #=className#" #=attr# href="\\#"><span class="#=iconClass# #=imageClass#"></span>#=text#</a>'; 17177 17178 var VirtualScrollable = Widget.extend({ 17179 init: function(element, options) { 17180 var that = this; 17181 17182 Widget.fn.init.call(that, element, options); 17183 that._refreshHandler = proxy(that.refresh, that); 17184 that.setDataSource(options.dataSource); 17185 that.wrap(); 17186 }, 17187 17188 setDataSource: function(dataSource) { 17189 var that = this; 17190 if (that.dataSource) { 17191 that.dataSource.unbind(CHANGE, that._refreshHandler); 17192 } 17193 that.dataSource = dataSource; 17194 that.dataSource.bind(CHANGE, that._refreshHandler); 17195 }, 17196 17197 options: { 17198 name: "VirtualScrollable", 17199 itemHeight: $.noop 17200 }, 17201 17202 wrap: function() { 17203 var that = this, 17204 // workaround for IE issue where scroll is not raised if container is same width as the scrollbar 17205 scrollbar = kendo.support.scrollbar() + 1, 17206 element = that.element; 17207 17208 element.css( { 17209 width: "auto", 17210 paddingRight: scrollbar, 17211 overflow: "hidden" 17212 }); 17213 that.content = element.children().first(); 17214 that.wrapper = that.content.wrap('<div class="k-virtual-scrollable-wrap"/>') 17215 .parent() 17216 .bind("DOMMouseScroll", proxy(that._wheelScroll, that)) 17217 .bind("mousewheel", proxy(that._wheelScroll, that)); 17218 17219 if (kendo.support.touch) { 17220 that.drag = new kendo.Drag(that.wrapper, { 17221 global: true, 17222 move: function(e) { 17223 that.verticalScrollbar.scrollTop(that.verticalScrollbar.scrollTop() - e.y.delta); 17224 e.preventDefault(); 17225 } 17226 }); 17227 } 17228 17229 that.verticalScrollbar = $('<div class="k-scrollbar k-scrollbar-vertical" />') 17230 .css({ 17231 width: scrollbar 17232 }).appendTo(element) 17233 .bind("scroll", proxy(that._scroll, that)); 17234 }, 17235 17236 _wheelScroll: function(e) { 17237 var that = this, 17238 scrollTop = that.verticalScrollbar.scrollTop(), 17239 originalEvent = e.originalEvent, 17240 delta; 17241 17242 e.preventDefault(); 17243 17244 if (originalEvent.wheelDelta) { 17245 delta = originalEvent.wheelDelta; 17246 } else if (originalEvent.detail) { 17247 delta = (-originalEvent.detail) * 10; 17248 } else if ($.browser.opera) { 17249 delta = -originalEvent.wheelDelta; 17250 } 17251 that.verticalScrollbar.scrollTop(scrollTop + (-delta)); 17252 }, 17253 17254 _scroll: function(e) { 17255 var that = this, 17256 scrollTop = e.currentTarget.scrollTop, 17257 dataSource = that.dataSource, 17258 rowHeight = that.itemHeight, 17259 skip = dataSource.skip() || 0, 17260 start = that._rangeStart || skip, 17261 height = that.element.innerHeight(), 17262 isScrollingUp = !!(that._scrollbarTop && that._scrollbarTop > scrollTop), 17263 firstItemIndex = math.max(math.floor(scrollTop / rowHeight), 0), 17264 lastItemIndex = math.max(firstItemIndex + math.floor(height / rowHeight), 0); 17265 17266 that._scrollTop = scrollTop - (start * rowHeight); 17267 that._scrollbarTop = scrollTop; 17268 17269 if (!that._fetch(firstItemIndex, lastItemIndex, isScrollingUp)) { 17270 that.wrapper[0].scrollTop = that._scrollTop; 17271 } 17272 }, 17273 17274 _fetch: function(firstItemIndex, lastItemIndex, scrollingUp) { 17275 var that = this, 17276 dataSource = that.dataSource, 17277 itemHeight = that.itemHeight, 17278 take = dataSource.take(), 17279 rangeStart = that._rangeStart || dataSource.skip() || 0, 17280 currentSkip = math.floor(firstItemIndex / take) * take, 17281 fetching = false, 17282 prefetchAt = 0.33; 17283 17284 if (firstItemIndex < rangeStart) { 17285 17286 fetching = true; 17287 rangeStart = math.max(0, lastItemIndex - take); 17288 that._scrollTop = (firstItemIndex - rangeStart) * itemHeight; 17289 that._page(rangeStart, take); 17290 17291 } else if (lastItemIndex >= rangeStart + take && !scrollingUp) { 17292 17293 fetching = true; 17294 rangeStart = firstItemIndex; 17295 that._scrollTop = itemHeight; 17296 that._page(rangeStart, take); 17297 17298 } else if (!that._fetching) { 17299 17300 if (firstItemIndex < (currentSkip + take) - take * prefetchAt && firstItemIndex > take) { 17301 dataSource.prefetch(currentSkip - take, take); 17302 } 17303 if (lastItemIndex > currentSkip + take * prefetchAt) { 17304 dataSource.prefetch(currentSkip + take, take); 17305 } 17306 17307 } 17308 return fetching; 17309 }, 17310 17311 _page: function(skip, take) { 17312 var that = this, 17313 dataSource = that.dataSource; 17314 17315 clearTimeout(that._timeout); 17316 that._fetching = true; 17317 that._rangeStart = skip; 17318 17319 if (dataSource.inRange(skip, take)) { 17320 dataSource.range(skip, take); 17321 } else { 17322 kendo.ui.progress(that.wrapper.parent(), true); 17323 that._timeout = setTimeout(function() { 17324 dataSource.range(skip, take); 17325 }, 100); 17326 } 17327 }, 17328 17329 refresh: function() { 17330 var that = this, 17331 html = "", 17332 maxHeight = 250000, 17333 dataSource = that.dataSource, 17334 rangeStart = that._rangeStart, 17335 scrollbar = kendo.support.scrollbar(), 17336 wrapperElement = that.wrapper[0], 17337 totalHeight, 17338 idx, 17339 itemHeight; 17340 17341 kendo.ui.progress(that.wrapper.parent(), false); 17342 clearTimeout(that._timeout); 17343 17344 itemHeight = that.itemHeight = that.options.itemHeight() || 0; 17345 17346 var addScrollBarHeight = (wrapperElement.scrollWidth > wrapperElement.offsetWidth) ? scrollbar : 0; 17347 17348 totalHeight = dataSource.total() * itemHeight + addScrollBarHeight; 17349 17350 for (idx = 0; idx < math.floor(totalHeight / maxHeight); idx++) { 17351 html += '<div style="width:1px;height:' + maxHeight + 'px"></div>'; 17352 } 17353 17354 if (totalHeight % maxHeight) { 17355 html += '<div style="width:1px;height:' + (totalHeight % maxHeight) + 'px"></div>'; 17356 } 17357 17358 that.verticalScrollbar.html(html); 17359 wrapperElement.scrollTop = that._scrollTop; 17360 17361 if (that.drag) { 17362 that.drag.cancel(); 17363 } 17364 17365 if (rangeStart && !that._fetching) { // we are rebound from outside local range should be reset 17366 that._rangeStart = dataSource.skip(); 17367 } 17368 that._fetching = false; 17369 } 17370 });
17371 17372 function groupCells(count) { 17373 return new Array(count + 1).join('<td class="k-group-cell"> </td>'); 17374 } 17375 17376 var defaultCommands = { 17377 create: { 17378 text: "Add new record", 17379 imageClass: "k-add", 17380 className: "k-grid-add", 17381 iconClass: "k-icon" 17382 }, 17383 cancel: { 17384 text: "Cancel changes", 17385 imageClass: "k-cancel", 17386 className: "k-grid-cancel-changes", 17387 iconClass: "k-icon" 17388 }, 17389 save: { 17390 text: "Save changes", 17391 imageClass: "k-update", 17392 className: "k-grid-save-changes", 17393 iconClass: "k-icon" 17394 }, 17395 destroy: { 17396 text: "Delete", 17397 imageClass: "k-delete", 17398 className: "k-grid-delete", 17399 iconClass: "k-icon" 17400 }, 17401 edit: { 17402 text: "Edit", 17403 imageClass: "k-edit", 17404 className: "k-grid-edit", 17405 iconClass: "k-icon" 17406 }, 17407 update: { 17408 text: "Update", 17409 imageClass: "k-update", 17410 className: "k-grid-update", 17411 iconClass: "k-icon" 17412 }, 17413 canceledit: { 17414 text: "Cancel", 17415 imageClass: "k-cancel", 17416 className: "k-grid-cancel", 17417 iconClass: "k-icon" 17418 } 17419 }; 17420 17421 function heightAboveHeader(context) { 17422 var top = 0; 17423 $('> .k-grouping-header, > .k-grid-toolbar', context).each(function () { 17424 top += this.offsetHeight; 17425 }); 17426 return top; 17427 } 17428 17429 function cursor(context, value) { 17430 $('th, th .k-grid-filter, th .k-link', context) 17431 .add(document.body) 17432 .css('cursor', value); 17433 } 17434 function buildEmptyAggregatesObject(aggregates) { 17435 var idx, 17436 length, 17437 fieldsMap = {}; 17438 17439 if (!isEmptyObject(aggregates)) { 17440 if (isArray(aggregates)) { 17441 for (idx = 0, length = aggregates.length; idx < length; idx++) { 17442 fieldsMap[aggregates[idx].field] = {}; 17443 } 17444 } else { 17445 fieldsMap[aggregates.field] = {}; 17446 } 17447 } 17448 return fieldsMap; 17449 } 17450 17451 function reorder(selector, sourceIndex, destIndex) { 17452 var source = selector.eq(sourceIndex), 17453 dest = selector.eq(destIndex); 17454 17455 source[sourceIndex > destIndex ? "insertBefore" : "insertAfter"](dest); 17456 } 17457 17458 var Grid = Widget.extend({ 17459 init: function(element, options) { 17460 var that = this; 17461 17462 options = isArray(options) ? { dataSource: options } : options; 17463 17464 Widget.fn.init.call(that, element, options); 17465 17466 that._element(); 17467 17468 that._columns(that.options.columns); 17469 17470 that._dataSource(); 17471 17472 that._tbody(); 17473 17474 that._pageable(); 17475 17476 that._toolbar(); 17477 17478 that._thead(); 17479 17480 that._groupable(); 17481 17482 that._templates(); 17483 17484 that._navigatable(); 17485 17486 that._selectable(); 17487 17488 that._details(); 17489 17490 that._editable(); 17491 17492 if (that.options.autoBind) { 17493 that.dataSource.fetch(); 17494 } 17495 17496 kendo.notify(that); 17497 }, 17498 17499 events: [ 17500 CHANGE, 17501 "dataBinding", 17502 DATABOUND, 17503 DETAILEXPAND, 17504 DETAILCOLLAPSE, 17505 DETAILINIT, 17506 EDIT, 17507 SAVE, 17508 REMOVE, 17509 SAVECHANGES, 17510 COLUMNRESIZE, 17511 COLUMNREORDER 17512 ], 17513 17514 setDataSource: function(dataSource) { 17515 var that = this; 17516 17517 that.options.dataSource = dataSource; 17518 17519 that._dataSource(); 17520 17521 that._pageable(); 17522 17523 if (that.options.groupable) { 17524 that._groupable(); 17525 } 17526 17527 that._thead(); 17528 17529 if (that.virtualScrollable) { 17530 that.virtualScrollable.setDataSource(that.options.dataSource); 17531 } 17532 17533 if (that.options.autoBind) { 17534 dataSource.fetch();
17535 } 17536 }, 17537 17538 options: { 17539 name: "Grid", 17540 columns: [], 17541 toolbar: null, 17542 autoBind: true, 17543 filterable: false, 17544 scrollable: true, 17545 sortable: false, 17546 selectable: false, 17547 navigatable: false, 17548 pageable: false, 17549 editable: false, 17550 groupable: false, 17551 rowTemplate: "", 17552 altRowTemplate: "", 17553 dataSource: {}, 17554 height: null, 17555 resizable: false, 17556 reorderable: false 17557 }, 17558 17559 setOptions: function(options) { 17560 var that = this; 17561 17562 Widget.fn.setOptions.call(this, options); 17563 17564 that._templates(); 17565 }, 17566 17567 items: function() { 17568 return this.tbody.children(':not(.k-grouping-row,.k-detail-row,.k-group-footer)'); 17569 }, 17570 17571 _element: function() { 17572 var that = this, 17573 table = that.element; 17574 17575 if (!table.is("table")) { 17576 table = $("<table />").appendTo(that.element); 17577 } 17578 17579 that.table = table.attr("cellspacing", 0); 17580 17581 that._wrapper(); 17582 }, 17583 17584 positionColumnResizeHandles: function() { 17585 var that = this, 17586 left = 0, 17587 th, 17588 scrollable = that.options.scrollable, 17589 container = scrollable ? that.wrapper.find(".k-grid-header-wrap") : that.wrapper; 17590 17591 if (that.options.resizable) { 17592 container.find("> .k-resize-handle").remove(); 17593 17594 that.thead.find("th").each(function() { 17595 left += this.offsetWidth; 17596 17597 th = $(this); 17598 if (th.is(":not(.k-group-cell,.k-hierarchy-cell)")) { 17599 $('<div class="k-resize-handle" />') 17600 .css({ 17601 left: left - indicatorWidth, 17602 top: scrollable ? 0 : heightAboveHeader(that.wrapper), 17603 width: indicatorWidth * 2 17604 }) 17605 .appendTo(container) 17606 .data("th", th); 17607 } 17608 }); 17609 } 17610 }, 17611 17612 _resizable: function() { 17613 var that = this, 17614 options = that.options, 17615 container, 17616 columnStart, 17617 columnWidth, 17618 gridWidth, 17619 col; 17620 17621 if (options.resizable) { 17622 container = options.scrollable ? that.wrapper.find(".k-grid-header-wrap") : that.wrapper; 17623 17624 that.positionColumnResizeHandles(); 17625 17626 container.kendoResizable({ 17627 handle: ".k-resize-handle", 17628 hint: function(handle) { 17629 return $('<div class="k-grid-resize-indicator" />').css({ 17630 height: handle.data("th").outerHeight() + that.tbody.attr("clientHeight") 17631 }); 17632 }, 17633 start: function(e) { 17634 var th = $(e.currentTarget).data("th"), 17635 index = $.inArray(th[0], th.parent().children(":visible")), 17636 contentTable = that.tbody.parent(), 17637 footer = that.footer || $(); 17638 17639 cursor(that.wrapper, th.css('cursor')); 17640 17641 if (options.scrollable) { 17642 col = that.thead.parent().find("col:eq(" + index + ")") 17643 .add(contentTable.children("colgroup").find("col:eq(" + index + ")")) 17644 .add(footer.find("colgroup").find("col:eq(" + index + ")")); 17645 } else { 17646 col = contentTable.children("colgroup").find("col:eq(" + index + ")"); 17647 } 17648 17649 columnStart = e.pageX; 17650 columnWidth = th.outerWidth(); 17651 gridWidth = that.tbody.outerWidth(); 17652 }, 17653 resize: function(e) { 17654 var width = columnWidth + e.pageX - columnStart, 17655 footer = that.footer || $(), 17656 left = 0; 17657 17658 if (width > 10) { 17659 col.css('width', width); 17660 17661 if (options.scrollable) {
17662 that._footerWidth = gridWidth + e.pageX - columnStart; 17663 that.tbody.parent() 17664 .add(that.thead.parent()) 17665 .add(footer.find("table")) 17666 .css('width', that._footerWidth); 17667 } 17668 17669 $('.k-resize-handle', that.wrapper).each(function () { 17670 left += $(this).data('th').outerWidth(); 17671 $(this).css('left', left - indicatorWidth); 17672 }); 17673 } 17674 }, 17675 resizeend: function(e) { 17676 var th = $(e.currentTarget).data("th"), 17677 newWidth = th.outerWidth(), 17678 column; 17679 17680 cursor(that.wrapper, ""); 17681 17682 if (columnWidth != newWidth) { 17683 column = that.columns[th.parent().find("th:not(.k-group-cell,.k-hierarchy-cell)").index(th)]; 17684 17685 column.width = newWidth; 17686 17687 that.trigger(COLUMNRESIZE, { 17688 column: column, 17689 oldWidth: columnWidth, 17690 newWidth: newWidth 17691 }); 17692 } 17693 17694 that.positionColumnResizeHandles(); 17695 17696 if (that._hasDetails()) { 17697 that.tbody.find(">tr.k-detail-row [" + kendo.attr("role") + "=grid]").kendoGrid("positionColumnResizeHandles"); 17698 } 17699 } 17700 }); 17701 } 17702 }, 17703 17704 _draggable: function() { 17705 var that = this; 17706 if (that.options.reorderable) { 17707 that._draggableInstance = that.thead.kendoDraggable({ 17708 group: kendo.guid(), 17709 filter: ".k-header:not(.k-group-cell,.k-hierarchy-cell)[" + kendo.attr("field") + "]", 17710 hint: function(target) { 17711 return $('<div class="k-header k-drag-clue" />') 17712 .css({ 17713 width: target.width(), 17714 paddingLeft: target.css("paddingLeft"), 17715 paddingRight: target.css("paddingRight"), 17716 lineHeight: target.height() + "px", 17717 paddingTop: target.css("paddingTop"), 17718 paddingBottom: target.css("paddingBottom") 17719 }) 17720 .html(target.attr(kendo.attr("title")) || target.attr(kendo.attr("field"))) 17721 .prepend('<span class="k-icon k-drag-status k-denied" />'); 17722 } 17723 }).data("kendoDraggable"); 17724 } 17725 }, 17726 17727 _reorderable: function() { 17728 var that = this; 17729 if (that.options.reorderable) { 17730 that.thead.kendoReorderable({ 17731 draggable: that._draggableInstance, 17732 change: function(e) { 17733 var column = that.columns[e.oldIndex]; 17734 that.trigger(COLUMNREORDER, { 17735 newIndex: e.newIndex, 17736 oldIndex: e.oldIndex, 17737 column: column 17738 }); 17739 that.reorderColumn(e.newIndex, column); 17740 } 17741 }); 17742 } 17743 }, 17744 17745 reorderColumn: function(destIndex, column) { 17746 var that = this, 17747 sourceIndex = $.inArray(column, that.columns), 17748 rows, 17749 idx, 17750 length; 17751 17752 if (sourceIndex === destIndex) { 17753 return; 17754 } 17755 17756 that.columns.splice(sourceIndex, 1); 17757 that.columns.splice(destIndex, 0, column); 17758 that._templates(); 17759 17760 reorder(that.thead.prev().find("col:not(.k-group-col,.k-hierarchy-col)"), sourceIndex, destIndex); 17761 if (that.options.scrollable) { 17762 reorder(that.tbody.prev().find("col:not(.k-group-col,.k-hierarchy-col)"), sourceIndex, destIndex); 17763 } 17764 17765 reorder(that.thead.find(".k-header:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex); 17766 17767 if (that.footer) { 17768 reorder(that.footer.find(".k-grid-footer-wrap>table>colgroup>col:not(.k-group-col,.k-hierarchy-col)"), sourceIndex, destIndex); 17769 reorder(that.footer.find(".k-footer-template>td:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex); 17770 } 17771 17772 rows = that.tbody.children(":not(.k-grouping-row,.k-detail-row)"); 17773 for (idx = 0, length = rows.length; idx < length; idx += 1) { 17774 reorder(rows.eq(idx).find(">td:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex); 17775 } 17776 17777 if (that.options.resizable) { 17778 that.positionColumnResizeHandles(); 17779
17780 if (that._hasDetails()) { 17781 that.tbody.find(">tr.k-detail-row [" + kendo.attr("role") + "=grid]").kendoGrid("positionColumnResizeHandles"); 17782 } 17783 } 17784 }, 17785 17786 cellIndex: function(td) { 17787 return $(td).parent().find('td:not(.k-group-cell,.k-hierarchy-cell)').index(td); 17788 }, 17789 17790 _modelForContainer: function(container) { 17791 if (!container.is("tr") && this._editMode() !== "popup") { 17792 container = container.closest("tr"); 17793 } 17794 17795 var id = container.attr(kendo.attr("uid")); 17796 17797 return this.dataSource.getByUid(id); 17798 }, 17799 17800 _editable: function() { 17801 var that = this, 17802 editable = that.options.editable, 17803 handler = function () { 17804 var target = document.activeElement, 17805 cell = that._editContainer; 17806 17807 if (cell && !$.contains(cell[0], target) && cell[0] !== target && !$(target).closest(".k-animation-container").length) { 17808 if (that.editable.end()) { 17809 that.closeCell(); 17810 } 17811 } 17812 }; 17813 17814 if (editable) { 17815 var mode = that._editMode(); 17816 17817 if (mode === "incell") { 17818 if (editable.update !== false) { 17819 that.wrapper.delegate("tr:not(.k-grouping-row) > td:not(.k-hierarchy-cell,.k-detail-cell,.k-group-cell,.k-edit-cell,:has(a.k-grid-delete))", CLICK, function(e) { 17820 var td = $(this); 17821 17822 if (td.closest("tbody")[0] !== that.tbody[0] || $(e.target).is(":input")) { 17823 return; 17824 } 17825 17826 if (that.editable) { 17827 if (that.editable.end()) { 17828 that.closeCell(); 17829 that.editCell(td); 17830 } 17831 } else { 17832 that.editCell(td); 17833 } 17834 17835 }); 17836 17837 that.wrapper.bind("focusin", function(e) { 17838 clearTimeout(that.timer); 17839 that.timer = null; 17840 }); 17841 that.wrapper.bind("focusout", function(e) { 17842 that.timer = setTimeout(handler, 1); 17843 }); 17844 } 17845 } else { 17846 if (editable.update !== false) { 17847 that.wrapper.delegate("tbody>tr:not(.k-detail-row,.k-grouping-row):visible a.k-grid-edit", CLICK, function(e) { 17848 e.preventDefault(); 17849 that.editRow($(this).closest("tr")); 17850 }); 17851 } 17852 } 17853 17854 if (editable.destroy !== false) { 17855 that.wrapper.delegate("tbody>tr:not(.k-detail-row,.k-grouping-row):visible a.k-grid-delete", CLICK, function(e) { 17856 e.preventDefault(); 17857 that.removeRow($(this).closest("tr")); 17858 }); 17859 } 17860 } 17861 }, 17862 17863 editCell: function(cell) { 17864 var that = this, 17865 column = that.columns[that.cellIndex(cell)], 17866 model = that._modelForContainer(cell); 17867 17868 17869 if (model && (!model.editable || model.editable(column.field)) && !column.command && column.field) { 17870 17871 that._attachModelChange(model); 17872 17873 that._editContainer = cell; 17874 17875 that.editable = cell.addClass("k-edit-cell") 17876 .kendoEditable({ 17877 fields: { field: column.field, format: column.format, editor: column.editor }, 17878 model: model, 17879 change: function(e) { 17880 if (that.trigger(SAVE, { values: e.values, container: cell, model: model } )) { 17881 e.preventDefault(); 17882 } 17883 } 17884 }).data("kendoEditable"); 17885
17886 cell.parent().addClass("k-grid-edit-row"); 17887 17888 that.trigger(EDIT, { container: cell, model: model }); 17889 } 17890 }, 17891 17892 _destroyEditable: function() { 17893 var that = this; 17894 17895 if (that.editable) { 17896 that._detachModelChange(); 17897 17898 that.editable.destroy(); 17899 delete that.editable; 17900 17901 if (that._editMode() === "popup") { 17902 that._editContainer.data("kendoWindow").close(); 17903 } 17904 17905 that._editContainer = null; 17906 } 17907 }, 17908 17909 _attachModelChange: function(model) { 17910 var that = this; 17911 17912 that._modelChangeHandler = function(e) { 17913 that._modelChange({ field: e.field, model: this }); 17914 }; 17915 17916 model.bind("change", that._modelChangeHandler); 17917 }, 17918 17919 _detachModelChange: function() { 17920 var that = this, 17921 container = that._editContainer, 17922 model = that._modelForContainer(container); 17923 17924 if (model) { 17925 model.unbind(CHANGE, that._modelChangeHandler); 17926 } 17927 }, 17928 17929 closeCell: function() { 17930 var that = this, 17931 cell = that._editContainer.removeClass("k-edit-cell"), 17932 id = cell.closest("tr").attr(kendo.attr("uid")), 17933 column = that.columns[that.cellIndex(cell)], 17934 model = that.dataSource.getByUid(id); 17935 17936 cell.parent().removeClass("k-grid-edit-row"); 17937 17938 that._destroyEditable(); // editable should be destoryed before content of the container is changed 17939 17940 that._displayCell(cell, column, model); 17941 17942 if (cell.hasClass("k-dirty-cell")) { 17943 $('<span class="k-dirty"/>').prependTo(cell); 17944 } 17945 }, 17946 17947 _displayCell: function(cell, column, dataItem) { 17948 var that = this, 17949 state = { storage: {}, count: 0 }, 17950 settings = extend({}, kendo.Template, that.options.templateSettings), 17951 tmpl = kendo.template(that._cellTmpl(column, state), settings); 17952 17953 if (state.count > 0) { 17954 tmpl = proxy(tmpl, state.storage); 17955 } 17956 17957 cell.empty().html(tmpl(dataItem)); 17958 }, 17959 17960 removeRow: function(row) { 17961 var that = this, 17962 model, 17963 mode; 17964 17965 if (!that._confirmation()) { 17966 return; 17967 } 17968 17969 row = $(row).hide(); 17970 model = that._modelForContainer(row); 17971 17972 if (model && !that.trigger(REMOVE, { row: row, model: model })) { 17973 that.dataSource.remove(model); 17974 17975 mode = that._editMode(); 17976 17977 if (mode === "inline" || mode === "popup") { 17978 that.dataSource.sync(); 17979 } 17980 } 17981 }, 17982 17983 _editMode: function() { 17984 var mode = "incell", 17985 editable = this.options.editable; 17986 17987 if (editable !== true) { 17988 mode = editable.mode || editable; 17989 } 17990 17991 return mode; 17992 }, 17993 17994 editRow: function(row) { 17995 var that = this, 17996 model = that._modelForContainer(row), 17997 container; 17998 17999 that.cancelRow(); 18000 18001 if (model) { 18002 18003 that._attachModelChange(model); 18004 18005 if (that._editMode() === "popup") { 18006 that._createPopupEditor(model); 18007 } else { 18008 that._createInlineEditor(row, model); 18009 } 18010 18011 container = that._editContainer; 18012 18013 container.delegate("a.k-grid-cancel", CLICK, function(e) { 18014 e.preventDefault(); 18015 that.cancelRow(); 18016 }); 18017 18018 container.delegate("a.k-grid-update", CLICK, function(e) { 18019 e.preventDefault(); 18020 that.saveRow(); 18021 }); 18022 } 18023 }, 18024 18025 _createPopupEditor: function(model) { 18026 var that = this, 18027 html = '<div ' + kendo.attr("uid") + '="' + model.uid + '"><div class="k-edit-form-container">', 18028 column, 18029 fields = [], 18030 idx, 18031 length, 18032 tmpl, 18033 editable = that.options.editable, 18034 options = isPlainObject(editable) ? editable.window : {}, 18035 settings = extend({}, kendo.Template, that.options.templateSettings); 18036 18037 if (editable.template) { 18038 html += (kendo.template(editable.template, settings))(model); 18039 } else { 18040 for (idx = 0, length = that.columns.length; idx < length; idx++) { 18041 column = that.columns[idx]; 18042 18043 if (!column.command) { 18044 html += '<div class="k-edit-label"><label for="' + column.field + '">' + (column.title || column.field || "") + '</label></div>'; 18045 18046 if ((!model.editable || model.editable(column.field)) && column.field) { 18047 fields.push({ field: column.field, format: column.format, editor: column.editor }); 18048 html += '<div ' + kendo.attr("container-for") + '="' + column.field + '" class="k-edit-field"></div>'; 18049 } else { 18050 var state = { storage: {}, count: 0 }; 18051 18052 tmpl = kendo.template(that._cellTmpl(column, state), settings); 18053 18054 if (state.count > 0) { 18055 tmpl = proxy(tmpl, state.storage); 18056 } 18057 18058 html += '<div class="k-edit-field">' + tmpl(model) + '</div>'; 18059 } 18060 } 18061 } 18062 } 18063 18064 html += that._createButton("update") + that._createButton("canceledit"); 18065 html += '</div></div>'; 18066 18067 var container = that._editContainer = $(html) 18068 .appendTo(that.wrapper) 18069 .kendoWindow(extend({ 18070 modal: true, 18071 resizable: false, 18072 draggable: true, 18073 title: "Edit", 18074 visible: false 18075 }, options)); 18076 18077 var wnd = container.data("kendoWindow"); 18078 18079 wnd.wrapper.delegate(".k-close", "click", function() { 18080 that.cancelRow(); 18081 }); 18082 18083 that.editable = that._editContainer 18084 .kendoEditable({ 18085 fields: fields, 18086 model: model, 18087 clearContainer: false 18088 }).data("kendoEditable"); 18089 18090 wnd.center().open(); 18091
18092 that.trigger(EDIT, { container: container, model: model }); 18093 }, 18094 18095 _createInlineEditor: function(row, model) { 18096 var that = this, 18097 column, 18098 cell, 18099 fields = []; 18100 18101 row.children(":not(.k-group-cell,.k-hierarchy-cell)").each(function() { 18102 cell = $(this); 18103 column = that.columns[that.cellIndex(cell)]; 18104 18105 if (!column.command && column.field && (!model.editable || model.editable(column.field))) { 18106 fields.push({ field: column.field, format: column.format, editor: column.editor }); 18107 cell.attr("data-container-for", column.field); 18108 cell.empty(); 18109 } else if (column.command && hasCommand(column.command, "edit")) { 18110 cell.empty(); 18111 $(that._createButton("update") + that._createButton("canceledit")).appendTo(cell); 18112 } 18113 }); 18114 18115 that._editContainer = row; 18116 18117 that.editable = row 18118 .addClass("k-grid-edit-row") 18119 .kendoEditable({ 18120 fields: fields, 18121 model: model, 18122 clearContainer: false 18123 }).data("kendoEditable"); 18124 18125 that.trigger(EDIT, { container: row, model: model }); 18126 }, 18127 18128 cancelRow: function() { 18129 var that = this, 18130 container = that._editContainer, 18131 model; 18132 18133 if (container) { 18134 model = that._modelForContainer(container); 18135 18136 that.dataSource.cancelChanges(model); 18137 18138 if (that._editMode() !== "popup") { 18139 that._displayRow(container); 18140 } else { 18141 that._displayRow(that.items().filter("[" + kendo.attr("uid") + "=" + model.uid + "]")); 18142 } 18143 18144 that._destroyEditable(); 18145 } 18146 }, 18147 18148 saveRow: function() { 18149 var that = this, 18150 container = that._editContainer, 18151 model = that._modelForContainer(container), 18152 editable = that.editable; 18153 18154 if (container && editable && editable.end() && 18155 !that.trigger(SAVE, { container: container, model: model } )) { 18156 18157 if (that._editMode() !== "popup") { 18158 that._displayRow(container); 18159 } 18160 18161 that._destroyEditable(); 18162 that.dataSource.sync(); 18163 } 18164 }, 18165 18166 _displayRow: function(row) { 18167 var that = this, 18168 model = that._modelForContainer(row); 18169 18170 if (model) { 18171 row.replaceWith($((row.hasClass("k-alt") ? that.altRowTemplate : that.rowTemplate)(model))); 18172 } 18173 }, 18174 18175 _showMessage: function(text) { 18176 return window.confirm(text); 18177 }, 18178 18179 _confirmation: function() { 18180 var that = this, 18181 editable = that.options.editable, 18182 confirmation = editable === true || typeof editable === STRING ? DELETECONFIRM : editable.confirmation; 18183 18184 return confirmation !== false && confirmation != null ? that._showMessage(confirmation) : true; 18185 }, 18186 18187 cancelChanges: function() { 18188 this.dataSource.cancelChanges(); 18189 }, 18190 18191 saveChanges: function() { 18192 var that = this; 18193 18194 if (((that.editable && that.editable.end()) || !that.editable) && !that.trigger(SAVECHANGES)) { 18195 that.dataSource.sync(); 18196 } 18197 }, 18198 18199 addRow: function() { 18200 var that = this, 18201 index, 18202 dataSource = that.dataSource; 18203 18204 if ((that.editable && that.editable.end()) || !that.editable) { 18205 index = dataSource.indexOf((dataSource.view() || [])[0]); 18206 if (index < 0) { 18207 index = 0; 18208 } 18209 18210 var model = dataSource.insert(index, {}), 18211 id = model.uid, 18212 mode = that._editMode(), 18213 row = that.table.find("tr[" + kendo.attr("uid") + "=" + id + "]"), 18214 cell = row.children("td:not(.k-group-cell,.k-hierarchy-cell)").first(); 18215 18216 if ((mode === "inline" || mode === "popup") && row.length) { 18217 that.editRow(row);
18218 } else if (cell.length) { 18219 that.editCell(cell); 18220 } 18221 } 18222 }, 18223 18224 _toolbar: function() { 18225 var that = this, 18226 wrapper = that.wrapper, 18227 toolbar = that.options.toolbar, 18228 template; 18229 18230 if (toolbar) { 18231 toolbar = isFunction(toolbar) ? toolbar({}) : (typeof toolbar === STRING ? toolbar : that._toolbarTmpl(toolbar).replace(templateHashRegExp, "\\#")); 18232 18233 template = proxy(kendo.template(toolbar), that); 18234 18235 $('<div class="k-toolbar k-grid-toolbar" />') 18236 .html(template({})) 18237 .prependTo(wrapper) 18238 .delegate(".k-grid-add", CLICK, function(e) { e.preventDefault(); that.addRow(); }) 18239 .delegate(".k-grid-cancel-changes", CLICK, function(e) { e.preventDefault(); that.cancelChanges(); }) 18240 .delegate(".k-grid-save-changes", CLICK, function(e) { e.preventDefault(); that.saveChanges(); }); 18241 } 18242 }, 18243 18244 _toolbarTmpl: function(commands) { 18245 var that = this, 18246 idx, 18247 length, 18248 html = ""; 18249 18250 if (isArray(commands)) { 18251 for (idx = 0, length = commands.length; idx < length; idx++) { 18252 html += that._createButton(commands[idx]); 18253 } 18254 } 18255 return html; 18256 }, 18257 18258 _createButton: function(command) { 18259 var template = command.template || COMMANDBUTTONTEMP, 18260 commandName = typeof command === STRING ? command : command.name, 18261 options = { className: "", text: commandName, imageClass: "", attr: "", iconClass: "" }; 18262 18263 if (isPlainObject(command)) { 18264 options = extend(true, options, defaultCommands[commandName], command); 18265 } else { 18266 options = extend(true, options, defaultCommands[commandName]); 18267 } 18268 18269 return kendo.template(template)(options); 18270 }, 18271 18272 _groupable: function() { 18273 var that = this, 18274 wrapper = that.wrapper, 18275 groupable = that.options.groupable; 18276 18277 if (!that.groupable) { 18278 that.table.delegate(".k-grouping-row .k-collapse, .k-grouping-row .k-expand", CLICK, function(e) { 18279 var element = $(this), 18280 group = element.closest("tr"); 18281 18282 if(element.hasClass('k-collapse')) { 18283 that.collapseGroup(group); 18284 } else { 18285 that.expandGroup(group); 18286 } 18287 e.preventDefault(); 18288 e.stopPropagation(); 18289 }); 18290 } 18291 18292 if (groupable) { 18293 if(!wrapper.has("div.k-grouping-header")[0]) { 18294 $("<div />").addClass("k-grouping-header").html(" ").prependTo(wrapper); 18295 } 18296 18297 if (that.groupable) { 18298 that.groupable.destroy(); 18299 } 18300 18301 that.groupable = new Groupable(wrapper, { 18302 draggable: that._draggableInstance, 18303 groupContainer: "div.k-grouping-header", 18304 dataSource: that.dataSource, 18305 allowDrag: that.options.reorderable 18306 }); 18307 } 18308 }, 18309 18310 _selectable: function() { 18311 var that = this, 18312 multi, 18313 cell, 18314 selectable = that.options.selectable; 18315 18316 if (selectable) { 18317 multi = typeof selectable === STRING && selectable.toLowerCase().indexOf("multiple") > -1; 18318 cell = typeof selectable === STRING && selectable.toLowerCase().indexOf("cell") > -1; 18319 18320 that.selectable = new kendo.ui.Selectable(that.table, { 18321 filter: ">" + (cell ? SELECTION_CELL_SELECTOR : "tbody>tr:not(.k-grouping-row,.k-detail-row,.k-group-footer)"), 18322 multiple: multi, 18323 change: function() { 18324 that.trigger(CHANGE); 18325 } 18326 }); 18327 18328 if (that.options.navigatable) { 18329 that.wrapper.keydown(function(e) { 18330 var current = that.current(); 18331 if (e.keyCode === keys.SPACEBAR && e.target == that.wrapper[0] && !current.hasClass("k-edit-cell")) { 18332 e.preventDefault(); 18333 e.stopPropagation(); 18334 current = cell ? current : current.parent(); 18335 18336 if(multi) { 18337 if(!e.ctrlKey) { 18338 that.selectable.clear(); 18339 } else { 18340 if(current.hasClass(SELECTED)) { 18341 current.removeClass(SELECTED); 18342 current = null; 18343 } 18344 } 18345 } else { 18346 that.selectable.clear(); 18347 } 18348 18349 that.selectable.value(current); 18350 } 18351 }); 18352 } 18353 } 18354 }, 18355 18356 clearSelection: function() { 18357 var that = this; 18358 that.selectable.clear(); 18359 that.trigger(CHANGE); 18360 }, 18361 18362 select: function(items) { 18363 var that = this, 18364 selectable = that.selectable; 18365 18366 items = $(items); 18367 if(items.length) { 18368 if(!selectable.options.multiple) { 18369 selectable.clear(); 18370 items = items.first(); 18371 } 18372 selectable.value(items); 18373 return; 18374 } 18375 18376 return selectable.value(); 18377 }, 18378 18379 current: function(element) { 18380 var that = this, 18381 scrollable = that.options.scrollable, 18382 current = that._current; 18383 18384 if (element !== undefined && element.length) { 18385 if (!current || current[0] !== element[0]) { 18386 element.addClass(FOCUSED); 18387 if (current) { 18388 current.removeClass(FOCUSED); 18389 } 18390 that._current = element; 18391 18392 if(element.length && scrollable) { 18393 that._scrollTo(element.parent()[0], that.content[0]); 18394 if (scrollable.virtual) { 18395 that._scrollTo(element[0], that.content.find(">.k-virtual-scrollable-wrap")[0]); 18396 } else { 18397 that._scrollTo(element[0], that.content[0]); 18398 } 18399 } 18400 } 18401 } 18402 18403 return that._current; 18404 }, 18405 18406 _scrollTo: function(element, container) { 18407 var isHorizontal = element.tagName.toLowerCase() === "td", 18408 elementOffset = element[isHorizontal ? "offsetLeft" : "offsetTop"], 18409 elementOffsetDir = element[isHorizontal ? "offsetWidth" : "offsetHeight"], 18410 containerScroll = container[isHorizontal ? "scrollLeft" : "scrollTop"], 18411 containerOffsetDir = container[isHorizontal ? "clientWidth" : "clientHeight"], 18412 bottomDistance = elementOffset + elementOffsetDir; 18413 18414 container[isHorizontal ? "scrollLeft" : "scrollTop"] = containerScroll > elementOffset ? 18415 elementOffset : 18416 (bottomDistance > (containerScroll + containerOffsetDir) ? 18417 (bottomDistance - containerOffsetDir) : containerScroll); 18418 }, 18419 18420 _navigatable: function() { 18421 var that = this, 18422 wrapper = that.wrapper, 18423 table = that.table.addClass(FOCUSABLE), 18424 currentProxy = proxy(that.current, that), 18425 selector = "." + FOCUSABLE + " " + CELL_SELECTOR, 18426 browser = $.browser,
18427 clickCallback = function(e) { 18428 var currentTarget = $(e.currentTarget); 18429 if (currentTarget.closest("tbody")[0] !== that.tbody[0]) { 18430 return; 18431 } 18432 18433 currentProxy(currentTarget); 18434 if(!$(e.target).is(":button,a,:input,a>.k-icon,textarea,span.k-icon,.k-input")) { 18435 setTimeout(function() { wrapper.focus(); } ); 18436 } 18437 18438 e.stopPropagation(); 18439 }; 18440 18441 if (that.options.navigatable) { 18442 wrapper.bind({ 18443 focus: function() { 18444 var current = that._current; 18445 if(current && current.is(":visible")) { 18446 current.addClass(FOCUSED); 18447 } else { 18448 currentProxy(that.table.find(FIRST_CELL_SELECTOR)); 18449 } 18450 }, 18451 focusout: function(e) { 18452 if (that._current) { 18453 that._current.removeClass(FOCUSED); 18454 } 18455 e.stopPropagation(); 18456 }, 18457 keydown: function(e) { 18458 var key = e.keyCode,
18459 current = that.current(), 18460 shiftKey = e.shiftKey, 18461 dataSource = that.dataSource, 18462 pageable = that.options.pageable, 18463 canHandle = !$(e.target).is(":button,a,:input,a>.t-icon"), 18464 isInCell = that._editMode() == "incell", 18465 currentIndex, 18466 cell, 18467 handled = false; 18468 18469 if (canHandle && keys.UP === key) { 18470 currentProxy(current ? current.parent().prevAll(ROW_SELECTOR).last().children(":eq(" + current.index() + "),:eq(0)").last() : table.find(FIRST_CELL_SELECTOR)); 18471 handled = true; 18472 } else if (canHandle && keys.DOWN === key) { 18473 currentProxy(current ? current.parent().nextAll(ROW_SELECTOR).first().children(":eq(" + current.index() + "),:eq(0)").last() : table.find(FIRST_CELL_SELECTOR)); 18474 handled = true; 18475 } else if (canHandle && keys.LEFT === key) { 18476 currentProxy(current ? current.prevAll(DATA_CELL + ":first") : table.find(FIRST_CELL_SELECTOR)); 18477 handled = true; 18478 } else if (canHandle && keys.RIGHT === key) { 18479 currentProxy(current ? current.nextAll(":visible:first") : table.find(FIRST_CELL_SELECTOR)); 18480 handled = true; 18481 } else if (canHandle && pageable && keys.PAGEDOWN == key) { 18482 that._current = null; 18483 dataSource.page(dataSource.page() + 1); 18484 handled = true; 18485 } else if (canHandle && pageable && keys.PAGEUP == key) { 18486 that._current = null; 18487 dataSource.page(dataSource.page() - 1); 18488 handled = true; 18489 } else if (that.options.editable) { 18490 current = current ? current : table.find(FIRST_CELL_SELECTOR); 18491 if (keys.ENTER == key || keys.F12 == key) { 18492 that._handleEditing(current); 18493 handled = true; 18494 } else if (keys.TAB == key && isInCell) { 18495 cell = shiftKey ? current.prevAll(DATA_CELL + ":first") : current.nextAll(":visible:first"); 18496 if (!cell.length) { 18497 cell = current.parent()[shiftKey ? "prevAll" : "nextAll"]("tr:not(.k-grouping-row,.k-detail-row):visible") 18498 .children(DATA_CELL + (shiftKey ? ":last" : ":first")); 18499 } 18500 18501 if (cell.length) { 18502 that._handleEditing(current, cell); 18503 handled = true; 18504 } 18505 } else if (keys.ESC == key && that._editContainer) { 18506 if (that._editContainer.has(current[0]) || current[0] === that._editContainer[0]) { 18507 if (isInCell) { 18508 that.closeCell(); 18509 } else { 18510 currentIndex = that.items().index(current.parent()); 18511 document.activeElement.blur(); 18512 that.cancelRow(); 18513 if (currentIndex >= 0) { 18514 that.current(that.items().eq(currentIndex).children().filter(DATA_CELL).first()); 18515 } 18516 } 18517 18518 if (browser.msie && parseInt(browser.version, 10) < 9) { 18519 document.body.focus(); 18520 }
18521 wrapper.focus(); 18522 handled = true; 18523 } 18524 } 18525 } 18526 18527 if(handled) { 18528 e.preventDefault(); 18529 e.stopPropagation(); 18530 } 18531 } 18532 }); 18533 18534 wrapper.delegate(selector, "mousedown", clickCallback); } 18535 }, 18536 18537 _handleEditing: function(current, next) { 18538 var that = this, 18539 mode = that._editMode(), 18540 editContainer = that._editContainer, 18541 idx, 18542 isEdited; 18543 18544 if (mode == "incell") { 18545 isEdited = current.hasClass("k-edit-cell"); 18546 } else { 18547 isEdited = current.parent().hasClass("k-grid-edit-row"); 18548 } 18549 18550 if (that.editable) { 18551 if ($.contains(editContainer[0], document.activeElement)) { 18552 $(document.activeElement).blur(); 18553 } 18554 18555 if (that.editable.end()) { 18556 if (mode == "incell") { 18557 that.closeCell(); 18558 } else { 18559 if (current.parent()[0] === editContainer[0]) { 18560 idx = that.items().index(current.parent()); 18561 } else { 18562 idx = that.items().index(editContainer); 18563 } 18564 that.saveRow(); 18565 that.current(that.items().eq(idx).children().filter(DATA_CELL).first()); 18566 isEdited = true; 18567 } 18568 } else { 18569 if (mode == "incell") { 18570 that.current(editContainer); 18571 } else { 18572 that.current(editContainer.children().filter(DATA_CELL).first()); 18573 } 18574 editContainer.find(":input:visible:first").focus(); 18575 return; 18576 } 18577 } 18578 18579 if (next) { 18580 that.current(next); 18581 } 18582 18583 that.wrapper.focus(); 18584 if ((!isEdited && !next) || next) { 18585 if (mode == "incell") { 18586 that.editCell(that.current()); 18587 } else { 18588 that.editRow(that.current().parent()); 18589 } 18590 } 18591 }, 18592 18593 _wrapper: function() { 18594 var that = this, 18595 table = that.table, 18596 height = that.options.height, 18597 wrapper = that.element; 18598 18599 if (!wrapper.is("div")) { 18600 wrapper = wrapper.wrap("<div/>").parent(); 18601 } 18602 18603 that.wrapper = wrapper.addClass("k-grid k-widget") 18604 .attr(TABINDEX, math.max(table.attr(TABINDEX) || 0, 0)); 18605 18606 table.removeAttr(TABINDEX); 18607 18608 if (height) { 18609 that.wrapper.css(HEIGHT, height); 18610 table.css(HEIGHT, "auto"); 18611 } 18612 }, 18613 18614 _tbody: function() { 18615 var that = this, 18616 table = that.table, 18617 tbody; 18618 18619 tbody = table.find(">tbody"); 18620 18621 if (!tbody.length) { 18622 tbody = $("<tbody/>").appendTo(table); 18623 } 18624 18625 that.tbody = tbody; 18626 }, 18627 18628 _scrollable: function() { 18629 var that = this, 18630 header, 18631 table, 18632 options = that.options, 18633 scrollable = options.scrollable, 18634 scrollbar = kendo.support.scrollbar(); 18635 18636 if (scrollable) { 18637 header = that.wrapper.children(".k-grid-header"); 18638 18639 if (!header[0]) { 18640 header = $('<div class="k-grid-header" />').insertBefore(that.table); 18641 } 18642 18643 // workaround for IE issue where scroll is not raised if container is same width as the scrollbar 18644 header.css("padding-right", scrollable.virtual ? scrollbar + 1 : scrollbar); 18645 table = $('<table cellspacing="0" />'); 18646 table.append(that.thead); 18647 header.empty().append($('<div class="k-grid-header-wrap" />').append(table)); 18648 18649 that.content = that.table.parent(); 18650 18651 if (that.content.is(".k-virtual-scrollable-wrap")) { 18652 that.content = that.content.parent(); 18653 } 18654 18655 if (!that.content.is(".k-grid-content, .k-virtual-scrollable-wrap")) {
18656 that.content = that.table.wrap('<div class="k-grid-content" />').parent(); 18657 18658 if (scrollable !== true && scrollable.virtual) { 18659 that.virtualScrollable = new VirtualScrollable(that.content, { 18660 dataSource: that.dataSource, 18661 itemHeight: proxy(that._averageRowHeight, that) 18662 }); 18663 } 18664 } 18665 18666 that.scrollables = header.children(".k-grid-header-wrap"); // the footer does not exists here yet! 18667 18668 if (scrollable.virtual) { 18669 that.content.find(">.k-virtual-scrollable-wrap").bind('scroll', function () { 18670 that.scrollables.scrollLeft(this.scrollLeft); 18671 }); 18672 } else { 18673 that.content.bind('scroll', function () { 18674 that.scrollables.scrollLeft(this.scrollLeft); 18675 }); 18676 18677 var touchScroller = kendo.touchScroller(that.content); 18678 if (touchScroller && touchScroller.movable) { 18679 touchScroller.movable.bind("change", function(e) { 18680 that.scrollables.scrollLeft(-e.sender.x); 18681 }); 18682 } 18683 } 18684 } 18685 }, 18686 18687 _setContentHeight: function() { 18688 var that = this, 18689 options = that.options, 18690 height = that.wrapper.innerHeight(), 18691 header = that.wrapper.children(".k-grid-header"), 18692 scrollbar = kendo.support.scrollbar(); 18693 18694 if (options.scrollable) { 18695 18696 height -= header.outerHeight(); 18697 18698 if (that.pager) { 18699 height -= that.pager.element.outerHeight(); 18700 } 18701 18702 if(options.groupable) { 18703 height -= that.wrapper.children(".k-grouping-header").outerHeight(); 18704 } 18705 18706 if(options.toolbar) { 18707 height -= that.wrapper.children(".k-grid-toolbar").outerHeight(); 18708 } 18709 18710 if (that.footerTemplate) { 18711 height -= that.wrapper.children(".k-grid-footer").outerHeight(); 18712 } 18713 18714 var isGridHeightSet = function(el) { 18715 var initialHeight, newHeight; 18716 if (el[0].style.height) { 18717 return true; 18718 } else { 18719 initialHeight = el.height(); 18720 } 18721 18722 el.height("auto"); 18723 newHeight = el.height(); 18724 18725 if (initialHeight != newHeight) { 18726 el.height(""); 18727 return true; 18728 } 18729 el.height(""); 18730 return false; 18731 }; 18732 18733 if (isGridHeightSet(that.wrapper)) { // set content height only if needed 18734 if (height > scrollbar * 2) { // do not set height if proper scrollbar cannot be displayed 18735 that.content.height(height); 18736 } else { 18737 that.content.height(scrollbar * 2 + 1); 18738 } 18739 } 18740 } 18741 }, 18742 18743 _averageRowHeight: function() { 18744 var that = this, 18745 rowHeight = that._rowHeight; 18746 18747 if (!that._rowHeight) { 18748 that._rowHeight = rowHeight = that.table.outerHeight() / that.table[0].rows.length; 18749 that._sum = rowHeight; 18750 that._measures = 1; 18751 } 18752 18753 var currentRowHeight = that.table.outerHeight() / that.table[0].rows.length; 18754 18755 if (rowHeight !== currentRowHeight) { 18756 that._measures ++; 18757 that._sum += currentRowHeight; 18758 that._rowHeight = that._sum / that._measures; 18759 } 18760 return rowHeight; 18761 }, 18762 18763 _dataSource: function() { 18764 var that = this, 18765 options = that.options, 18766 pageable, 18767 dataSource = options.dataSource; 18768 18769 dataSource = isArray(dataSource) ? { data: dataSource } : dataSource; 18770 18771 if (isPlainObject(dataSource)) { 18772 extend(dataSource, { table: that.table, fields: that.columns }); 18773 18774 pageable = options.pageable; 18775 18776 if (isPlainObject(pageable) && pageable.pageSize !== undefined) { 18777 dataSource.pageSize = pageable.pageSize; 18778 } 18779 } 18780 18781 if (that.dataSource && that._refreshHandler) { 18782 that.dataSource.unbind(CHANGE, that._refreshHandler) 18783 .unbind(REQUESTSTART, that._requestStartHandler) 18784 .unbind(ERROR, that._errorHandler); 18785 } else { 18786 that._refreshHandler = proxy(that.refresh, that); 18787 that._requestStartHandler = proxy(that._requestStart, that); 18788 that._errorHandler = proxy(that._error, that); 18789 } 18790 18791 that.dataSource = DataSource.create(dataSource) 18792 .bind(CHANGE, that._refreshHandler) 18793 .bind(REQUESTSTART, that._requestStartHandler) 18794 .bind(ERROR, that._errorHandler); 18795 }, 18796 18797 _error: function() { 18798 this._progress(false); 18799 }, 18800 18801 _requestStart: function() { 18802 this._progress(true); 18803 }, 18804 18805 _modelChange: function(e) { 18806 var that = this, 18807 model = e.model, 18808 row = that.tbody.find("tr[" + kendo.attr("uid") + "=" + model.uid +"]"), 18809 cell, 18810 column, 18811 isAlt = row.hasClass("k-alt"), 18812 tmp, 18813 idx, 18814 length; 18815
18816 if (row.children(".k-edit-cell").length && !that.options.rowTemplate) { 18817 row.children(":not(.k-group-cell,.k-hierarchy-cell)").each(function() { 18818 cell = $(this); 18819 column = that.columns[that.cellIndex(cell)]; 18820 18821 if (column.field === e.field) { 18822 if (!cell.hasClass("k-edit-cell")) { 18823 that._displayCell(cell, column, model); 18824 $('<span class="k-dirty"/>').prependTo(cell); 18825 } else { 18826 cell.addClass("k-dirty-cell"); 18827 } 18828 } 18829 }); 18830 18831 } else if (!row.hasClass("k-grid-edit-row")) { 18832 tmp = $((isAlt ? that.altRowTemplate : that.rowTemplate)(model)); 18833 18834 row.replaceWith(tmp); 18835 18836 for (idx = 0, length = that.columns.length; idx < length; idx++) { 18837 column = that.columns[idx]; 18838 18839 if (column.field === e.field) { 18840 cell = tmp.children(":not(.k-group-cell,.k-hierarchy-cell)").eq(idx); 18841 $('<span class="k-dirty"/>').prependTo(cell); 18842 } 18843 } 18844 that.trigger("itemChange", { item: tmp, data: model, ns: ui }); 18845 } 18846 18847 }, 18848 18849 _pageable: function() { 18850 var that = this, 18851 wrapper, 18852 pageable = that.options.pageable; 18853 18854 if (pageable) { 18855 wrapper = that.wrapper.children("div.k-grid-pager").empty(); 18856 18857 if (!wrapper.length) { 18858 wrapper = $('<div class="k-pager-wrap k-grid-pager"/>').appendTo(that.wrapper); 18859 } 18860 18861 if (that.pager) { 18862 that.pager.destroy(); 18863 } 18864 18865 if (typeof pageable === "object" && pageable instanceof kendo.ui.Pager) { 18866 that.pager = pageable; 18867 } else { 18868 that.pager = new kendo.ui.Pager(wrapper, extend({}, pageable, { dataSource: that.dataSource })); 18869 } 18870 } 18871 }, 18872 18873 _footer: function() { 18874 var that = this, 18875 aggregates = that.dataSource.aggregates(), 18876 html = "", 18877 footerTemplate = that.footerTemplate, 18878 options = that.options; 18879 18880 if (footerTemplate) { 18881 aggregates = !isEmptyObject(aggregates) ? aggregates : buildEmptyAggregatesObject(that.dataSource.aggregate()); 18882 18883 html = $(that._wrapFooter(footerTemplate(aggregates))); 18884 18885 if (that.footer) { 18886 var tmp = html; 18887 18888 that.footer.replaceWith(tmp); 18889 that.footer = tmp; 18890 } else { 18891 if (options.scrollable) { 18892 that.footer = options.pageable ? html.insertBefore(that.wrapper.children("div.k-grid-pager")) : html.appendTo(that.wrapper); 18893 that.scrollables = that.scrollables.add(that.footer.children(".k-grid-footer-wrap")); 18894 } else { 18895 that.footer = html.insertBefore(that.tbody); 18896 } 18897 } 18898
18899 if (options.resizable && that._footerWidth) { 18900 that.footer.find("table").css('width', that._footerWidth); 18901 } 18902 18903 that._setContentHeight(); 18904 } 18905 }, 18906 18907 _wrapFooter: function(footerRow) { 18908 var that = this, 18909 html = ""; 18910 18911 if (that.options.scrollable) { 18912 html = $('<div class="k-grid-footer"><div class="k-grid-footer-wrap"><table cellspacing="0"><tbody>' + footerRow + '</tbody></table></div></div>'); 18913 that._appendCols(html.find("table")); 18914 return html; 18915 } 18916 18917 return '<tfoot class="k-grid-footer">' + footerRow + '</tfoot>'; 18918 }, 18919 18920 _filterable: function() { 18921 var that = this, 18922 columns = that.columns, 18923 filterable = that.options.filterable; 18924 18925 if (filterable) { 18926 that.thead 18927 .find("th:not(.k-hierarchy-cell)") 18928 .each(function(index) { 18929 if (columns[index].filterable !== false) { 18930 $(this).kendoFilterMenu(extend(true, {}, filterable, columns[index].filterable, { dataSource: that.dataSource })); 18931 } 18932 }); 18933 } 18934 }, 18935 18936 _sortable: function() { 18937 var that = this, 18938 columns = that.columns, 18939 column, 18940 sortable = that.options.sortable; 18941 18942 if (sortable) { 18943 that.thead 18944 .find("th:not(.k-hierarchy-cell)") 18945 .each(function(index) { 18946 column = columns[index]; 18947 if (column.sortable !== false && !column.command) { 18948 $(this).kendoSortable(extend({}, sortable, { dataSource: that.dataSource })); 18949 } 18950 }); 18951 } 18952 }, 18953 18954 _columns: function(columns) { 18955 var that = this, 18956 table = that.table, 18957 encoded, 18958 cols = table.find("col"), 18959 dataSource = that.options.dataSource; 18960 18961 // using HTML5 data attributes as a configuration option e.g. <th data-field="foo">Foo</foo> 18962 columns = columns.length ? columns : map(table.find("th"), function(th, idx) { 18963 th = $(th); 18964 var sortable = th.attr(kendo.attr("sortable")), 18965 filterable = th.attr(kendo.attr("filterable")), 18966 type = th.attr(kendo.attr("type")), 18967 groupable = th.attr(kendo.attr("groupable")), 18968 field = th.attr(kendo.attr("field")); 18969 18970 if (!field) { 18971 field = th.text().replace(/\s|[^A-z0-9]/g, ""); 18972 } 18973 18974 return { 18975 field: field, 18976 type: type, 18977 sortable: sortable !== "false", 18978 filterable: filterable !== "false", 18979 groupable: groupable !== "false", 18980 template: th.attr(kendo.attr("template")), 18981 width: cols.eq(idx).css("width") 18982 }; 18983 }); 18984 18985 encoded = !(that.table.find("tbody tr").length > 0 && (!dataSource || !dataSource.transport)); 18986 18987 that.columns = map(columns, function(column) { 18988 column = typeof column === STRING ? { field: column } : column; 18989 return extend({ encoded: encoded }, column); 18990 }); 18991 }, 18992 18993 _tmpl: function(rowTemplate, alt) { 18994 var that = this, 18995 settings = extend({}, kendo.Template, that.options.templateSettings), 18996 idx, 18997 length = that.columns.length, 18998 template, 18999 state = { storage: {}, count: 0 }, 19000 column, 19001 type, 19002 hasDetails = that._hasDetails(), 19003 className = [], 19004 groups = that.dataSource.group().length; 19005 19006 if (!rowTemplate) { 19007 rowTemplate = "<tr"; 19008 19009 if (alt) { 19010 className.push("k-alt"); 19011 } 19012
19013 if (hasDetails) { 19014 className.push("k-master-row"); 19015 } 19016 19017 if (className.length) { 19018 rowTemplate += ' class="' + className.join(" ") + '"'; 19019 } 19020 19021 if (length) { // data item is an object 19022 rowTemplate += ' ' + kendo.attr("uid") + '="#=uid#"'; 19023 } 19024 19025 rowTemplate += ">"; 19026 19027 if (groups > 0) { 19028 rowTemplate += groupCells(groups); 19029 } 19030 19031 if (hasDetails) { 19032 rowTemplate += '<td class="k-hierarchy-cell"><a class="k-icon k-plus" href="\\#"></a></td>'; 19033 } 19034 19035 for (idx = 0; idx < length; idx++) { 19036 column = that.columns[idx]; 19037 template = column.template; 19038 type = typeof template; 19039 19040 rowTemplate += "<td>"; 19041 rowTemplate += that._cellTmpl(column, state); 19042 19043 rowTemplate += "</td>"; 19044 } 19045 19046 rowTemplate += "</tr>"; 19047 } 19048 19049 rowTemplate = kendo.template(rowTemplate, settings); 19050 19051 if (state.count > 0) { 19052 return proxy(rowTemplate, state.storage); 19053 } 19054 19055 return rowTemplate; 19056 }, 19057 19058 _cellTmpl: function(column, state) { 19059 var that = this, 19060 settings = extend({}, kendo.Template, that.options.templateSettings), 19061 template = column.template, 19062 paramName = settings.paramName, 19063 html = "", 19064 idx, 19065 length, 19066 format = column.format, 19067 type = typeof template; 19068 19069 if (column.command) { 19070 if (isArray(column.command)) { 19071 for (idx = 0, length = column.command.length; idx < length; idx++) { 19072 html += that._createButton(column.command[idx]); 19073 } 19074 return html.replace(templateHashRegExp, "\\#"); 19075 } 19076 return that._createButton(column.command).replace(templateHashRegExp, "\\#"); 19077 } 19078 19079 if (type === FUNCTION) { 19080 state.storage["tmpl" + state.count] = template; 19081 html += "#=this.tmpl" + state.count + "(" + paramName + ")#"; 19082 state.count ++; 19083 } else if (type === STRING) { 19084 html += template; 19085 } else { 19086 html += column.encoded ? "${" : "#="; 19087 19088 if (format) { 19089 html += 'kendo.format(\"' + format.replace(formatRegExp,"\\}") + '\",'; 19090 } 19091 19092 if (!settings.useWithBlock) { 19093 html += paramName + "."; 19094 } 19095 19096 html += column.field; 19097 19098 if (format) { 19099 html += ")"; 19100 } 19101 19102 html += column.encoded ? "}" : "#"; 19103 } 19104 return html; 19105 }, 19106 19107 _templates: function() { 19108 var that = this, 19109 options = that.options, 19110 dataSource = that.dataSource, 19111 groups = dataSource.group(), 19112 aggregates = dataSource.aggregate(); 19113 19114 that.rowTemplate = that._tmpl(options.rowTemplate); 19115 that.altRowTemplate = that._tmpl(options.altRowTemplate || options.rowTemplate, true); 19116 19117 if (that._hasDetails()) { 19118 that.detailTemplate = that._detailTmpl(options.detailTemplate || ""); 19119 } 19120 19121 if (!isEmptyObject(aggregates) || 19122 $.grep(that.columns, function(column) { return column.footerTemplate; }).length) { 19123 19124 that.footerTemplate = that._footerTmpl(aggregates, "footerTemplate", "k-footer-template"); 19125 } 19126 19127 if (groups.length && $.grep(that.columns, function(column) { return column.groupFooterTemplate; }).length) { 19128 aggregates = $.map(groups, function(g) { return g.aggregates; }); 19129 that.groupFooterTemplate = that._footerTmpl(aggregates, "groupFooterTemplate", "k-group-footer"); 19130 } 19131 }, 19132 19133 _footerTmpl: function(aggregates, templateName, rowClass) { 19134 var that = this, 19135 settings = extend({}, kendo.Template, that.options.templateSettings), 19136 paramName = settings.paramName, 19137 html = "", 19138 idx, 19139 length, 19140 columns = that.columns, 19141 template, 19142 type, 19143 storage = {}, 19144 count = 0, 19145 scope = {}, 19146 dataSource = that.dataSource, 19147 groups = dataSource.group().length, 19148 fieldsMap = buildEmptyAggregatesObject(aggregates), 19149 column; 19150 19151 html += '<tr class="' + rowClass + '">'; 19152 19153 if (groups > 0) { 19154 html += groupCells(groups); 19155 } 19156
19157 if (that._hasDetails()) { 19158 html += '<td class="k-hierarchy-cell"> </td>'; 19159 } 19160 19161 for (idx = 0, length = that.columns.length; idx < length; idx++) { 19162 column = columns[idx]; 19163 template = column[templateName]; 19164 type = typeof template; 19165 19166 html += "<td>"; 19167 19168 if (template) { 19169 if (type !== FUNCTION) { 19170 scope = fieldsMap[column.field] ? extend({}, settings, { paramName: paramName + "." + column.field }) : {}; 19171 template = kendo.template(template, scope); 19172 } 19173 19174 storage["tmpl" + count] = template; 19175 html += "#=this.tmpl" + count + "(" + paramName + ")#"; 19176 count ++; 19177 } else { 19178 html += " "; 19179 } 19180 19181 html += "</td>"; 19182 } 19183 19184 html += '</tr>'; 19185 19186 html = kendo.template(html, settings); 19187 19188 if (count > 0) { 19189 return proxy(html, storage); 19190 } 19191 19192 return html; 19193 }, 19194 19195 _detailTmpl: function(template) { 19196 var that = this, 19197 html = "", 19198 settings = extend({}, kendo.Template, that.options.templateSettings), 19199 paramName = settings.paramName, 19200 templateFunctionStorage = {}, 19201 templateFunctionCount = 0, 19202 groups = that.dataSource.group().length, 19203 columns = that.columns.length, 19204 type = typeof template; 19205 19206 html += '<tr class="k-detail-row">'; 19207 if (groups > 0) { 19208 html += groupCells(groups); 19209 } 19210 html += '<td class="k-hierarchy-cell"></td><td class="k-detail-cell"' + (columns ? ' colspan="' + columns + '"' : '') + ">"; 19211 19212 if (type === FUNCTION) { 19213 templateFunctionStorage["tmpl" + templateFunctionCount] = template; 19214 html += "#=this.tmpl" + templateFunctionCount + "(" + paramName + ")#"; 19215 templateFunctionCount ++; 19216 } else { 19217 html += template; 19218 } 19219 19220 html += "</td></tr>"; 19221 19222 html = kendo.template(html, settings); 19223 19224 if (templateFunctionCount > 0) { 19225 return proxy(html, templateFunctionStorage); 19226 } 19227 19228 return html; 19229 }, 19230 19231 _hasDetails: function() { 19232 var that = this; 19233 19234 return that.options.detailTemplate !== undefined || (that._events[DETAILINIT] || []).length; 19235 }, 19236 19237 _details: function() { 19238 var that = this; 19239 19240 that.table.delegate(".k-hierarchy-cell .k-plus, .k-hierarchy-cell .k-minus", CLICK, function(e) { 19241 var button = $(this), 19242 expanding = button.hasClass("k-plus"), 19243 masterRow = button.closest("tr.k-master-row"), 19244 detailRow, 19245 detailTemplate = that.detailTemplate, 19246 data, 19247 hasDetails = that._hasDetails(); 19248 19249 button.toggleClass("k-plus", !expanding) 19250 .toggleClass("k-minus", expanding); 19251 19252 if(hasDetails && !masterRow.next().hasClass("k-detail-row")) { 19253 data = that.dataItem(masterRow), 19254 $(detailTemplate(data)).insertAfter(masterRow); 19255 19256 that.trigger(DETAILINIT, { masterRow: masterRow, detailRow: masterRow.next(), data: data, detailCell: masterRow.next().find(".k-detail-cell") }); 19257 } 19258 19259 detailRow = masterRow.next(); 19260 19261 that.trigger(expanding ? DETAILEXPAND : DETAILCOLLAPSE, { masterRow: masterRow, detailRow: detailRow}); 19262 detailRow.toggle(expanding); 19263 19264 e.preventDefault(); 19265 return false; 19266 }); 19267 }, 19268 19269 dataItem: function(tr) { 19270 return this._data[this.tbody.find('> tr:not(.k-grouping-row,.k-detail-row,.k-group-footer)').index($(tr))]; 19271 }, 19272 19273 expandRow: function(tr) { 19274 $(tr).find('> td .k-plus, > td .k-expand').click(); 19275 }, 19276 19277 collapseRow: function(tr) { 19278 $(tr).find('> td .k-minus, > td .k-collapse').click(); 19279 }, 19280 19281 _thead: function() { 19282 var that = this, 19283 columns = that.columns,
19284 hasDetails = that._hasDetails() && columns.length, 19285 idx, 19286 length, 19287 html = "", 19288 thead = that.table.find("thead"), 19289 tr, 19290 th; 19291 19292 if (!thead.length) { 19293 thead = $("<thead/>").insertBefore(that.tbody); 19294 } 19295 19296 tr = that.table.find("tr").filter(":has(th)"); 19297 19298 if (!tr.length) { 19299 tr = thead.children().first(); 19300 if(!tr.length) { 19301 tr = $("<tr/>"); 19302 } 19303 } 19304 19305 if (!tr.children().length) { 19306 if (hasDetails) { 19307 html += '<th class="k-hierarchy-cell"> </th>'; 19308 } 19309 19310 for (idx = 0, length = columns.length; idx < length; idx++) { 19311 th = columns[idx]; 19312 19313 if (!th.command) { 19314 html += "<th " + kendo.attr("field") + "='" + th.field + "' "; 19315 if (th.title) { 19316 html += kendo.attr("title") + '="' + th.title.replace(/'/g, "\'") + '" '; 19317 } 19318 19319 if (th.groupable !== undefined) { 19320 html += kendo.attr("groupable") + "='" + th.groupable + "' "; 19321 } 19322 19323 if (th.aggregates) { 19324 html += kendo.attr("aggregates") + "='" + th.aggregates + "'"; 19325 } 19326 19327 html += ">" + (th.title || th.field || "") + "</th>"; 19328 } else { 19329 html += "<th>" + (th.title || "") + "</th>"; 19330 } 19331 } 19332 19333 tr.html(html); 19334 } else if (hasDetails) { 19335 tr.prepend('<th class="k-hierarchy-cell"> </th>'); 19336 } 19337 19338 tr.find("th").addClass("k-header"); 19339 19340 if(!that.options.scrollable) { 19341 thead.addClass("k-grid-header"); 19342 } 19343 19344 tr.appendTo(thead); 19345 19346 that.thead = thead; 19347 19348 that._sortable(); 19349 19350 that._filterable(); 19351 19352 that._scrollable(); 19353 19354 that._updateCols(); 19355 19356 that._setContentHeight(); 19357 19358 that._resizable(); 19359 19360 that._draggable(); 19361 19362 that._reorderable(); 19363 }, 19364 19365 _updateCols: function() { 19366 var that = this; 19367 19368 that._appendCols(that.thead.parent().add(that.table)); 19369 }, 19370 19371 _appendCols: function(table) { 19372 var that = this, 19373 colgroup = table.find("colgroup"), 19374 width, 19375 cols = map(that.columns, function(column) { 19376 width = column.width; 19377 if (width && parseInt(width, 10) !== 0) { 19378 return kendo.format('<col style="width:{0}"/>', typeof width === STRING? width : width + "px"); 19379 } 19380 19381 return "<col />"; 19382 }), 19383 groups = that.dataSource.group().length; 19384 19385 if (that._hasDetails()) { 19386 cols.splice(0, 0, '<col class="k-hierarchy-col" />'); 19387 } 19388 19389 if (colgroup.length) { 19390 colgroup.remove(); 19391 } 19392 19393 colgroup = $("<colgroup/>").append($(new Array(groups + 1).join('<col class="k-group-col">') + cols.join(""))); 19394 19395 table.prepend(colgroup); 19396 }, 19397 19398 _autoColumns: function(schema) { 19399 if (schema && schema.toJSON) { 19400 var that = this, 19401 field; 19402 19403 schema = schema.toJSON(); 19404 19405 for (field in schema) { 19406 that.columns.push({ field: field }); 19407 } 19408 19409 that._thead(); 19410 19411 that._templates(); 19412 } 19413 }, 19414 19415 _rowsHtml: function(data) { 19416 var that = this, 19417 html = "", 19418 idx, 19419 length, 19420 rowTemplate = that.rowTemplate, 19421 altRowTemplate = that.altRowTemplate; 19422 19423 for (idx = 0, length = data.length; idx < length; idx++) { 19424 if (idx % 2) { 19425 html += altRowTemplate(data[idx]); 19426 } else { 19427 html += rowTemplate(data[idx]); 19428 } 19429 19430 that._data.push(data[idx]); 19431 } 19432 19433 return html; 19434 }, 19435 19436 _groupRowHtml: function(group, colspan, level) { 19437 var that = this, 19438 html = "", 19439 idx, 19440 length, 19441 field = group.field, 19442 column = $.grep(that.columns, function(column) { return column.field == field; })[0] || { }, 19443 value = column.format ? kendo.format(column.format, group.value) : group.value, 19444 template = column.groupHeaderTemplate, 19445 text = (column.title || field) + ': ' + value, 19446 data = extend({}, { field: group.field, value: group.value }, group.aggregates[group.field]), 19447 groupItems = group.items; 19448 19449 if (template) { 19450 text = typeof template === FUNCTION ? template(data) : kendo.template(template)(data); 19451 } 19452 19453 html += '<tr class="k-grouping-row">' + groupCells(level) + 19454 '<td colspan="' + colspan + '">' + 19455 '<p class="k-reset">' + 19456 '<a class="k-icon k-collapse" href="#"></a>
19456' + text + 19457 '</p></td></tr>'; 19458 19459 if(group.hasSubgroups) { 19460 for(idx = 0, length = groupItems.length; idx < length; idx++) { 19461 html += that._groupRowHtml(groupItems[idx], colspan - 1, level + 1); 19462 } 19463 } else { 19464 html += that._rowsHtml(groupItems); 19465 } 19466 19467 if (that.groupFooterTemplate) { 19468 html += that.groupFooterTemplate(group.aggregates); 19469 } 19470 return html; 19471 }, 19472 19473 collapseGroup: function(group) { 19474 group = $(group).find(".k-icon").addClass("k-expand").removeClass("k-collapse").end(); 19475 19476 var level = group.find(".k-group-cell").length, 19477 footerCount = 1, 19478 offset, 19479 tr; 19480 19481 group.nextAll("tr").each(function() { 19482 tr = $(this), 19483 offset = tr.find(".k-group-cell").length; 19484 19485 if (tr.hasClass("k-group-row")) { 19486 footerCount++; 19487 } else if (tr.hasClass("k-group-footer")) { 19488 footerCount--; 19489 } 19490 19491 if (offset <= level || (tr.hasClass("k-group-footer") && footerCount < 0)) { 19492 return false; 19493 } 19494 19495 tr.hide(); 19496 }); 19497 }, 19498 19499 expandGroup: function(group) { 19500 group = $(group).find(".k-icon").addClass("k-collapse").removeClass("k-expand").end(); 19501 var that = this, 19502 level = group.find(".k-group-cell").length; 19503 19504 group.nextAll("tr").each(function () { 19505 var tr = $(this); 19506 var offset = tr.find(".k-group-cell").length; 19507 if (offset <= level) { 19508 return false; 19509 } 19510 19511 if (offset == level + 1 && !tr.hasClass("k-detail-row")) { 19512 tr.show(); 19513 19514 if (tr.hasClass("k-grouping-row") && tr.find(".k-icon").hasClass("k-collapse")) { 19515 that.expandGroup(tr); 19516 } 19517 19518 if (tr.hasClass("k-master-row") && tr.find(".k-icon").hasClass("k-minus")) { 19519 tr.next().show(); 19520 } 19521 } 19522 }); 19523 }, 19524 19525 _updateHeader: function(groups) { 19526 var that = this, 19527 cells = that.thead.find("th.k-group-cell"), 19528 length = cells.length; 19529 19530 if(groups > length) { 19531 $(new Array(groups - length + 1).join('<th class="k-group-cell k-header"> </th>')).prependTo(that.thead.find("tr")); 19532 } else if(groups < length) { 19533 length = length - groups; 19534 $($.grep(cells, function(item, index) { return length > index; } )).remove(); 19535 } 19536 }, 19537 19538 _firstDataItem: function(data, grouped) { 19539 if(data && grouped) { 19540 if(data.hasSubgroups) { 19541 data = this._firstDataItem(data.items[0], grouped); 19542 } else { 19543 data = data.items[0]; 19544 } 19545 } 19546 return data; 19547 }, 19548 19549 _progress: function(toggle) { 19550 var that = this, 19551 element = that.element.is("table") ? that.element.parent() : (that.content && that.content.length ? that.content : that.element); 19552 19553 kendo.ui.progress(element, toggle); 19554 }, 19555 19556 refresh: function(e) { 19557 var that = this, 19558 length, 19559 idx, 19560 html = "", 19561 data = that.dataSource.view(), 19562 tbody, 19563 placeholder, 19564 currentIndex, 19565 current = that.current(), 19566 groups = (that.dataSource.group() || []).length, 19567 colspan = groups + that.columns.length; 19568 19569 if (e && e.action === "itemchange" && that.editable) { // skip rebinding if editing is in progress 19570 return; 19571 } 19572 19573 that.trigger("dataBinding"); 19574 19575 if (current && current.hasClass("k-state-focused")) { 19576 currentIndex = that.items().index(current.parent()); 19577 } 19578 19579 that._destroyEditable(); 19580 19581 that._progress(false); 19582 19583 that._data = []; 19584 19585 if (!that.columns.length) { 19586 that._autoColumns(that._firstDataItem(data[0], groups)); 19587 colspan = groups + that.columns.length; 19588 } 19589 19590 that._group = groups > 0 || that._group; 19591 19592 if(that._group) { 19593 that._templates(); 19594 that._updateCols(); 19595 that._updateHeader(groups); 19596 that._group = groups > 0; 19597 } 19598 19599 if(groups > 0) { 19600 if (that.detailTemplate) { 19601 colspan++; 19602 } 19603 19604 for (idx = 0, length = data.length; idx < length; idx++) { 19605 html += that._groupRowHtml(data[idx], colspan, 0); 19606 } 19607 } else { 19608 html += that._rowsHtml(data); 19609 } 19610 19611 if (tbodySupportsInnerHtml) { 19612 that.tbody[0].innerHTML = html; 19613 } else { 19614 placeholder = document.createElement("div"); 19615 placeholder.innerHTML = "<table><tbody>" + html + "</tbody></table>"; 19616 tbody = placeholder.firstChild.firstChild; 19617 that.table[0].replaceChild(tbody, that.tbody[0]); 19618 that.tbody = $(tbody); 19619 } 19620 19621 that._footer(); 19622 19623 if (currentIndex >= 0) { 19624 that.current(that.items().eq(currentIndex).children().filter(DATA_CELL).first()); 19625 } 19626 19627 if (that.options.resizable) { 19628 that.positionColumnResizeHandles(); 19629 } 19630 19631 that.trigger(DATABOUND); 19632 } 19633 }); 19634 19635 function hasCommand(commands, name) { 19636 var idx, length, command; 19637 19638 if (typeof commands === STRING) { 19639 return commands === name; 19640 } 19641 19642 if (isArray(commands)) { 19643 for (idx = 0, length = commands.length; idx < length; idx++) { 19644 command = commands[idx]; 19645 19646 if ((typeof command === STRING && command === name) || (command.name === name)) { 19647 return true; 19648 } 19649 } 19650 } 19651 return false; 19652 } 19653 19654 ui.plugin(Grid); 19655 ui.plugin(VirtualScrollable); 19656})(jQuery); 19657(function($, undefined) { 19658 var kendo = window.kendo, 19659 CHANGE = "change", 19660 DATABOUND = "dataBound", 19661 DATABINDING = "dataBinding", 19662 Widget = kendo.ui.Widget, 19663 keys = kendo.keys, 19664 FOCUSSELECTOR = ">*", 19665 REQUESTSTART = "requestStart", 19666 ERROR = "error", 19667 FOCUSED = "k-state-focused", 19668 FOCUSABLE = "k-focusable", 19669 SELECTED = "k-state-selected", 19670 KEDITITEM = "k-edit-item", 19671 STRING = "string", 19672 EDIT = "edit", 19673 REMOVE = "remove", 19674 proxy = $.proxy, 19675 progress = kendo.ui.progress, 19676 DataSource = kendo.data.DataSource; 19677 19678 var ListView = Widget.extend( { 19679 init: function(element, options) { 19680 var that = this; 19681 19682 options = $.isArray(options) ? { dataSource: options } : options; 19683 19684 Widget.fn.init.call(that, element, options); 19685 19686 options = that.options; 19687 19688 that.wrapper = that.element; 19689
19690 that._element(); 19691 19692 that._dataSource(); 19693 19694 that.template = kendo.template(options.template || ""); 19695 that.altTemplate = kendo.template(options.altTemplate || options.template); 19696 that.editTemplate = kendo.template(options.editTemplate || ""); 19697 19698 that._navigatable(); 19699 19700 that._selectable(); 19701 19702 if (that.options.autoBind){ 19703 that.dataSource.fetch(); 19704 } 19705 19706 kendo.notify(that); 19707 }, 19708 19709 events: [ 19710 CHANGE, 19711 DATABINDING, 19712 DATABOUND, 19713 EDIT, 19714 REMOVE 19715 ], 19716 19717 options: { 19718 name: "ListView", 19719 autoBind: true, 19720 selectable: false, 19721 navigatable: false, 19722 template: "", 19723 altTemplate: "", 19724 editTemplate: "" 19725 }, 19726 19727 items: function() { 19728 return this.element.find(FOCUSSELECTOR); 19729 }, 19730 19731 setDataSource: function(dataSource) { 19732 this.options.dataSource = dataSource; 19733 this._dataSource(); 19734 19735 if (this.options.autoBind) { 19736 dataSource.fetch(); 19737 } 19738 }, 19739 19740 _dataSource: function() { 19741 var that = this; 19742 19743 if (that.dataSource && that._refreshHandler) { 19744 that.dataSource.unbind(CHANGE, that._refreshHandler) 19745 .unbind(REQUESTSTART, that._requestStartHandler) 19746 .unbind(ERROR, that._errorHandler); 19747 } else { 19748 that._refreshHandler = proxy(that.refresh, that); 19749 that._requestStartHandler = proxy(that._requestStart, that); 19750 that._errorHandler = proxy(that._error, that); 19751 } 19752 19753 that.dataSource = DataSource.create(that.options.dataSource) 19754 .bind(CHANGE, that._refreshHandler) 19755 .bind(REQUESTSTART, that._requestStartHandler) 19756 .bind(ERROR, that._errorHandler); 19757 }, 19758 19759 _requestStart: function() { 19760 progress(this.element, true); 19761 }, 19762 19763 _error: function() { 19764 progress(this.element, false); 19765 }, 19766 19767 _element: function() { 19768 this.element.addClass("k-widget k-listview"); 19769 }, 19770 19771 refresh: function(e) { 19772 var that = this, 19773 view = that.dataSource.view(), 19774 data, 19775 item, 19776 html = "", 19777 idx, 19778 length, 19779 template = that.template, 19780 altTemplate = that.altTemplate; 19781 19782 if (e && e.action === "itemchange") { 19783 if (!that.editable) { 19784 data = e.items[0]; 19785 idx = view.indexOf(data); 19786 19787 if (idx >= 0) { 19788 item = $(template(data)); 19789 that.items().eq(idx).replaceWith(item); 19790 19791 that.trigger("itemChange", { 19792 item: item, 19793 data: data 19794 }); 19795 } 19796 } 19797 19798 return; 19799 } 19800 19801 that.trigger(DATABINDING); 19802 19803 that._destroyEditable(); 19804 19805 for (idx = 0, length = view.length; idx < length; idx++) { 19806 if (idx % 2) { 19807 html += altTemplate(view[idx]); 19808 } else { 19809 html += template(view[idx]); 19810 } 19811 } 19812 19813 that.element.html(html); 19814 19815 that.trigger(DATABOUND); 19816 }, 19817 19818 _selectable: function() { 19819 var that = this, 19820 multi, 19821 current, 19822 selectable = that.options.selectable, 19823 navigatable = that.options.navigatable; 19824 19825 if (selectable) { 19826 multi = typeof selectable === STRING && selectable.toLowerCase().indexOf("multiple") > -1; 19827 19828 that.selectable = new kendo.ui.Selectable(that.element, { 19829 multiple: multi, 19830 filter: FOCUSSELECTOR, 19831 change: function() { 19832 that.trigger(CHANGE); 19833 } 19834 }); 19835 19836 if (navigatable) { 19837 that.element.keydown(function(e) { 19838 if (e.keyCode === keys.SPACEBAR) { 19839 current = that.current(); 19840 e.preventDefault(); 19841 if(multi) { 19842 if(!e.ctrlKey) { 19843 that.selectable.clear(); 19844 } else { 19845 if(current.hasClass(SELECTED)) { 19846 current.removeClass(SELECTED); 19847 current = null; 19848 } 19849 } 19850 } else { 19851 that.selectable.clear(); 19852 } 19853 19854 that.selectable.value(current); 19855 } 19856 }); 19857 } 19858 } 19859 }, 19860 19861 current: function(element) { 19862 var that = this, 19863 current = that._current; 19864 19865 if (element !== undefined && element.length) { 19866 if (!current || current[0] !== element[0]) { 19867 element.addClass(FOCUSED); 19868 if (current) { 19869 current.removeClass(FOCUSED); 19870 } 19871 that._current = element; 19872 } 19873 } 19874 19875 return that._current; 19876 }, 19877 19878 _navigatable: function() { 19879 var that = this, 19880 navigatable = that.options.navigatable, 19881 element = that.element, 19882 currentProxy = proxy(that.current, that), 19883 clickCallback = function(e) { 19884 currentProxy($(e.currentTarget)); 19885 if(!$(e.target).is(":button,a,:input,a>.k-icon,textarea")) { 19886 element.focus(); 19887 } 19888 }; 19889 19890 if (navigatable) { 19891 element.attr("tabIndex", Math.max(element.attr("tabIndex") || 0, 0)); 19892 element.bind({ 19893 focus: function() { 19894 var current = that._current; 19895 if(current && current.is(":visible")) { 19896 current.addClass(FOCUSED); 19897 } else { 19898 currentProxy(element.find(FOCUSSELECTOR).first()); 19899 } 19900 }, 19901 focusout: function() { 19902 if (that._current) { 19903 that._current.removeClass(FOCUSED); 19904 } 19905 }, 19906 keydown: function(e) { 19907 var key = e.keyCode,
19908 current = that.current(); 19909 19910 if (keys.UP === key) { 19911 that.current(current ? current.prev() : element.find(FOCUSSELECTOR).first()); 19912 } else if (keys.DOWN === key) { 19913 that.current(current ? current.next() : element.find(FOCUSSELECTOR).first()); 19914 } else if (keys.PAGEUP == key) { 19915 that._current = null; 19916 that.dataSource.page(that.dataSource.page() - 1); 19917 } else if (keys.PAGEDOWN == key) { 19918 that._current = null; 19919 that.dataSource.page(that.dataSource.page() + 1); 19920 } 19921 } 19922 }); 19923 19924 element.addClass(FOCUSABLE).delegate("." + FOCUSABLE + FOCUSSELECTOR, "mousedown", clickCallback); 19925 } 19926 }, 19927 19928 clearSelection: function() { 19929 var that = this; 19930 that.selectable.clear(); 19931 that.trigger(CHANGE); 19932 }, 19933 19934 select: function(items) { 19935 var that = this, 19936 selectable = that.selectable; 19937 19938 items = $(items); 19939 if(items.length) { 19940 if(!selectable.options.multiple) { 19941 selectable.clear(); 19942 items = items.first(); 19943 } 19944 selectable.value(items); 19945 return; 19946 } 19947 19948 return selectable.value(); 19949 }, 19950 19951 _destroyEditable: function() { 19952 var that = this; 19953 if (that.editable) { 19954 that.editable.destroy(); 19955 delete that.editable; 19956 } 19957 }, 19958 19959 _closeEditable: function(validate) { 19960 var that = this, 19961 editable = that.editable, 19962 data, 19963 container, 19964 valid = true; 19965 19966 if (editable) { 19967 if (validate) { 19968 valid = editable.end(); 19969 } 19970 19971 if (valid) { 19972 data = that.dataSource.view()[editable.element.index()]; 19973 container = $(that.template(data)); 19974 that._destroyEditable(); 19975 editable.element.replaceWith(container); 19976 } 19977 } 19978 19979 return valid; 19980 }, 19981 19982 edit: function(item) { 19983 var that = this, 19984 data = that.dataSource.view()[item.index()], 19985 container = $(that.editTemplate(data)).addClass(KEDITITEM); 19986 19987 that.cancel(); 19988 item.replaceWith(container); 19989 that.editable = container.kendoEditable({ model: data, clearContainer: false, errorTemplate: false }).data("kendoEditable"); 19990 19991 that.trigger(EDIT, { model: data, item: container }); 19992 }, 19993 19994 save: function() { 19995 if (this._closeEditable(true)) { 19996 this.dataSource.sync(); 19997 } 19998 }, 19999 20000 remove: function(item) { 20001 var that = this, 20002 dataSource = that.dataSource, 20003 data = dataSource.view()[item.index()]; 20004 20005 if (!that.trigger(REMOVE, { model: data, item: item })) { 20006 item.hide(); 20007 dataSource.remove(data); 20008 dataSource.sync(); 20009 } 20010 }, 20011 20012 add: function() { 20013 var that = this, 20014 dataSource = that.dataSource, 20015 index = dataSource.indexOf((dataSource.view() || [])[0]); 20016 20017 if (index < 0) { 20018 index = 0; 20019 } 20020 20021 that.cancel(); 20022 dataSource.insert(index, {}); 20023 that.edit(that.element.children().first()); 20024 }, 20025 20026 cancel: function() { 20027 var that = this, 20028 dataSource = that.dataSource, 20029 data, 20030 index = -1; 20031 20032 if (that.editable) { 20033 index = that.editable.element.index(); 20034 } 20035 20036 if (index != -1) { 20037 data = dataSource.view()[index]; 20038 dataSource.cancelChanges(data); 20039 that._closeEditable(false); 20040 } 20041 } 20042 20043 });
20044 20045 kendo.ui.plugin(ListView); 20046})(jQuery); 20047(function($,undefined) { 20048 20049 // Imports ================================================================ 20050 var kendo = window.kendo, 20051 Class = kendo.Class, 20052 Widget = kendo.ui.Widget, 20053 extend = $.extend, 20054 deepExtend = kendo.deepExtend, 20055 keys = kendo.keys; 20056 20057 // options can be: template (as string), cssClass, title, defaultValue 20058 var ToolTemplate = Class.extend({ 20059 init: function(options) { 20060 var that = this; 20061 that.options = options; 20062 }, 20063 20064 getHtml: function() { 20065 var options = this.options; 20066 return kendo.template(options.template)({ 20067 cssClass: options.cssClass, 20068 tooltip: options.title, 20069 initialValue: options.initialValue 20070 }); 20071 } 20072 }); 20073 20074 var EditorUtils = { 20075 select: function(editor) { 20076 editor.trigger("select", {}); 20077 }, 20078 20079 editorWrapperTemplate: 20080 '<table cellspacing="4" cellpadding="0" class="k-widget k-editor k-header"><tbody>' + 20081 '<tr><td class="k-editor-toolbar-wrap"><ul class="k-editor-toolbar"></ul></td></tr>' + 20082 '<tr><td class="k-editable-area"></td></tr>' + 20083 '</tbody></table>', 20084 20085 buttonTemplate: 20086 '<li class="k-editor-button">' + 20087 '<a href="" class="k-tool-icon #= cssClass #" unselectable="on" title="#= tooltip #">#= tooltip #</a>' + 20088 '</li>', 20089 20090 colorPickerTemplate: 20091 '<li class="k-editor-colorpicker">' + 20092 '<div class="k-widget k-colorpicker k-header #= cssClass #">' + 20093 '<span class="k-tool-icon"><span class="k-selected-color"></span></span><span class="k-icon k-arrow-down"></span>' + 20094 '</div></li>', 20095 20096 comboBoxTemplate: 20097 '<li class="k-editor-combobox">' + 20098 '<select title="#= tooltip #" class="#= cssClass #"></select>' + 20099 '</li>', 20100 20101 dropDownListTemplate: 20102 '<li class="k-editor-selectbox">' + 20103 '<select title="#= tooltip #" class="#= cssClass #"></select>' + 20104 '</li>', 20105 20106 focusable: ".k-colorpicker,a.k-tool-icon:not(.k-state-disabled),.k-selectbox, .k-combobox .k-input", 20107 20108 wrapTextarea: function(textarea) { 20109 20110 var w = textarea.width(), 20111 h = textarea.height(), 20112 template = EditorUtils.editorWrapperTemplate, 20113 editorWrap = $(template).insertBefore(textarea).width(w).height(h), 20114 editArea = editorWrap.find(".k-editable-area"); 20115 20116 textarea.appendTo(editArea).addClass("k-content k-raw-content").hide(); 20117 20118 return textarea.closest(".k-editor"); 20119 }, 20120 20121 renderTools: function(editor, tools) { 20122 var editorTools = {}, 20123 currentTool, tool, i, 20124 nativeTools = editor._nativeTools, 20125 template, 20126 options, 20127 toolsArea = $(editor.element).closest(".k-editor").find(".k-editor-toolbar"); 20128 20129 if (tools) { 20130 for (i = 0; i < tools.length; i++) { 20131 currentTool = tools[i]; 20132 20133 if ($.isPlainObject(currentTool)) { 20134 options = extend({ cssClass: "k-custom", type: "button", tooltip: "" }, currentTool); 20135 20136 if (options.name) { 20137 options.cssClass = "k-" + options.name; 20138 } 20139 20140 if (!options.template) { 20141 if (options.type == "button") { 20142 options.template = EditorUtils.buttonTemplate; 20143 } 20144 } 20145 } else if (editor._tools[currentTool]) { 20146 editorTools[currentTool] = editor._tools[currentTool]; 20147 options = editorTools[currentTool].options; 20148 } 20149 20150 template = options.template; 20151 20152 if (template) { 20153 20154 if (template.getHtml) { 20155 template = template.getHtml(); 20156 } else { 20157 if (!$.isFunction(template)) { 20158 template = kendo.template(template); 20159 } 20160 20161 template = template(options); 20162 } 20163 20164 tool = $(template).appendTo(toolsArea); 20165 20166 if (options.type == "button" && options.exec) { 20167 tool.find(".k-tool-icon").click($.proxy(options.exec, editor.element[0])); 20168 } 20169 } 20170 } 20171 } 20172 20173 for (i = 0; i < nativeTools.length; i++) { 20174 editorTools[nativeTools[i]] = editor._tools[nativeTools[i]]; 20175 } 20176 20177 editor.options.tools = editorTools; 20178 }, 20179 20180 createContentElement: function(textarea, stylesheets) { 20181 var iframe, wnd, doc, 20182 rtlStyle = textarea.closest(".k-rtl").length ? "direction:rtl;" : ""; 20183 20184 textarea.hide(); 20185 iframe = $("<iframe />", { src: 'javascript:"<html></html>"', frameBorder: "0" }) 20186 .css("display", "") 20187 .addClass("k-content") 20188 .insertBefore(textarea)[0]; 20189 20190 wnd = iframe.contentWindow || iframe; 20191 doc = wnd.document || iframe.contentDocument; 20192 20193 doc.designMode = "On"; 20194 doc.open(); 20195 doc.write( 20196 "<!DOCTYPE html><html><head>" + 20197 "<meta charset='utf-8' />" + 20198 "<style>" + 20199 "html,body{padding:0;margin:0;font-family:Verdana,Geneva,sans-serif;background:#fff;}" + 20200 "html{font-size:100%}body{font-size:.75em;line-height:1.5;padding-top:1px;margin-top:-1px;" + 20201 rtlStyle + 20202 "}" + 20203 "h1{font-size:2em;margin:.67em 0}h2{font-size:1.5em}h3{font-size:1.16em}h4{font-size:1em}h5{font-size:.83em}h6{font-size:.7em}" + 20204 "p{margin:0 0 1em;padding:0 .2em}.k-marker{display:none;}.k-paste-container{position:absolute;
20204left:-10000px;width:1px;height:1px;overflow:hidden}" + 20205 "ul,ol{padding-left:2.5em}" + 20206 "a{color:#00a}" + 20207 "code{font-size:1.23em}" + 20208 "</style>" + 20209 $.map(stylesheets, function(href){ 20210 return "<link rel='stylesheet' href='" + href + "'>"; 20211 }).join("") + 20212 "</head><body spellcheck='false'></body></html>" 20213 ); 20214 20215 doc.close(); 20216 20217 return wnd; 20218 }, 20219 20220 initializeContentElement: function(editor) { 20221 var isFirstKeyDown = true; 20222 20223 editor.window = EditorUtils.createContentElement($(editor.textarea), editor.options.stylesheets); 20224 editor.document = editor.window.contentDocument || editor.window.document; 20225 editor.body = editor.document.body; 20226 20227 $(editor.document) 20228 .bind({ 20229 keydown: function (e) { 20230 if (e.keyCode === keys.F10) { 20231 // Handling with timeout to avoid the default IE menu 20232 setTimeout(function() { 20233 var TABINDEX = "tabIndex", 20234 element = editor.wrapper, 20235 tabIndex = element.attr(TABINDEX); 20236 20237 // Chrome can't focus something which has already been focused 20238 element.attr(TABINDEX, tabIndex || 0).focus().find("li:has(" + focusable + ")").first().focus(); 20239 20240 if (!tabIndex && tabIndex !== 0) { 20241 element.removeAttr(TABINDEX); 20242 } 20243 20244 }, 100); 20245 20246 e.preventDefault(); 20247 return; 20248 } 20249 20250 var toolName = editor.keyboard.toolFromShortcut(editor.options.tools, e); 20251 20252 if (toolName) { 20253 e.preventDefault(); 20254 if (!/undo|redo/.test(toolName)) { 20255 editor.keyboard.endTyping(true); 20256 } 20257 editor.exec(toolName); 20258 return false; 20259 } 20260 20261 if (editor.keyboard.isTypingKey(e) && editor.pendingFormats.hasPending()) { 20262 if (isFirstKeyDown) { 20263 isFirstKeyDown = false; 20264 } else { 20265 var range = editor.getRange(); 20266 editor.pendingFormats.apply(range); 20267 editor.selectRange(range); 20268 } 20269 } 20270 20271 editor.keyboard.clearTimeout(); 20272 20273 editor.keyboard.keydown(e); 20274 }, 20275 keyup: function (e) { 20276 var selectionCodes = [8, 9, 33, 34, 35, 36, 37, 38, 39, 40, 40, 45, 46]; 20277 20278 if ($.browser.mozilla && e.keyCode == keys.BACKSPACE) { 20279 fixBackspace(editor, e); 20280 } 20281 20282 if ($.inArray(e.keyCode, selectionCodes) > -1 || (e.keyCode == 65 && e.ctrlKey && !e.altKey && !e.shiftKey)) { 20283 editor.pendingFormats.clear(); 20284 select(editor); 20285 } 20286 20287 if (editor.keyboard.isTypingKey(e)) { 20288 if (editor.pendingFormats.hasPending()) { 20289 var range = editor.getRange(); 20290 editor.pendingFormats.apply(range); 20291 editor.selectRange(range); 20292 } 20293 } else { 20294 isFirstKeyDown = true; 20295 } 20296 20297 editor.keyboard.keyup(e); 20298 }, 20299 mousedown: function(e) { 20300 editor.pendingFormats.clear(); 20301 20302 var target = $(e.target); 20303 20304 if (!$.browser.gecko && e.which == 2 && target.is("a[href]")) { 20305 window.open(target.attr("href"), "_new"); 20306 } 20307 }, 20308 mouseup: function () { 20309 select(editor); 20310 } 20311 }); 20312 20313 $(editor.window) 20314 .bind("blur", function () { 20315 var old = editor.textarea.value, 20316 value = editor.encodedValue(); 20317
20318 editor.update(); 20319 20320 if (value != old) { 20321 editor.trigger("change"); 20322 } 20323 }); 20324 20325 $(editor.body) 20326 .bind("cut paste", function (e) { 20327 editor.clipboard["on" + e.type](e); 20328 }); 20329 }, 20330 20331 fixBackspace: function(editor, e) { 20332 20333 var range = editor.getRange(), 20334 startContainer = range.startContainer, 20335 dom = kendo.ui.editor.Dom, 20336 startContainerChildCount = startContainer.childNodes.length; 20337 20338 if (startContainer == editor.body.firstChild || 20339 !dom.isBlock(startContainer) || 20340 (startContainerChildCount > 0 && !(startContainerChildCount == 1 && dom.is(startContainer.firstChild, "br")))) { 20341 return; 20342 } 20343 20344 var previousBlock = startContainer.previousSibling; 20345 20346 while (previousBlock && !dom.isBlock(previousBlock)) { 20347 previousBlock = previousBlock.previousSibling; 20348 } 20349 20350 if (!previousBlock) { 20351 return; 20352 } 20353 20354 var walker = editor.document.createTreeWalker(previousBlock, window.NodeFilter.SHOW_TEXT, null, false); 20355 20356 var textNode; 20357 20358 while (true) { 20359 textNode = walker.nextNode(); 20360 20361 if (textNode) { 20362 previousBlock = textNode; 20363 } else { 20364 break; 20365 } 20366 } 20367 20368 range.setStart(previousBlock, dom.isDataNode(previousBlock) ? previousBlock.nodeValue.length : 0); 20369 range.collapse(true); 20370 kendo.ui.editor.RangeUtils.selectRange(range); 20371 20372 dom.remove(startContainer); 20373 20374 e.preventDefault(); 20375 }, 20376 20377 formatByName: function(name, format) { 20378 for (var i = 0; i < format.length; i++) { 20379 if ($.inArray(name, format[i].tags) >= 0) { 20380 return format[i]; 20381 } 20382 } 20383 }, 20384 20385 registerTool: function(toolName, tool) { 20386 var tools = Editor.fn._tools; 20387 tools[toolName] = tool; 20388 if (tools[toolName].options && tools[toolName].options.template) { 20389 tools[toolName].options.template.options.cssClass = "k-" + toolName; 20390 } 20391 }, 20392 20393 registerFormat: function(formatName, format) { 20394 kendo.ui.Editor.fn.options.formats[formatName] = format; 20395 } 20396 }; 20397 20398 var select = EditorUtils.select, 20399 focusable = EditorUtils.focusable, 20400 wrapTextarea = EditorUtils.wrapTextarea, 20401 renderTools = EditorUtils.renderTools, 20402 initializeContentElement = EditorUtils.initializeContentElement, 20403 fixBackspace = EditorUtils.fixBackspace; 20404 20405 var localization = { 20406 bold: "Bold", 20407 italic: "Italic", 20408 underline: "Underline", 20409 strikethrough: "Strikethrough", 20410 superscript: "Superscript", 20411 subscript: "Subscript", 20412 justifyCenter: "Center text", 20413 justifyLeft: "Align text left", 20414 justifyRight: "Align text right", 20415 justifyFull: "Justify", 20416 insertUnorderedList: "Insert unordered list", 20417 insertOrderedList: "Insert ordered list", 20418 indent: "Indent", 20419 outdent: "Outdent", 20420 createLink: "Insert hyperlink", 20421 unlink: "Remove hyperlink", 20422 insertImage: "Insert image", 20423 insertHtml: "Insert HTML", 20424 fontName: "Select font family", 20425 fontNameInherit: "(inherited font)", 20426 fontSize: "Select font size", 20427 fontSizeInherit: "(inherited size)", 20428 formatBlock: "Format", 20429 style: "Styles", 20430 emptyFolder: "Empty Folder", 20431 uploadFile: "Upload", 20432 orderBy: "Arrange by:", 20433 orderBySize: "Size", 20434 orderByName: "Name", 20435 invalidFileType: "The selected file \"{0}\" is not valid. Supported file types are {1}.", 20436 deleteFile: 'Are you sure you want to delete "{0}"?', 20437 overwriteFile: 'A file with name "{0}" already exists in the current directory. Do you want to overwrite it?', 20438 directoryNotFound: "A directory with this name was not found." 20439 }; 20440 20441 var supportedBrowser = !kendo.support.mobileOS || (kendo.support.mobileOS.ios && kendo.support.mobileOS.majorVersion >= 5); 20442 20443 var Editor = Widget.extend({ 20444 init: function (element, options) { 20445 /* suppress initialization in mobile webkit devices (w/o proper contenteditable support) */ 20446 if (!supportedBrowser) { 20447 return; 20448 } 20449 20450 var that = this, 20451 wrapper, 20452 value, 20453 editorNS = kendo.ui.editor; 20454 20455 Widget.fn.init.call(that, element, options); 20456 20457 that.options = deepExtend({}, that.options, options); 20458 20459 element = $(element); 20460 20461 element.closest("form").bind("submit", function () {
20462 that.update(); 20463 }); 20464 20465 for (var id in that._tools) { 20466 that._tools[id].name = id.toLowerCase(); 20467 } 20468 20469 that.textarea = element.attr("autocomplete", "off")[0]; 20470 20471 wrapper = that.wrapper = wrapTextarea(element); 20472 20473 renderTools(that, that.options.tools); 20474 20475 initializeContentElement(that); 20476 20477 that.keyboard = new editorNS.Keyboard([ 20478 new editorNS.TypingHandler(that), 20479 new editorNS.SystemHandler(that) 20480 ]); 20481 20482 that.clipboard = new editorNS.Clipboard(this); 20483 20484 that.pendingFormats = new editorNS.PendingFormats(this); 20485 20486 that.undoRedoStack = new editorNS.UndoRedoStack(); 20487 20488 if (options && options.value) { 20489 value = options.value; 20490 } else { 20491 // indented HTML introduces problematic ranges in IE 20492 value = element.val().replace(/[\r\n\v\f\t ]+/ig, " "); 20493 } 20494 20495 that.value(value); 20496 20497 function toolFromClassName(element) { 20498 var tool = $.grep(element.className.split(" "), function (x) { 20499 return !/^k-(widget|tool-icon|state-hover|header|combobox|dropdown|selectbox|colorpicker)$/i.test(x); 20500 }); 20501 return tool[0] ? tool[0].substring(2) : "custom"; 20502 } 20503 20504 function appendShortcutSequence(localizedText, tool) { 20505 if (!tool.key) { 20506 return localizedText; 20507 } 20508 20509 var res = localizedText + " ("; 20510 20511 if (tool.ctrl) { 20512 res += "Ctrl + "; 20513 } 20514 20515 if (tool.shift) { 20516 res += "Shift + "; 20517 } 20518 20519 if (tool.alt) { 20520 res += "Alt + "; 20521 } 20522 20523 res += tool.key + ")"; 20524 20525 return res; 20526 } 20527 20528 var toolbarItems = ".k-editor-toolbar > li > *, .k-editor-toolbar > li select", 20529 buttons = ".k-editor-button .k-tool-icon", 20530 enabledButtons = buttons + ":not(.k-state-disabled)", 20531 disabledButtons = buttons + ".k-state-disabled"; 20532 20533 wrapper.find(".k-combobox .k-input").keydown(function(e) { 20534 var combobox = $(this).closest(".k-combobox").data("kendoComboBox"), 20535 key = e.keyCode; 20536 20537 if (key == keys.RIGHT || key == keys.LEFT) { 20538 combobox.close(); 20539 } else if (key == keys.DOWN) { 20540 if (!combobox.dropDown.isOpened()) { 20541 e.stopImmediatePropagation(); 20542 combobox.open(); 20543 } 20544 } 20545 }); 20546 20547 wrapper 20548 .delegate(enabledButtons, "mouseenter", function() { $(this).addClass("k-state-hover"); }) 20549 .delegate(enabledButtons, "mouseleave", function() { $(this).removeClass("k-state-hover"); }) 20550 .delegate(buttons, "mousedown", false) 20551 .delegate(focusable, "keydown", function(e) { 20552 var closestLi = $(this).closest("li"), 20553 focusableTool = "li:has(" + focusable + ")", 20554 focusElement, 20555 keyCode = e.keyCode; 20556 20557 if (keyCode == keys.RIGHT) { 20558 focusElement = closestLi.nextAll(focusableTool).first().find(focusable); 20559 } else if (keyCode == keys.LEFT) { 20560 focusElement = closestLi.prevAll(focusableTool).last().find(focusable); 20561 } else if (keyCode == keys.ESC) { 20562 focusElement = that; 20563 } else if (keyCode == keys.TAB && !(e.ctrlKey || e.altKey)) { 20564 // skip tabbing to disabled tools, and focus the editing area when running out of tools 20565 if (e.shiftKey) { 20566 focusElement = closestLi.prevAll(focusableTool).last().find(focusable); 20567 20568 if (focusElement.length) { 20569 e.preventDefault(); 20570 } else { 20571 return; 20572 } 20573 } else { 20574 e.preventDefault(); 20575 20576 focusElement = closestLi.nextAll(focusableTool).first().find(focusable); 20577 20578 if (!focusElement.length) { 20579 focusElement = that; 20580 } 20581 } 20582 } 20583 20584 if (focusElement) { 20585 focusElement.focus(); 20586 } 20587 }) 20588 .delegate(enabledButtons, "click", function (e) { 20589 e.preventDefault(); 20590 e.stopPropagation(); 20591 that.exec(toolFromClassName(this)); 20592 }) 20593 .delegate(disabledButtons, "click", function(e) { e.preventDefault(); }) 20594 .find(toolbarItems) 20595 .each(function () { 20596 var toolName = toolFromClassName(this), 20597 options = that.options, 20598 tool = options.tools[toolName], 20599 description = options.localization[toolName], 20600 $this = $(this); 20601 20602 if (!tool) { 20603 return; 20604 } 20605 20606 if (toolName == "fontSize" || toolName == "fontName") { 20607 var inheritText = options.localization[toolName + "Inherit"] || localization[toolName + "Inherit"]; 20608 options[toolName][0].Text = inheritText; 20609 $this.find("input").val(inheritText).end() 20610 .find("span.k-input").text(inheritText).end(); 20611 } 20612 20613 tool.initialize($this, { 20614 title: appendShortcutSequence(description, tool), 20615 editor: that 20616 }); 20617 20618 }); 20619 20620 that.bind("select", function() { 20621 var range = that.getRange(); 20622 20623 var nodes = editorNS.RangeUtils.textNodes(range); 20624 20625 if (!nodes.length) { 20626 nodes = [range.startContainer]; 20627 } 20628 20629 wrapper.find(toolbarItems) 20630 .each(function () { 20631 var tool = that.options.tools[toolFromClassName(this)]; 20632 if (tool) { 20633 tool.update($(this), nodes, that.pendingFormats); 20634 } 20635 }); 20636 }); 20637 20638 $(document) 20639 .bind("DOMNodeInserted", function(e) { 20640 var wrapper = that.wrapper; 20641 20642 if ($.contains(e.target, wrapper[0]) || wrapper[0] == e.target) { 20643 // preserve updated value before re-initializing 20644 // don't use update() to prevent the editor from encoding the content too early 20645 that.textarea.value = that.value(); 20646 wrapper.find("iframe").remove(); 20647 initializeContentElement(that); 20648 } 20649 }) 20650 .bind("mousedown", function(e) { 20651 try { 20652 if (that.keyboard.isTypingInProgress()) { 20653 that.keyboard.endTyping(true); 20654 } 20655 20656 if (!that.selectionRestorePoint) { 20657 that.selectionRestorePoint = new editorNS.RestorePoint(that.getRange()); 20658 } 20659 } catch (e) { } 20660 }); 20661 20662 kendo.notify(that); 20663 }, 20664 20665 events: [ 20666 "select", 20667 "change", 20668 "execute", 20669 "error", 20670 "paste" 20671 ], 20672 20673 options: { 20674 name: "Editor", 20675 localization: localization, 20676 formats: {}, 20677 encoded: true, 20678 stylesheets: [], 20679 dialogOptions: { 20680 modal: true, resizable: false, draggable: true, 20681 animation: false 20682 }, 20683 fontName: [ 20684 { Text: localization.fontNameInherit, Value: "inherit" }, 20685 { Text: "Arial", Value: "Arial,Helvetica,sans-serif" }, 20686 { Text: "Courier New", Value: "'Courier New',Courier,monospace" }, 20687 { Text: "Georgia", Value: "Georgia,serif" }, 20688 { Text: "Impact", Value: "Impact,Charcoal,sans-serif" }, 20689 { Text: "Lucida Console", Value: "'Lucida Console',Monaco,monospace" }, 20690 { Text: "Tahoma", Value: "Tahoma,Geneva,sans-serif" }, 20691 { Text: "Times New Roman", Value: "'Times New Roman',Times,serif" }, 20692 { Text: "Trebuchet MS", Value: "'Trebuchet MS',Helvetica,sans-serif" }, 20693 { Text: "Verdana", Value: "Verdana,Geneva,sans-serif" } 20694 ], 20695 fontSize: [ 20696 { Text: localization.fontSizeInherit, Value: "inherit" }, 20697 { Text: "1 (8pt)", Value: "xx-small" },
20698 { Text: "2 (10pt)", Value: "x-small" }, 20699 { Text: "3 (12pt)", Value: "small" }, 20700 { Text: "4 (14pt)", Value: "medium" }, 20701 { Text: "5 (18pt)", Value: "large" }, 20702 { Text: "6 (24pt)", Value: "x-large" }, 20703 { Text: "7 (36pt)", Value: "xx-large" } 20704 ], 20705 formatBlock: [ 20706 { Text: "Paragraph", Value: "p" }, 20707 { Text: "Quotation", Value: "blockquote" }, 20708 { Text: "Heading 1", Value: "h1" }, 20709 { Text: "Heading 2", Value: "h2" }, 20710 { Text: "Heading 3", Value: "h3" }, 20711 { Text: "Heading 4", Value: "h4" }, 20712 { Text: "Heading 5", Value: "h5" }, 20713 { Text: "Heading 6", Value: "h6" } 20714 ], 20715 tools: [ 20716 "bold", 20717 "italic", 20718 "underline", 20719 "strikethrough", 20720 "fontName", 20721 "fontSize", 20722 "foreColor", 20723 "backColor", 20724 "justifyLeft", 20725 "justifyCenter", 20726 "justifyRight", 20727 "justifyFull", 20728 "insertUnorderedList", 20729 "insertOrderedList", 20730 "indent", 20731 "outdent", 20732 "formatBlock", 20733 "createLink", 20734 "unlink", 20735 "insertImage" 20736 //"style", 20737 //"subscript", 20738 //"superscript", 20739 ] 20740 }, 20741 20742 _nativeTools: [ 20743 "insertLineBreak", 20744 "insertParagraph", 20745 "redo", 20746 "undo" 20747 ], 20748 20749 _tools: { 20750 undo: { options: { key: "Z", ctrl: true } }, 20751 redo: { options: { key: "Y", ctrl: true } } 20752 }, 20753 20754 value: function (html) { 20755 var body = this.body, 20756 dom = kendo.ui.editor.Dom; 20757 20758 if (html === undefined) { 20759 return kendo.ui.editor.Serializer.domToXhtml(body); 20760 } 20761 20762 this.pendingFormats.clear(); 20763 20764 // handle null value passed as a parameter 20765 html = (html || "") 20766 // Some browsers do not allow setting CDATA sections through innerHTML so we encode them 20767 .replace(/<!\[CDATA\[(.*)?\]\]>/g, "<!--[CDATA[$1]]-->") 20768 // Encode script tags to avoid execution and lost content (IE) 20769 .replace(/<script([^>]*)>(.*)?<\/script>/ig, "<telerik:script $1>$2<\/telerik:script>") 20770 // <img>\s+\w+ creates invalid nodes after cut in IE 20771 .replace(/(<\/?img[^>]*>)[\r\n\v\f\t ]+/ig, "$1"); 20772 20773 if (!$.browser.msie) { 20774 // Add <br/>s to empty paragraphs in mozilla/chrome, to make them focusable 20775 html = html.replace(/<p([^>]*)>(\s*)?<\/p>/ig, '<p $1><br _moz_dirty="" /><\/p>'); 20776 } 20777 20778 if ($.browser.msie && parseInt($.browser.version, 10) < 9) { 20779 // Internet Explorer removes comments from the beginning of the html 20780 html = "<br/>" + html; 20781 20782 var originalSrc = "originalsrc", 20783 originalHref = "originalhref"; 20784 20785 // IE < 8 makes href and src attributes absolute 20786 html = html.replace(/href\s*=\s*(?:'|")?([^'">\s]*)(?:'|")?/, originalHref + '="$1"'); 20787 html = html.replace(/src\s*=\s*(?:'|")?([^'">\s]*)(?:'|")?/, originalSrc + '="$1"'); 20788 20789 body.innerHTML = html; 20790 dom.remove(body.firstChild); 20791 20792 $(body).find("telerik\\:script,script,link,img,a").each(function () { 20793 var node = this; 20794 if (node[originalHref]) { 20795 node.setAttribute("href", node[originalHref]); 20796 node.removeAttribute(originalHref); 20797 } 20798 if (node[originalSrc]) { 20799 node.setAttribute("src", node[originalSrc]); 20800 node.removeAttribute(originalSrc); 20801 } 20802 }); 20803 } else { 20804 body.innerHTML = html; 20805 if ($.browser.msie) { 20806 // having unicode characters creates denormalized DOM tree in IE9 20807 dom.normalize(body); 20808 } 20809 } 20810 20811 this.selectionRestorePoint = null; 20812 this.update(); 20813 }, 20814 20815 focus: function () { 20816 this.window.focus(); 20817 }, 20818 20819 update: function (value) { 20820 this.textarea.value = value || this.options.encoded ? this.encodedValue() : this.value(); 20821 }, 20822 20823 encodedValue: function () { 20824 return kendo.ui.editor.Dom.encode(this.value()); 20825 }, 20826 20827 createRange: function (document) { 20828 return kendo.ui.editor.RangeUtils.createRange(document || this.document); 20829 }, 20830 20831 getSelection: function () { 20832 return kendo.ui.editor.SelectionUtils.selectionFromDocument(this.document); 20833 }, 20834 20835 selectRange: function(range) { 20836 this.focus();
20837 var selection = this.getSelection(); 20838 selection.removeAllRanges(); 20839 selection.addRange(range); 20840 }, 20841 20842 getRange: function () { 20843 var selection = this.getSelection(), 20844 range = selection.rangeCount > 0 ? selection.getRangeAt(0) : this.createRange(), 20845 doc = this.document; 20846 20847 if (range.startContainer == doc && range.endContainer == doc && !range.startOffset && !range.endOffset) { 20848 range.setStart(this.body, 0); 20849 range.collapse(true); 20850 } 20851 20852 return range; 20853 }, 20854 20855 selectedHtml: function() { 20856 return kendo.ui.editor.Serializer.domToXhtml(this.getRange().cloneContents()); 20857 }, 20858 20859 paste: function (html) { 20860 this.clipboard.paste(html); 20861 }, 20862 20863 exec: function (name, params) { 20864 var that = this, 20865 range, body, id, 20866 tool = "", pendingTool; 20867 20868 name = name.toLowerCase(); 20869 20870 // restore selection 20871 if (!that.keyboard.isTypingInProgress()) { 20872 that.focus(); 20873 20874 range = that.getRange(); 20875 body = that.document.body; 20876 } 20877 20878 // exec tool 20879 for (id in that.options.tools) { 20880 if (id.toLowerCase() == name) { 20881 tool = that.options.tools[id]; 20882 break; 20883 } 20884 } 20885 20886 if (tool) { 20887 range = that.getRange(); 20888 20889 if (!/undo|redo/i.test(name) && tool.willDelayExecution(range)) { 20890 // clone our tool to apply params only once 20891 pendingTool = $.extend({}, tool); 20892 $.extend(pendingTool.options, { params: params }); 20893 that.pendingFormats.toggle(pendingTool); 20894 select(that); 20895 return; 20896 } 20897 20898 var command = tool.command ? tool.command(extend({ range: range }, params)) : null; 20899 20900 that.trigger("execute", { name: name, command: command }); 20901 20902 if (/undo|redo/i.test(name)) { 20903 that.undoRedoStack[name](); 20904 } else if (command) { 20905 if (!command.managesUndoRedo) { 20906 that.undoRedoStack.push(command); 20907 } 20908 20909 command.editor = that; 20910 command.exec(); 20911 20912 if (command.async) { 20913 command.change = $.proxy(function () { select(that); }, that); 20914 return; 20915 } 20916 } 20917 20918 select(that); 20919 } 20920 } 20921 }); 20922 20923 kendo.ui.plugin(Editor); 20924 20925 var Tool = Class.extend({ 20926 init: function(options) { 20927 this.options = options; 20928 }, 20929 20930 initialize: function($ui, options) { 20931 $ui.attr({ unselectable: "on", title: options.title }); 20932 }, 20933 20934 command: function (commandArguments) { 20935 return new this.options.command(commandArguments); 20936 }, 20937 20938 update: function() { 20939 }, 20940 20941 willDelayExecution: function() { 20942 return false; 20943 } 20944 20945 }); 20946 20947 Tool.exec = function (editor, name, value) { 20948 editor.exec(name, { value: value }); 20949 }; 20950 20951 var FormatTool = Tool.extend({ 20952 init: function (options) { 20953 Tool.fn.init.call(this, options); 20954 }, 20955 20956 command: function (commandArguments) { 20957 var that = this; 20958 return new kendo.ui.editor.FormatCommand(extend(commandArguments, { 20959 formatter: that.options.formatter 20960 })); 20961 }, 20962 20963 update: function($ui, nodes, pendingFormats) { 20964 var isPending = pendingFormats.isPending(this.name), 20965 isFormatted = this.options.finder.isFormatted(nodes), 20966 isActive = isPending ? !isFormatted : isFormatted; 20967 20968 $ui.toggleClass("k-state-active", isActive); 20969 } 20970 });
20971 20972 // Exports ================================================================ 20973 20974 extend(kendo.ui, { 20975 editor:{ 20976 ToolTemplate: ToolTemplate, 20977 EditorUtils: EditorUtils, 20978 Tool: Tool, 20979 FormatTool: FormatTool 20980 } 20981 }); 20982 20983})(jQuery); 20984(function($) { 20985 20986var kendo = window.kendo, 20987 map = $.map, 20988 extend = $.extend, 20989 STYLE = "style", 20990 FLOAT = "float", 20991 CSSFLOAT = "cssFloat", 20992 STYLEFLOAT = "styleFloat", 20993 CLASS = "class", 20994 KMARKER = "k-marker"; 20995 20996function makeMap(items) { 20997 var obj = {}, 20998 i, len; 20999 21000 for (i = 0, len = items.length; i < len; i++) { 21001 obj[items[i]] = true; 21002 } 21003 return obj; 21004} 21005 21006var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed".split(",")), 21007 blockElements = "div,p,h1,h2,h3,h4,h5,h6,address,applet,blockquote,button,center,dd,dir,dl,dt,fieldset,form,frameset,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,pre,script,table,tbody,td,tfoot,th,thead,tr,ul".split(","), 21008 block = makeMap(blockElements), 21009 inlineElements = "span,em,a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,strike,strong,sub,sup,textarea,tt,u,var".split(","), 21010 inline = makeMap(inlineElements), 21011 fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected".split(",")); 21012 21013var normalize = function (node) { 21014 if (node.nodeType == 1) { 21015 node.normalize(); 21016 } 21017}; 21018 21019if ($.browser.msie && parseInt($.browser.version, 10) >= 8) { 21020 normalize = function(parent) { 21021 if (parent.nodeType == 1 && parent.firstChild) { 21022 var prev = parent.firstChild, 21023 node = prev; 21024 21025 while (true) { 21026 node = node.nextSibling; 21027 21028 if (!node) { 21029 break; 21030 } 21031 21032 if (node.nodeType == 3 && prev.nodeType == 3) { 21033 node.nodeValue = prev.nodeValue + node.nodeValue; 21034 Dom.remove(prev); 21035 } 21036 21037 prev = node; 21038 } 21039 } 21040 }; 21041} 21042 21043var whitespace = /^\s+$/, 21044 rgb = /rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i, 21045 amp = /&/g, 21046 openTag = /</g, 21047 closeTag = />/g, 21048 nbsp = /\u00a0/g, 21049 bom = /\ufeff/g; 21050 var cssAttributes = 21051 ("color,padding-left,padding-right,padding-top,padding-bottom," + 21052 "background-color,background-attachment,background-image,background-position,background-repeat," + 21053 "border-top-style,border-top-width,border-top-color," + 21054 "border-bottom-style,border-bottom-width,border-bottom-color," + 21055 "border-left-style,border-left-width,border-left-color," + 21056 "border-right-style,border-right-width,border-right-color," + 21057 "font-family,font-size,font-style,font-variant,font-weight,line-height" 21058 ).split(","); 21059 21060var Dom = { 21061 findNodeIndex: function(node) { 21062 var i = 0; 21063 21064 while (true) { 21065 node = node.previousSibling; 21066 21067 if (!node) { 21068 break; 21069 } 21070 21071 i++; 21072 } 21073 21074 return i; 21075 }, 21076 21077 isDataNode: function(node) { 21078 return node && node.nodeValue !== null && node.data !== null; 21079 }, 21080 21081 isAncestorOf: function(parent, node) { 21082 try { 21083 return !Dom.isDataNode(parent) && ($.contains(parent, Dom.isDataNode(node) ? node.parentNode : node) || node.parentNode == parent); 21084 } catch (e) { 21085 return false; 21086 } 21087 }, 21088 21089 isAncestorOrSelf: function(root, node) { 21090 return Dom.isAncestorOf(root, node) || root == node; 21091 }, 21092 21093 findClosestAncestor: function(root, node) { 21094 if (Dom.isAncestorOf(root, node)) { 21095 while (node && node.parentNode != root) { 21096 node = node.parentNode; 21097 } 21098 } 21099 21100 return node; 21101 }, 21102 21103 getNodeLength: function(node) { 21104 return Dom.isDataNode(node) ? node.length : node.childNodes.length; 21105 }, 21106 21107 splitDataNode: function(node, offset) { 21108 var newNode = node.cloneNode(false); 21109 node.deleteData(offset, node.length); 21110 newNode.deleteData(0, offset); 21111 Dom.insertAfter(newNode, node); 21112 }, 21113 21114 attrEquals: function(node, attributes) { 21115 for (var key in attributes) { 21116 var value = node[key]; 21117 21118 if (key == FLOAT) { 21119 value = node[$.support.cssFloat ? CSSFLOAT : STYLEFLOAT]; 21120 } 21121 21122 if (typeof value == "object") { 21123 if (!Dom.attrEquals(value, attributes[key])) { 21124 return false; 21125 } 21126 } else if (value != attributes[key]) { 21127 return false; 21128 } 21129 } 21130 21131 return true; 21132 }, 21133 21134 blockParentOrBody: function(node) { 21135 return Dom.parentOfType(node, blockElements) || node.ownerDocument.body; 21136 }, 21137 21138 blockParents: function(nodes) { 21139 var blocks = [], 21140 i, len; 21141 21142 for (i = 0, len = nodes.length; i < len; i++) { 21143 var block = Dom.parentOfType(nodes[i], Dom.blockElements); 21144 if (block && $.inArray(block, blocks) < 0) { 21145 blocks.push(block); 21146 } 21147 } 21148 21149 return blocks; 21150 }, 21151 21152 windowFromDocument: function(document) { 21153 return document.defaultView || document.parentWindow; 21154 }, 21155 21156 normalize: normalize, 21157 blockElements: blockElements,
21158 inlineElements: inlineElements, 21159 empty: empty, 21160 fillAttrs: fillAttrs, 21161 21162 toHex: function (color) { 21163 var matches = rgb.exec(color); 21164 21165 if (!matches) { 21166 return color; 21167 } 21168 21169 return "#" + map(matches.slice(1), function (x) { 21170 x = parseInt(x, 10).toString(16); 21171 return x.length > 1 ? x : "0" + x; 21172 }).join(""); 21173 }, 21174 21175 encode: function (value) { 21176 return value.replace(amp, "&") 21177 .replace(openTag, "<") 21178 .replace(closeTag, ">") 21179 .replace(nbsp, " "); 21180 }, 21181 21182 name: function (node) { 21183 return node.nodeName.toLowerCase(); 21184 }, 21185 21186 significantChildNodes: function(node) { 21187 return $.grep(node.childNodes, function(child) { 21188 return child.nodeType != 3 || !Dom.isWhitespace(child); 21189 }); 21190 }, 21191 21192 lastTextNode: function(node) { 21193 var result = null; 21194 21195 if (node.nodeType == 3) { 21196 return node; 21197 } 21198 21199 for (var child = node.lastChild; child; child = child.previousSibling) { 21200 result = Dom.lastTextNode(child); 21201 21202 if (result) { 21203 return result; 21204 } 21205 } 21206 21207 return result; 21208 }, 21209 21210 is: function (node, nodeName) { 21211 return Dom.name(node) == nodeName; 21212 }, 21213 21214 isMarker: function(node) { 21215 return node.className == KMARKER; 21216 }, 21217 21218 isWhitespace: function(node) { 21219 return whitespace.test(node.nodeValue); 21220 }, 21221 21222 isBlock: function(node) { 21223 return block[Dom.name(node)]; 21224 }, 21225 21226 isEmpty: function(node) { 21227 return empty[Dom.name(node)]; 21228 }, 21229 21230 isInline: function(node) { 21231 return inline[Dom.name(node)]; 21232 }, 21233 21234 scrollTo: function (node) { 21235 node.ownerDocument.body.scrollTop = $(Dom.isDataNode(node) ? node.parentNode : node).offset().top; 21236 }, 21237 21238 insertAt: function (parent, newElement, position) { 21239 parent.insertBefore(newElement, parent.childNodes[position] || null); 21240 }, 21241 21242 insertBefore: function (newElement, referenceElement) { 21243 if (referenceElement.parentNode) { 21244 return referenceElement.parentNode.insertBefore(newElement, referenceElement); 21245 } else { 21246 return referenceElement; 21247 } 21248 }, 21249 21250 insertAfter: function (newElement, referenceElement) { 21251 return referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling); 21252 }, 21253 21254 remove: function (node) { 21255 node.parentNode.removeChild(node); 21256 }, 21257 21258 trim: function (parent) { 21259 for (var i = parent.childNodes.length - 1; i >= 0; i--) { 21260 var node = parent.childNodes[i]; 21261 if (Dom.isDataNode(node)) { 21262 if (!node.nodeValue.replace(bom, "").length) { 21263 Dom.remove(node); 21264 } 21265 21266 if (Dom.isWhitespace(node)) { 21267 Dom.insertBefore(node, parent); 21268 } 21269 } else if (node.className != KMARKER) { 21270 Dom.trim(node); 21271 if (!node.childNodes.length && !Dom.isEmpty(node)) { 21272 Dom.remove(node); 21273 } 21274 } 21275 } 21276 21277 return parent; 21278 }, 21279 21280 parentOfType: function (node, tags) { 21281 do { 21282 node = node.parentNode; 21283 } while (node && !(Dom.ofType(node, tags))); 21284 21285 return node; 21286 }, 21287 21288 ofType: function (node, tags) { 21289 return $.inArray(Dom.name(node), tags) >= 0; 21290 }, 21291 21292 changeTag: function (referenceElement, tagName) { 21293 var newElement = Dom.create(referenceElement.ownerDocument, tagName), 21294 attributes = referenceElement.attributes, 21295 i, len, name, value, attribute; 21296 21297 for (i = 0, len = attributes.length; i < len; i++) { 21298 attribute = attributes[i]; 21299 if (attribute.specified) { 21300 // IE < 8 cannot set class or style via setAttribute
21301 name = attribute.nodeName; 21302 value = attribute.nodeValue; 21303 if (name == CLASS) { 21304 newElement.className = value; 21305 } else if (name == STYLE) { 21306 newElement.style.cssText = referenceElement.style.cssText; 21307 } else { 21308 newElement.setAttribute(name, value); 21309 } 21310 } 21311 } 21312 21313 while (referenceElement.firstChild) { 21314 newElement.appendChild(referenceElement.firstChild); 21315 } 21316 21317 Dom.insertBefore(newElement, referenceElement); 21318 Dom.remove(referenceElement); 21319 return newElement; 21320 }, 21321 21322 wrap: function (node, wrapper) { 21323 Dom.insertBefore(wrapper, node); 21324 wrapper.appendChild(node); 21325 return wrapper; 21326 }, 21327 21328 unwrap: function (node) { 21329 var parent = node.parentNode; 21330 while (node.firstChild) { 21331 parent.insertBefore(node.firstChild, node); 21332 } 21333 21334 parent.removeChild(node); 21335 }, 21336 21337 create: function (document, tagName, attributes) { 21338 return Dom.attr(document.createElement(tagName), attributes); 21339 }, 21340 21341 attr: function (element, attributes) { 21342 attributes = extend({}, attributes); 21343 21344 if (attributes && STYLE in attributes) { 21345 Dom.style(element, attributes.style); 21346 delete attributes.style; 21347 } 21348 return extend(element, attributes); 21349 }, 21350 21351 style: function (node, value) { 21352 $(node).css(value || {}); 21353 }, 21354 21355 unstyle: function (node, value) { 21356 for (var key in value) { 21357 if (key == FLOAT) { 21358 key = $.support.cssFloat ? CSSFLOAT : STYLEFLOAT; 21359 } 21360 21361 node.style[key] = ""; 21362 } 21363 21364 if (node.style.cssText === "") { 21365 node.removeAttribute(STYLE); 21366 } 21367 }, 21368 21369 inlineStyle: function(document, name, attributes) { 21370 var span = $(Dom.create(document, name, attributes)), 21371 style; 21372 21373 document.body.appendChild(span[0]); 21374 21375 style = map(cssAttributes, function(value) { 21376 if ($.browser.msie && value == "line-height" && span.css(value) == "1px") { 21377 return "line-height:1.5"; 21378 } else { 21379 return value + ":" + span.css(value); 21380 } 21381 }).join(";"); 21382 21383 span.remove(); 21384 21385 return style; 21386 }, 21387 21388 removeClass: function(node, classNames) { 21389 var className = " " + node.className + " ", 21390 classes = classNames.split(" "), 21391 i, len; 21392 21393 for (i = 0, len = classes.length; i < len; i++) { 21394 className = className.replace(" " + classes[i] + " ", " "); 21395 } 21396 21397 className = $.trim(className); 21398 21399 if (className.length) { 21400 node.className = className; 21401 } else { 21402 node.removeAttribute(CLASS); 21403 } 21404 }, 21405 21406 commonAncestor: function () { 21407 var count = arguments.length, 21408 paths = [], 21409 minPathLength = Infinity, 21410 output = null, 21411 i, ancestors, node, first, j; 21412 21413 if (!count) { 21414 return null; 21415 } 21416 21417 if (count == 1) { 21418 return arguments[0]; 21419 } 21420 21421 for (i = 0; i < count; i++) { 21422 ancestors = []; 21423 node = arguments[i]; 21424 while (node) { 21425 ancestors.push(node); 21426 node = node.parentNode; 21427 } 21428 paths.push(ancestors.reverse()); 21429 minPathLength = Math.min(minPathLength, ancestors.length); 21430 } 21431 21432 if (count == 1) { 21433 return paths[0][0]; 21434 } 21435 21436 for (i = 0; i < minPathLength; i++) { 21437 first = paths[0][i]; 21438 21439 for (j = 1; j < count; j++) { 21440 if (first != paths[j][i]) { 21441 return output; 21442 } 21443 } 21444 21445 output = first; 21446 } 21447 return output; 21448 } 21449}; 21450 21451kendo.ui.editor.Dom = Dom; 21452 21453})(jQuery); 21454(function($, undefined) { 21455 21456// Imports ================================================================ 21457var kendo = window.kendo, 21458 Editor = kendo.ui.editor, 21459 dom = Editor.Dom, 21460 extend = $.extend; 21461 21462var fontSizeMappings = 'xx-small,x-small,small,medium,large,x-large,xx-large'.split(','), 21463 quoteRe = /"/g, 21464 brRe = /<br[^>]*>/i, 21465 emptyPRe = /<p><\/p>/i; 21466 21467var Serializer = { 21468 domToXhtml: function(root) { 21469 var result = []; 21470 var tagMap = { 21471 'telerik:script': { start: function (node) { result.push('<script'); attr(node); result.push('>'); }, end: function () { result.push('</script>'); } }, 21472 b: { start: function () { result.push('<strong>'); }, end: function () { result.push('</strong>'); } }, 21473 i: { start: function () { result.push('<em>'); }, end: function () { result.push('</em>'); } }, 21474 u: { start: function () { result.push('<span style="text-decoration:underline;">'); }, end: function () { result.push('</span>'); } }, 21475 font: { 21476 start: function (node) { 21477 result.push('<span style="'); 21478 21479 var color = node.getAttribute('color'); 21480 var size = fontSizeMappings[node.getAttribute('size')]; 21481 var face = node.getAttribute('face'); 21482 21483 if (color) { 21484 result.push('color:'); 21485 result.push(dom.toHex(color)); 21486 result.push(';'); 21487 } 21488 21489 if (face) { 21490 result.push('font-face:'); 21491 result.push(face); 21492 result.push(';'); 21493 } 21494 21495 if (size) { 21496 result.push('font-size:'); 21497 result.push(size); 21498 result.push(';'); 21499 } 21500 21501 result.push('">'); 21502 }, 21503 end: function (node) { 21504 result.push('</span>'); 21505 } 21506 } 21507 }; 21508 21509 function attr(node) { 21510 var specifiedAttributes = [], 21511 attributes = node.attributes, 21512 attribute, i, l, 21513 trim = $.trim; 21514 21515 if (dom.is(node, 'img')) { 21516 var width = node.style.width, 21517 height = node.style.height, 21518 $node = $(node); 21519 21520 if (width) { 21521 $node.attr('width', parseInt(width, 10)); 21522 dom.unstyle(node, { width: undefined }); 21523 } 21524 21525 if (height) { 21526 $node.attr('height', parseInt(height, 10)); 21527 dom.unstyle(node, { height: undefined }); 21528 } 21529 } 21530 21531 for (i = 0, l = attributes.length; i < l; i++) { 21532 attribute = attributes[i];
21533 var name = attribute.nodeName; 21534 // In IE < 8 the 'value' attribute is not returned as 'specified'. The same goes for type="text" 21535 if (attribute.specified || (name == 'value' && !node.value) || (name == 'type' && attribute.nodeValue == 'text')) { 21536 if (name.indexOf('_moz') < 0 && name != 'complete') { 21537 specifiedAttributes.push(attribute); 21538 } 21539 } 21540 } 21541 21542 if (!specifiedAttributes.length) { 21543 return; 21544 } 21545 21546 specifiedAttributes.sort(function (a, b) { 21547 return a.nodeName > b.nodeName ? 1 : a.nodeName < b.nodeName ? -1 : 0; 21548 }); 21549 21550 for (i = 0, l = specifiedAttributes.length; i < l; i++) { 21551 attribute = specifiedAttributes[i]; 21552 var attributeName = attribute.nodeName; 21553 var attributeValue = attribute.nodeValue; 21554 21555 result.push(' '); 21556 result.push(attributeName); 21557 result.push('="'); 21558 if (attributeName == 'style') { 21559 // In IE < 8 the style attribute does not return proper nodeValue 21560 var css = trim(attributeValue || node.style.cssText).split(';'); 21561 21562 for (var cssIndex = 0, len = css.length; cssIndex < len; cssIndex++) { 21563 var pair = css[cssIndex]; 21564 if (pair.length) { 21565 var propertyAndValue = pair.split(':'); 21566 var property = trim(propertyAndValue[0].toLowerCase()), 21567 value = trim(propertyAndValue[1]); 21568 21569 if (property == "font-size-adjust" || property == "font-stretch") { 21570 continue; 21571 } 21572 21573 if (property.indexOf('color') >= 0) { 21574 value = dom.toHex(value); 21575 } 21576 21577 if (property.indexOf('font') >= 0) { 21578 value = value.replace(quoteRe, "'"); 21579 } 21580 21581 result.push(property); 21582 result.push(':'); 21583 result.push(value); 21584 result.push(';'); 21585 } 21586 } 21587 } else if (attributeName == 'src' || attributeName == 'href') { 21588 result.push(node.getAttribute(attributeName, 2)); 21589 } else { 21590 result.push(dom.fillAttrs[attributeName] ? attributeName : attributeValue); 21591 } 21592 21593 result.push('"'); 21594 } 21595 } 21596 21597 function children(node, skip) { 21598 for (var childNode = node.firstChild; childNode; childNode = childNode.nextSibling) { 21599 child(childNode, skip); 21600 } 21601 } 21602 21603 function child(node, skip) { 21604 var nodeType = node.nodeType, 21605 tagName, mapper, 21606 parent, value, previous; 21607 21608 if (nodeType == 1) { 21609 tagName = dom.name(node); 21610 21611 if (!tagName || (node.attributes._moz_dirty && dom.is(node, 'br'))) { 21612 return; 21613 } 21614 21615 mapper = tagMap[tagName]; 21616 21617 if (mapper) { 21618 mapper.start(node); 21619 children(node); 21620 mapper.end(node); 21621 return; 21622 } 21623 21624 result.push('<'); 21625 result.push(tagName); 21626 21627 attr(node); 21628 21629 if (dom.empty[tagName]) { 21630 result.push(' />'); 21631 } else { 21632 result.push('>'); 21633 children(node, skip || dom.is(node, 'pre')); 21634 result.push('</'); 21635 result.push(tagName); 21636 result.push('>'); 21637 } 21638 } else if (nodeType == 3) { 21639 value = node.nodeValue; 21640 21641 if (!skip && $.support.leadingWhitespace) { 21642 parent = node.parentNode; 21643 previous = node.previousSibling; 21644 21645 if (!previous) { 21646 previous = (dom.isInline(parent) ? parent : node).previousSibling; 21647 } 21648 21649 if (!previous || previous.innerHTML === "" || dom.isBlock(previous)) { 21650 value = value.replace(/^[\r\n\v\f\t ]+/, ''); 21651 } 21652 21653 value = value.replace(/ +/, ' '); 21654 } 21655 21656 result.push(dom.encode(value)); 21657 21658 } else if (nodeType == 4) { 21659 result.push('<![CDATA['); 21660 result.push(node.data); 21661 result.push(']]>'); 21662 } else if (nodeType == 8) { 21663 if (node.data.indexOf('[CDATA[') < 0) { 21664 result.push('<!--'); 21665 result.push(node.data); 21666 result.push('-->'); 21667 } else { 21668 result.push('<!'); 21669 result.push(node.data); 21670 result.push('>'); 21671 } 21672 } 21673 } 21674 21675 children(root); 21676 21677 result = result.join(''); 21678 21679 // if serialized dom contains only whitespace elements, consider it empty (required field validation) 21680 if (result.replace(brRe, "").replace(emptyPRe, "") === "") { 21681 return ""; 21682 } 21683 21684 return result; 21685 } 21686 21687}; 21688 21689extend(Editor, { 21690 Serializer: Serializer 21691}); 21692 21693})(jQuery); 21694(function($) { 21695 21696 // Imports ================================================================ 21697 var kendo = window.kendo, 21698 Class = kendo.Class, 21699 extend = $.extend, 21700 Editor = kendo.ui.editor, 21701 dom = Editor.Dom, 21702 findNodeIndex = dom.findNodeIndex, 21703 isDataNode = dom.isDataNode, 21704 findClosestAncestor = dom.findClosestAncestor, 21705 getNodeLength = dom.getNodeLength, 21706 normalize = dom.normalize; 21707 21708var SelectionUtils = { 21709 selectionFromWindow: function(window) { 21710 if ($.browser.msie && $.browser.version < 9) { 21711 return new W3CSelection(window.document); 21712 } 21713 21714 return window.getSelection(); 21715 }, 21716 21717 selectionFromRange: function(range) { 21718 var rangeDocument = RangeUtils.documentFromRange(range); 21719 return SelectionUtils.selectionFromDocument(rangeDocument); 21720 }, 21721 21722 selectionFromDocument: function(document) { 21723 return SelectionUtils.selectionFromWindow(dom.windowFromDocument(document)); 21724 } 21725}; 21726 21727var W3CRange = Class.extend({ 21728 init: function(doc) { 21729 $.extend(this, { 21730 ownerDocument: doc, /* not part of the spec; used when cloning ranges, traversing the dom and creating fragments */ 21731 startContainer: doc, 21732 endContainer: doc, 21733 commonAncestorContainer: doc, 21734 startOffset: 0, 21735 endOffset: 0, 21736 collapsed: true 21737 }); 21738 }, 21739 21740 // Positioning Methods 21741 setStart: function (node, offset) { 21742 this.startContainer = node; 21743 this.startOffset = offset; 21744 updateRangeProperties(this); 21745 fixIvalidRange(this, true); 21746 }, 21747 21748 setEnd: function (node, offset) { 21749 this.endContainer = node; 21750 this.endOffset = offset; 21751 updateRangeProperties(this); 21752 fixIvalidRange(this, false); 21753 }, 21754 21755 setStartBefore: function (node) { 21756 this.setStart(node.parentNode, findNodeIndex(node));
21757 }, 21758 21759 setStartAfter: function (node) { 21760 this.setStart(node.parentNode, findNodeIndex(node) + 1); 21761 }, 21762 21763 setEndBefore: function (node) { 21764 this.setEnd(node.parentNode, findNodeIndex(node)); 21765 }, 21766 21767 setEndAfter: function (node) { 21768 this.setEnd(node.parentNode, findNodeIndex(node) + 1); 21769 }, 21770 21771 selectNode: function (node) { 21772 this.setStartBefore(node); 21773 this.setEndAfter(node); 21774 }, 21775 21776 selectNodeContents: function (node) { 21777 this.setStart(node, 0); 21778 this.setEnd(node, node[node.nodeType === 1 ? 'childNodes' : 'nodeValue'].length); 21779 }, 21780 21781 collapse: function (toStart) { 21782 var that = this; 21783 21784 if (toStart) { 21785 that.setEnd(that.startContainer, that.startOffset); 21786 } else { 21787 that.setStart(that.endContainer, that.endOffset); 21788 } 21789 }, 21790 21791 // Editing Methods 21792 21793 deleteContents: function () { 21794 var that = this, 21795 range = that.cloneRange(); 21796 21797 if (that.startContainer != that.commonAncestorContainer) { 21798 that.setStartAfter(findClosestAncestor(that.commonAncestorContainer, that.startContainer)); 21799 } 21800 21801 that.collapse(true); 21802 21803 (function deleteSubtree(iterator) { 21804 while (iterator.next()) { 21805 if (iterator.hasPartialSubtree()) { 21806 deleteSubtree(iterator.getSubtreeIterator()); 21807 } else { 21808 iterator.remove(); 21809 } 21810 } 21811 })(new RangeIterator(range)); 21812 }, 21813 21814 cloneContents: function () { 21815 // clone subtree 21816 var document = RangeUtils.documentFromRange(this); 21817 return (function cloneSubtree(iterator) { 21818 var node, frag = document.createDocumentFragment(); 21819 21820 while (node = iterator.next()) { 21821 node = node.cloneNode(!iterator.hasPartialSubtree()); 21822 21823 if (iterator.hasPartialSubtree()) { 21824 node.appendChild(cloneSubtree(iterator.getSubtreeIterator())); 21825 } 21826 21827 frag.appendChild(node); 21828 } 21829 21830 return frag; 21831 })(new RangeIterator(this)); 21832 }, 21833 21834 extractContents: function () { 21835 var that = this, 21836 range = that.cloneRange(); 21837 21838 if (that.startContainer != that.commonAncestorContainer) { 21839 that.setStartAfter(findClosestAncestor(that.commonAncestorContainer, that.startContainer)); 21840 } 21841 21842 that.collapse(true); 21843 21844 var document = RangeUtils.documentFromRange(that); 21845 21846 return (function extractSubtree(iterator) { 21847 var node, frag = document.createDocumentFragment(); 21848 21849 while (node = iterator.next()) { 21850 if (iterator.hasPartialSubtree()) { 21851 node = node.cloneNode(false); 21852 node.appendChild(extractSubtree(iterator.getSubtreeIterator())); 21853 } else { 21854 iterator.remove(that.originalRange); 21855 } 21856 21857 frag.appendChild(node); 21858 } 21859 21860 return frag; 21861 })(new RangeIterator(range)); 21862 }, 21863 21864 insertNode: function (node) { 21865 var that = this; 21866 21867 if (isDataNode(that.startContainer)) { 21868 if (that.startOffset != that.startContainer.nodeValue.length) { 21869 dom.splitDataNode(that.startContainer, that.startOffset); 21870 } 21871 21872 dom.insertAfter(node, that.startContainer); 21873 } else { 21874 dom.insertAt(that.startContainer, node, that.startOffset); 21875 } 21876 21877 that.setStart(that.startContainer, that.startOffset); 21878 }, 21879 21880 cloneRange: function () { 21881 // fast copy 21882 return $.extend(new W3CRange(this.ownerDocument), { 21883 startContainer: this.startContainer, 21884 endContainer: this.endContainer, 21885 commonAncestorContainer: this.commonAncestorContainer, 21886 startOffset: this.startOffset, 21887 endOffset: this.endOffset, 21888 collapsed: this.collapsed, 21889 21890 originalRange: this /* not part of the spec; used to update the original range when calling extractContents() on clones */ 21891 }); 21892 }, 21893 21894 // used for debug purposes 21895 toString: function () { 21896 var startNodeName = this.startContainer.nodeName, 21897 endNodeName = this.endContainer.nodeName; 21898 21899 return [startNodeName == "#text" ? this.startContainer.nodeValue : startNodeName, '(', this.startOffset, ') : ', 21900 endNodeName == "#text" ? this.endContainer.nodeValue : endNodeName, '(', this.endOffset, ')'].join(''); 21901 } 21902}); 21903 21904/* can be used in Range.compareBoundaryPoints if we need it one day */ 21905function compareBoundaries(start, end, startOffset, endOffset) { 21906 if (start == end) { 21907 return endOffset - startOffset; 21908 } 21909 21910 // end is child of start 21911 var container = end; 21912 while (container && container.parentNode != start) { 21913 container = container.parentNode; 21914 } 21915 21916 if (container) { 21917 return findNodeIndex(container) - startOffset; 21918 } 21919 21920 // start is child of end 21921 container = start; 21922 while (container && container.parentNode != end) { 21923 container = container.parentNode; 21924 } 21925 21926 if (container) { 21927 return endOffset - findNodeIndex(container) - 1; 21928 } 21929 21930 // deep traversal 21931 var root = dom.commonAncestor(start, end); 21932 var startAncestor = start; 21933 21934 while (startAncestor && startAncestor.parentNode != root) { 21935 startAncestor = startAncestor.parentNode; 21936 } 21937 21938 if (!startAncestor) { 21939 startAncestor = root; 21940 } 21941 21942 var endAncestor = end; 21943 while (endAncestor && endAncestor.parentNode != root) { 21944 endAncestor = endAncestor.parentNode; 21945 } 21946 21947 if (!endAncestor) { 21948 endAncestor = root; 21949 } 21950 21951 if (startAncestor == endAncestor) { 21952 return 0; 21953 } 21954 21955 return findNodeIndex(endAncestor) - findNodeIndex(startAncestor); 21956} 21957 21958function fixIvalidRange(range, toStart) { 21959 function isInvalidRange(range) { 21960 try { 21961 return compareBoundaries(range.startContainer, range.endContainer, range.startOffset, range.endOffset) < 0; 21962 } catch (ex) { 21963 // range was initially invalid (e.g. when cloned from invalid range) - it must be fixed 21964 return true; 21965 } 21966 } 21967 21968 if (isInvalidRange(range)) { 21969 if (toStart) { 21970 range.commonAncestorContainer = range.endContainer = range.startContainer; 21971 range.endOffset = range.startOffset; 21972 } else { 21973 range.commonAncestorContainer = range.startContainer = range.endContainer; 21974 range.startOffset = range.endOffset; 21975 } 21976 21977 range.collapsed = true; 21978 } 21979} 21980 21981function updateRangeProperties(range) { 21982 range.collapsed = range.startContainer == range.endContainer && range.startOffset == range.endOffset; 21983 21984 var node = range.startContainer; 21985 while (node && node != range.endContainer && !dom.isAncestorOf(node, range.endContainer)) { 21986 node = node.parentNode; 21987 } 21988 21989 range.commonAncestorContainer = node; 21990} 21991 21992var RangeIterator = Class.extend({ 21993 init: function(range) { 21994 $.extend(this, { 21995 range: range, 21996 _current: null, 21997 _next: null, 21998 _end: null 21999 }); 22000 22001 if (range.collapsed) { 22002 return; 22003 } 22004 22005 var root = range.commonAncestorContainer; 22006 22007 this._next = range.startContainer == root && !isDataNode(range.startContainer) ? 22008 range.startContainer.childNodes[range.startOffset] : 22009 findClosestAncestor(root, range.startContainer); 22010 22011 this._end = range.endContainer == root && !isDataNode(range.endContainer) ? 22012 range.endContainer.childNodes[range.endOffset] : 22013 findClosestAncestor(root, range.endContainer).nextSibling; 22014 }, 22015 22016 hasNext: function () { 22017 return !!this._next; 22018 }, 22019 22020 next: function () { 22021 var that = this, 22022 current = that._current = that._next; 22023 that._next = that._current && that._current.nextSibling != that._end ? 22024 that._current.nextSibling : null; 22025 22026 if (isDataNode(that._current)) { 22027 if (that.range.endContainer == that._current) { 22028 current = current.cloneNode(true); 22029 current.deleteData(that.range.endOffset, current.length - that.range.endOffset); 22030 } 22031 22032 if (that.range.startContainer == that._current) { 22033 current = current.cloneNode(true); 22034 current.deleteData(0, that.range.startOffset); 22035 } 22036 } 22037 22038 return current; 22039 }, 22040 22041 traverse: function (callback) { 22042 var that = this, 22043 current; 22044 22045 function next() { 22046 that._current = that._next; 22047 that._next = that._current && that._current.nextSibling != that._end ? that._current.nextSibling : null; 22048 return that._current; 22049 } 22050 22051 while (current = next()) { 22052 if (that.hasPartialSubtree()) { 22053 that.getSubtreeIterator().traverse(callback); 22054 } else { 22055 callback(current); 22056 } 22057 } 22058 22059 return current; 22060 }, 22061 22062 remove: function (originalRange) { 22063 var that = this, 22064 inStartContainer = that.range.startContainer == that._current, 22065 inEndContainer = that.range.endContainer == that._current, 22066 start, end, delta; 22067 22068 if (isDataNode(that._current) && (inStartContainer || inEndContainer)) { 22069 start = inStartContainer ? that.range.startOffset : 0; 22070 end = inEndContainer ? that.range.endOffset : that._current.length; 22071 delta = end - start; 22072 22073 if (originalRange && (inStartContainer || inEndContainer)) { 22074 if (that._current == originalRange.startContainer && start <= originalRange.startOffset) { 22075 originalRange.startOffset -= delta; 22076 } 22077 22078 if (that._current == originalRange.endContainer && end <= originalRange.endOffset) { 22079 originalRange.endOffset -= delta; 22080 } 22081 } 22082 22083 that._current.deleteData(start, delta); 22084 } else { 22085 var parent = that._current.parentNode; 22086 22087 if (originalRange && (that.range.startContainer == parent || that.range.endContainer == parent)) { 22088 var nodeIndex = findNodeIndex(that._current); 22089 22090 if (parent == originalRange.startContainer && nodeIndex <= originalRange.startOffset) { 22091 originalRange.startOffset -= 1; 22092 } 22093 22094 if (parent == originalRange.endContainer && nodeIndex < originalRange.endOffset) { 22095 originalRange.endOffset -= 1; 22096 } 22097 } 22098 22099 dom.remove(that._current); 22100 } 22101 }, 22102 22103 hasPartialSubtree: function () { 22104 return !isDataNode(this._current) && 22105 (dom.isAncestorOrSelf(this._current, this.range.startContainer) || 22106 dom.isAncestorOrSelf(this._current, this.range.endContainer));
22107 }, 22108 22109 getSubtreeIterator: function () { 22110 var that = this, 22111 subRange = that.range.cloneRange(); 22112 22113 subRange.selectNodeContents(that._current); 22114 22115 if (dom.isAncestorOrSelf(that._current, that.range.startContainer)) { 22116 subRange.setStart(that.range.startContainer, that.range.startOffset); 22117 } 22118 22119 if (dom.isAncestorOrSelf(that._current, that.range.endContainer)) { 22120 subRange.setEnd(that.range.endContainer, that.range.endOffset); 22121 } 22122 22123 return new RangeIterator(subRange); 22124 } 22125}); 22126 22127var W3CSelection = Class.extend({ 22128 init: function(doc) { 22129 this.ownerDocument = doc; 22130 this.rangeCount = 1; 22131 }, 22132 22133 addRange: function (range) { 22134 var textRange = this.ownerDocument.body.createTextRange(); 22135 22136 // end container should be adopted first in order to prevent selection with negative length 22137 adoptContainer(textRange, range, false); 22138 adoptContainer(textRange, range, true); 22139 22140 textRange.select(); 22141 }, 22142 22143 removeAllRanges: function () { 22144 this.ownerDocument.selection.empty(); 22145 }, 22146 22147 getRangeAt: function () { 22148 var textRange, range = new W3CRange(this.ownerDocument), selection = this.ownerDocument.selection, element; 22149 22150 try { 22151 textRange = selection.createRange(); 22152 element = textRange.item ? textRange.item(0) : textRange.parentElement(); 22153 if (element.ownerDocument != this.ownerDocument) { 22154 return range; 22155 } 22156 } catch (ex) { 22157 return range; 22158 } 22159 22160 if (selection.type == 'Control') { 22161 range.selectNode(textRange.item(0)); 22162 } else { 22163 adoptEndPoint(textRange, range, true); 22164 adoptEndPoint(textRange, range, false); 22165 22166 if (range.startContainer.nodeType == 9) { 22167 range.setStart(range.endContainer, range.startOffset); 22168 } 22169 22170 if (range.endContainer.nodeType == 9) { 22171 range.setEnd(range.startContainer, range.endOffset); 22172 } 22173 22174 if (textRange.compareEndPoints('StartToEnd', textRange) === 0) { 22175 range.collapse(false); 22176 } 22177 22178 var startContainer = range.startContainer, 22179 endContainer = range.endContainer, 22180 body = this.ownerDocument.body; 22181 22182 if (!range.collapsed && range.startOffset === 0 && range.endOffset == getNodeLength(range.endContainer) && // check for full body selection 22183 !(startContainer == endContainer && isDataNode(startContainer) && startContainer.parentNode == body)) { // but not when single textnode is selected 22184 var movedStart = false, 22185 movedEnd = false; 22186 22187 while (findNodeIndex(startContainer) === 0 && startContainer == startContainer.parentNode.firstChild && startContainer != body) { 22188 startContainer = startContainer.parentNode; 22189 movedStart = true; 22190 } 22191 22192 while (findNodeIndex(endContainer) == getNodeLength(endContainer.parentNode) - 1 && endContainer == endContainer.parentNode.lastChild && endContainer != body) { 22193 endContainer = endContainer.parentNode; 22194 movedEnd = true; 22195 } 22196 22197 if (startContainer == body && endContainer == body && movedStart && movedEnd) { 22198 range.setStart(startContainer, 0); 22199 range.setEnd(endContainer, getNodeLength(body)); 22200 } 22201 } 22202 } 22203 22204 return range; 22205 } 22206}); 22207 22208function adoptContainer(textRange, range, start) { 22209 // find anchor node and offset 22210 var container = range[start ? 'startContainer' : 'endContainer']; 22211 var offset = range[start ? 'startOffset' : 'endOffset'], textOffset = 0; 22212 var anchorNode = isDataNode(container) ? container : container.childNodes[offset] || null; 22213 var anchorParent = isDataNode(container) ? container.parentNode : container; 22214 // visible data nodes need a text offset 22215 if (container.nodeType == 3 || container.nodeType == 4) { 22216 textOffset = offset; 22217 } 22218 22219 // create a cursor element node to position range (since we can't select text nodes) 22220 var cursorNode = anchorParent.insertBefore(dom.create(range.ownerDocument, 'a'), anchorNode); 22221 22222 var cursor = range.ownerDocument.body.createTextRange(); 22223 cursor.moveToElementText(cursorNode); 22224 dom.remove(cursorNode); 22225 cursor[start ? 'moveStart' : 'moveEnd']('character', textOffset); 22226 cursor.collapse(false); 22227 textRange.setEndPoint(start ? 'StartToStart' : 'EndToStart', cursor); 22228} 22229 22230function adoptEndPoint(textRange, range, start) { 22231 var cursorNode = dom.create(range.ownerDocument, 'a'), cursor = textRange.duplicate(); 22232 cursor.collapse(start);
22233 var parent = cursor.parentElement(); 22234 do { 22235 parent.insertBefore(cursorNode, cursorNode.previousSibling); 22236 cursor.moveToElementText(cursorNode); 22237 } while (cursor.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', textRange) > 0 && cursorNode.previousSibling); 22238 22239 cursor.setEndPoint(start ? 'EndToStart' : 'EndToEnd', textRange); 22240 22241 var target = cursorNode.nextSibling; 22242 22243 if (!target) { 22244 // at end of text node 22245 target = cursorNode.previousSibling; 22246 22247 if (target && isDataNode(target)) { // in case of collapsed range in empty tag 22248 range.setEnd(target, target.nodeValue.length); 22249 dom.remove(cursorNode); 22250 } else { 22251 range.selectNodeContents(parent); 22252 dom.remove(cursorNode); 22253 range.endOffset -= 1; // cursorNode was in parent 22254 } 22255 22256 return; 22257 } 22258 22259 dom.remove(cursorNode); 22260 22261 if (isDataNode(target)) { 22262 range[start ? 'setStart' : 'setEnd'](target, cursor.text.length); 22263 } else { 22264 range[start ? 'setStartBefore' : 'setEndBefore'](target); 22265 } 22266} 22267 22268var RangeEnumerator = Class.extend({ 22269 init: function(range) { 22270 this.enumerate = function () { 22271 var nodes = []; 22272 22273 function visit(node) { 22274 if (dom.is(node, 'img') || (node.nodeType == 3 && !dom.isWhitespace(node))) { 22275 nodes.push(node); 22276 } else { 22277 node = node.firstChild; 22278 while (node) { 22279 visit(node); 22280 node = node.nextSibling; 22281 } 22282 } 22283 } 22284 22285 new RangeIterator(range).traverse(visit); 22286 22287 return nodes; 22288 }; 22289 } 22290}); 22291 22292var RestorePoint = Class.extend({ 22293 init: function(range) { 22294 var that = this; 22295 that.range = range; 22296 that.rootNode = RangeUtils.documentFromRange(range); 22297 that.body = that.rootNode.body; 22298 that.html = that.body.innerHTML; 22299 22300 that.startContainer = that.nodeToPath(range.startContainer); 22301 that.endContainer = that.nodeToPath(range.endContainer); 22302 that.startOffset = that.offset(range.startContainer, range.startOffset); 22303 that.endOffset = that.offset(range.endContainer, range.endOffset); 22304 }, 22305 22306 index: function(node) { 22307 var result = 0, 22308 lastType = node.nodeType; 22309 22310 while (node = node.previousSibling) { 22311 var nodeType = node.nodeType; 22312 22313 if (nodeType != 3 || lastType != nodeType) { 22314 result ++; 22315 } 22316 22317 lastType = nodeType; 22318 } 22319 22320 return result; 22321 }, 22322 22323 offset: function(node, value) { 22324 if (node.nodeType == 3) { 22325 while ((node = node.previousSibling) && node.nodeType == 3) { 22326 value += node.nodeValue.length; 22327 } 22328 } 22329 22330 return value; 22331 }, 22332 22333 nodeToPath: function(node) { 22334 var path = []; 22335 22336 while (node != this.rootNode) { 22337 path.push(this.index(node)); 22338 node = node.parentNode; 22339 } 22340 22341 return path; 22342 }, 22343 22344 toRangePoint: function(range, start, path, denormalizedOffset) { 22345 var node = this.rootNode, 22346 length = path.length, 22347 offset = denormalizedOffset; 22348 22349 while (length--) { 22350 node = node.childNodes[path[length]]; 22351 } 22352 22353 while (node.nodeType == 3 && node.nodeValue.length < offset) { 22354 offset -= node.nodeValue.length; 22355 node = node.nextSibling; 22356 } 22357 22358 range[start ? 'setStart' : 'setEnd'](node, offset); 22359 }, 22360 22361 toRange: function () { 22362 var that = this, 22363 result = that.range.cloneRange(); 22364 22365 that.toRangePoint(result, true, that.startContainer, that.startOffset); 22366 that.toRangePoint(result, false, that.endContainer, that.endOffset); 22367 22368 return result; 22369 } 22370 22371}); 22372 22373var Marker = Class.extend({ 22374 init: function() { 22375 this.caret = null; 22376 }, 22377 22378 addCaret: function (range) { 22379 var that = this; 22380 22381 that.caret = dom.create(RangeUtils.documentFromRange(range), 'span', { className: 'k-marker' });
22382 range.insertNode(that.caret); 22383 range.selectNode(that.caret); 22384 return that.caret; 22385 }, 22386 22387 removeCaret: function (range) { 22388 var that = this, 22389 previous = that.caret.previousSibling, 22390 startOffset = 0; 22391 22392 if (previous) { 22393 startOffset = isDataNode(previous) ? previous.nodeValue.length : findNodeIndex(previous); 22394 } 22395 22396 var container = that.caret.parentNode; 22397 var containerIndex = previous ? findNodeIndex(previous) : 0; 22398 22399 dom.remove(that.caret); 22400 normalize(container); 22401 22402 var node = container.childNodes[containerIndex]; 22403 22404 if (isDataNode(node)) { 22405 range.setStart(node, startOffset); 22406 } 22407 else if (node) { 22408 var textNode = dom.lastTextNode(node); 22409 if (textNode) { 22410 range.setStart(textNode, textNode.nodeValue.length); 22411 } else { 22412 range[previous ? 'setStartAfter' : 'setStartBefore'](node); 22413 } 22414 } else { 22415 if (!$.browser.msie && !container.innerHTML) { 22416 container.innerHTML = '<br _moz_dirty="" />'; 22417 } 22418 22419 range.selectNodeContents(container); 22420 } 22421 range.collapse(true); 22422 }, 22423 22424 add: function (range, expand) { 22425 var that = this; 22426 22427 if (expand && range.collapsed) { 22428 that.addCaret(range); 22429 range = RangeUtils.expand(range); 22430 } 22431 22432 var rangeBoundary = range.cloneRange(); 22433 22434 rangeBoundary.collapse(false); 22435 that.end = dom.create(RangeUtils.documentFromRange(range), 'span', { className: 'k-marker' }); 22436 rangeBoundary.insertNode(that.end); 22437 22438 rangeBoundary = range.cloneRange(); 22439 rangeBoundary.collapse(true); 22440 that.start = that.end.cloneNode(true); 22441 rangeBoundary.insertNode(that.start); 22442 22443 range.setStartBefore(that.start); 22444 range.setEndAfter(that.end); 22445 22446 normalize(range.commonAncestorContainer); 22447 22448 return range; 22449 }, 22450 22451 remove: function (range) { 22452 var that = this, 22453 start = that.start, 22454 end = that.end, 22455 shouldNormalizeStart, 22456 shouldNormalizeEnd, 22457 shouldNormalize; 22458 22459 normalize(range.commonAncestorContainer); 22460 22461 while (!start.nextSibling && start.parentNode) { 22462 start = start.parentNode; 22463 } 22464 22465 while (!end.previousSibling && end.parentNode) { 22466 end = end.parentNode; 22467 } 22468 22469 // merely accessing the siblings will solve range issues in IE 22470 shouldNormalizeStart = (start.previousSibling && start.previousSibling.nodeType == 3) && 22471 (start.nextSibling && start.nextSibling.nodeType == 3); 22472 22473 shouldNormalizeEnd = (end.previousSibling && end.previousSibling.nodeType == 3) && 22474 (end.nextSibling && end.nextSibling.nodeType == 3); 22475 22476 shouldNormalize = shouldNormalizeStart && shouldNormalizeEnd; 22477 22478 start = start.nextSibling; 22479 end = end.previousSibling; 22480 22481 var collapsed = false; 22482 var collapsedToStart = false; 22483 // collapsed range 22484 if (start == that.end) { 22485 collapsedToStart = !!that.start.previousSibling; 22486 start = end = that.start.previousSibling || that.end.nextSibling; 22487 collapsed = true; 22488 } 22489 22490 dom.remove(that.start); 22491 dom.remove(that.end); 22492 22493 if (!start || !end) { 22494 range.selectNodeContents(range.commonAncestorContainer); 22495 range.collapse(true); 22496 return; 22497 } 22498 22499 var startOffset = collapsed ? isDataNode(start) ? start.nodeValue.length : start.childNodes.length : 0; 22500 var endOffset = isDataNode(end) ? end.nodeValue.length : end.childNodes.length; 22501 22502 if (start.nodeType == 3) { 22503 while (start.previousSibling && start.previousSibling.nodeType == 3) { 22504 start = start.previousSibling; 22505 startOffset += start.nodeValue.length; 22506 } 22507 } 22508 22509 if (end.nodeType == 3) { 22510 while (end.previousSibling && end.previousSibling.nodeType == 3) { 22511 end = end.previousSibling; 22512 endOffset += end.nodeValue.length; 22513 } 22514 } 22515 22516 var startIndex = findNodeIndex(start), startParent = start.parentNode; 22517 var endIndex = findNodeIndex(end), endParent = end.parentNode; 22518 22519 for (var startPointer = start; startPointer.previousSibling; startPointer = startPointer.previousSibling) { 22520 if (startPointer.nodeType == 3 && startPointer.previousSibling.nodeType == 3) { 22521 startIndex--; 22522 } 22523 } 22524 22525 for (var endPointer = end; endPointer.previousSibling; endPointer = endPointer.previousSibling) { 22526 if (endPointer.nodeType == 3 && endPointer.previousSibling.nodeType == 3) { 22527 endIndex--; 22528 } 22529 } 22530 22531 normalize(startParent); 22532 22533 if (start.nodeType == 3) { 22534 start = startParent.childNodes[startIndex]; 22535 } 22536 22537 normalize(endParent); 22538 if (end.nodeType == 3) { 22539 end = endParent.childNodes[endIndex]; 22540 } 22541 22542 if (collapsed) { 22543 if (start.nodeType == 3) { 22544 range.setStart(start, startOffset); 22545 } else { 22546 range[collapsedToStart ? 'setStartAfter' : 'setStartBefore'](start); 22547 } 22548 22549 range.collapse(true); 22550 22551 } else { 22552 if (start.nodeType == 3) { 22553 range.setStart(start, startOffset); 22554 } else { 22555 range.setStartBefore(start); 22556 } 22557 22558 if (end.nodeType == 3) { 22559 range.setEnd(end, endOffset); 22560 } else { 22561 range.setEndAfter(end); 22562 } 22563 } 22564 if (that.caret) { 22565 that.removeCaret(range); 22566 } 22567 } 22568 22569}); 22570 22571var boundary = /[\u0009-\u000d]|\u0020|\u00a0|\ufeff|\.|,|;|:|!|\(|\)|\?/; 22572 22573var RangeUtils = { 22574 nodes: function(range) { 22575 var nodes = RangeUtils.textNodes(range); 22576 if (!nodes.length) { 22577 range.selectNodeContents(range.commonAncestorContainer); 22578 nodes = RangeUtils.textNodes(range); 22579 if (!nodes.length) { 22580 nodes = dom.significantChildNodes(range.commonAncestorContainer); 22581 } 22582 } 22583 return nodes; 22584 }, 22585 22586 textNodes: function(range) { 22587 return new RangeEnumerator(range).enumerate(); 22588 }, 22589 22590 documentFromRange: function(range) { 22591 var startContainer = range.startContainer; 22592 return startContainer.nodeType == 9 ? startContainer : startContainer.ownerDocument; 22593 }, 22594 22595 createRange: function(document) { 22596 if ($.browser.msie && $.browser.version < 9) { 22597 return new W3CRange(document); 22598 } 22599 22600 return document.createRange(); 22601 }, 22602 22603 selectRange: function(range) { 22604 var image = RangeUtils.image(range); 22605 if (image) { 22606 range.setStartAfter(image); 22607 range.setEndAfter(image); 22608 } 22609 var selection = SelectionUtils.selectionFromRange(range); 22610 selection.removeAllRanges(); 22611 selection.addRange(range); 22612 }, 22613 22614 split: function(range, node, trim) { 22615 function partition(start) { 22616 var partitionRange = range.cloneRange(); 22617 partitionRange.collapse(start); 22618 partitionRange[start ? 'setStartBefore' : 'setEndAfter'](node); 22619 var contents = partitionRange.extractContents(); 22620 if (trim) { 22621 contents = dom.trim(contents); 22622 }
22623 dom[start ? 'insertBefore' : 'insertAfter'](contents, node); 22624 } 22625 partition(true); 22626 partition(false); 22627 }, 22628 22629 getMarkers: function(range) { 22630 var markers = []; 22631 22632 new RangeIterator(range).traverse(function (node) { 22633 if (node.className == 'k-marker') { 22634 markers.push(node); 22635 } 22636 }); 22637 22638 return markers; 22639 }, 22640 22641 image: function (range) { 22642 var nodes = []; 22643 22644 new RangeIterator(range).traverse(function (node) { 22645 if (dom.is(node, 'img')) { 22646 nodes.push(node); 22647 } 22648 }); 22649 22650 if (nodes.length == 1) { 22651 return nodes[0]; 22652 } 22653 }, 22654 22655 expand: function (range) { 22656 var result = range.cloneRange(); 22657 22658 var startContainer = result.startContainer.childNodes[result.startOffset === 0 ? 0 : result.startOffset - 1]; 22659 var endContainer = result.endContainer.childNodes[result.endOffset]; 22660 22661 if (!isDataNode(startContainer) || !isDataNode(endContainer)) { 22662 return result; 22663 } 22664 22665 var beforeCaret = startContainer.nodeValue; 22666 var afterCaret = endContainer.nodeValue; 22667 22668 if (!beforeCaret || !afterCaret) { 22669 return result; 22670 } 22671 22672 var startOffset = beforeCaret.split('').reverse().join('').search(boundary); 22673 var endOffset = afterCaret.search(boundary); 22674 22675 if (!startOffset || !endOffset) { 22676 return result; 22677 } 22678 22679 endOffset = endOffset == -1 ? afterCaret.length : endOffset; 22680 startOffset = startOffset == -1 ? 0 : beforeCaret.length - startOffset; 22681 22682 result.setStart(startContainer, startOffset); 22683 result.setEnd(endContainer, endOffset); 22684 22685 return result; 22686 }, 22687 22688 isExpandable: function (range) { 22689 var node = range.startContainer; 22690 var rangeDocument = RangeUtils.documentFromRange(range); 22691 22692 if (node == rangeDocument || node == rangeDocument.body) { 22693 return false; 22694 } 22695 22696 var result = range.cloneRange(); 22697 22698 var value = node.nodeValue; 22699 if (!value) { 22700 return false; 22701 } 22702 22703 var beforeCaret = value.substring(0, result.startOffset); 22704 var afterCaret = value.substring(result.startOffset); 22705 22706 var startOffset = 0, endOffset = 0; 22707 22708 if (beforeCaret) { 22709 startOffset = beforeCaret.split('').reverse().join('').search(boundary); 22710 } 22711 22712 if (afterCaret) { 22713 endOffset = afterCaret.search(boundary); 22714 } 22715 22716 return startOffset && endOffset; 22717 } 22718}; 22719 22720extend(Editor, { 22721 SelectionUtils: SelectionUtils, 22722 W3CRange: W3CRange, 22723 RangeIterator: RangeIterator, 22724 W3CSelection: W3CSelection, 22725 RangeEnumerator: RangeEnumerator, 22726 RestorePoint: RestorePoint, 22727 Marker: Marker, 22728 RangeUtils: RangeUtils 22729}); 22730 22731})(jQuery); 22732(function($) { 22733 22734 // Imports ================================================================ 22735 var kendo = window.kendo, 22736 Class = kendo.Class, 22737 Editor = kendo.ui.editor, 22738 EditorUtils = Editor.EditorUtils, 22739 registerTool = EditorUtils.registerTool, 22740 dom = Editor.Dom, 22741 RangeUtils = Editor.RangeUtils, 22742 selectRange = RangeUtils.selectRange, 22743 Tool = Editor.Tool, 22744 ToolTemplate = Editor.ToolTemplate, 22745 RestorePoint = Editor.RestorePoint, 22746 Marker = Editor.Marker, 22747 extend = $.extend; 22748 22749var Command = Class.extend({ 22750 init: function(options) { 22751 var that = this; 22752 that.options = options; 22753 that.restorePoint = new RestorePoint(options.range); 22754 that.marker = new Marker(); 22755 that.formatter = options.formatter; 22756 }, 22757 22758 getRange: function () { 22759 return this.restorePoint.toRange(); 22760 }, 22761 22762 lockRange: function (expand) { 22763 return this.marker.add(this.getRange(), expand); 22764 }, 22765 22766 releaseRange: function (range) { 22767 this.marker.remove(range); 22768 selectRange(range); 22769 }, 22770 22771 undo: function () { 22772 var point = this.restorePoint; 22773 point.body.innerHTML = point.html; 22774 selectRange(point.toRange()); 22775 }, 22776 22777 redo: function () { 22778 this.exec(); 22779 }, 22780 22781 exec: function () { 22782 var that = this, 22783 range = that.lockRange(true); 22784 that.formatter.editor = that.editor; 22785 that.formatter.toggle(range); 22786 that.releaseRange(range); 22787 } 22788}); 22789 22790var GenericCommand = Class.extend({ 22791 init: function(startRestorePoint, endRestorePoint) { 22792 this.body = startRestorePoint.body; 22793 this.startRestorePoint = startRestorePoint; 22794 this.endRestorePoint = endRestorePoint; 22795 }, 22796 22797 redo: function () { 22798 this.body.innerHTML = this.endRestorePoint.html; 22799 selectRange(this.endRestorePoint.toRange()); 22800 }, 22801 22802 undo: function () { 22803 this.body.innerHTML = this.startRestorePoint.html; 22804 selectRange(this.startRestorePoint.toRange()); 22805 } 22806}); 22807 22808var InsertHtmlCommand = Command.extend({ 22809 init: function(options) { 22810 Command.fn.init.call(this, options); 22811 22812 this.managesUndoRedo = true; 22813 }, 22814 22815 exec: function() { 22816 var editor = this.editor; 22817 var range = editor.getRange(); 22818 var startRestorePoint = new RestorePoint(range); 22819 22820 editor.clipboard.paste(this.options.value || ''); 22821 editor.undoRedoStack.push(new GenericCommand(startRestorePoint, new RestorePoint(editor.getRange()))); 22822 22823 editor.focus(); 22824 } 22825}); 22826 22827var InsertHtmlTool = Tool.extend({ 22828 initialize: function(ui, initOptions) { 22829 var editor = initOptions.editor; 22830 22831 new Editor.SelectBox(ui, { 22832 dataSource: editor.options.insertHtml || [], 22833 dataTextField: "text", 22834 dataValueField: "value", 22835 change: function (e) { 22836 Tool.exec(editor, 'insertHtml', this.value()); 22837 }, 22838 title: editor.options.localization.insertHtml, 22839 highlightFirst: false 22840 }); 22841 }, 22842 22843 command: function (commandArguments) { 22844 return new InsertHtmlCommand(commandArguments); 22845 }, 22846 22847 update: function(ui, nodes) { 22848 var selectbox = ui.data("kendoSelectBox") || ui.find("select").data("kendoSelectBox"); 22849 selectbox.close(); 22850 selectbox.value(selectbox.options.title); 22851 } 22852}); 22853 22854var UndoRedoStack = Class.extend({ 22855 init: function() { 22856 this.stack = []; 22857 this.currentCommandIndex = -1; 22858 }, 22859 22860 push: function (command) { 22861 var that = this; 22862 22863 that.stack = that.stack.slice(0, that.currentCommandIndex + 1); 22864 that.currentCommandIndex = that.stack.push(command) - 1; 22865 }, 22866 22867 undo: function () { 22868 if (this.canUndo()) { 22869 this.stack[this.currentCommandIndex--].undo(); 22870 } 22871 }, 22872 22873 redo: function () { 22874 if (this.canRedo()) { 22875 this.stack[++this.currentCommandIndex].redo(); 22876 } 22877 }, 22878 22879 canUndo: function () { 22880 return this.currentCommandIndex >= 0; 22881 }, 22882 22883 canRedo: function () { 22884 return this.currentCommandIndex != this.stack.length - 1; 22885 } 22886}); 22887 22888var TypingHandler = Class.extend({ 22889 init: function(editor) { 22890 this.editor = editor; 22891 }, 22892
22893 keydown: function (e) { 22894 var that = this, 22895 editor = that.editor, 22896 keyboard = editor.keyboard, 22897 isTypingKey = keyboard.isTypingKey(e); 22898 22899 if (isTypingKey && !keyboard.isTypingInProgress()) { 22900 var range = editor.getRange(); 22901 that.startRestorePoint = new RestorePoint(range); 22902 22903 keyboard.startTyping(function () { 22904 editor.selectionRestorePoint = that.endRestorePoint = new RestorePoint(editor.getRange()); 22905 editor.undoRedoStack.push(new GenericCommand(that.startRestorePoint, that.endRestorePoint)); 22906 }); 22907 22908 return true; 22909 } 22910 22911 return false; 22912 }, 22913 22914 keyup: function (e) { 22915 var keyboard = this.editor.keyboard; 22916 22917 if (keyboard.isTypingInProgress()) { 22918 keyboard.endTyping(); 22919 return true; 22920 } 22921 22922 return false; 22923 } 22924}); 22925 22926var SystemHandler = Class.extend({ 22927 init: function(editor) { 22928 this.editor = editor; 22929 this.systemCommandIsInProgress = false; 22930 }, 22931 22932 createUndoCommand: function () { 22933 var that = this; 22934 22935 that.endRestorePoint = new RestorePoint(that.editor.getRange()); 22936 that.editor.undoRedoStack.push(new GenericCommand(that.startRestorePoint, that.endRestorePoint)); 22937 that.startRestorePoint = that.endRestorePoint; 22938 }, 22939 22940 changed: function () { 22941 if (this.startRestorePoint) { 22942 return this.startRestorePoint.html != this.editor.body.innerHTML; 22943 } 22944 22945 return false; 22946 }, 22947 22948 keydown: function (e) { 22949 var that = this, 22950 editor = that.editor, 22951 keyboard = editor.keyboard; 22952 22953 if (keyboard.isModifierKey(e)) { 22954 22955 if (keyboard.isTypingInProgress()) { 22956 keyboard.endTyping(true); 22957 } 22958 22959 that.startRestorePoint = new RestorePoint(editor.getRange()); 22960 return true; 22961 } 22962 22963 if (keyboard.isSystem(e)) { 22964 that.systemCommandIsInProgress = true; 22965 22966 if (that.changed()) { 22967 that.systemCommandIsInProgress = false; 22968 that.createUndoCommand(); 22969 } 22970 22971 return true; 22972 } 22973 22974 return false; 22975 }, 22976 22977 keyup: function (e) { 22978 var that = this; 22979 22980 if (that.systemCommandIsInProgress && that.changed()) { 22981 that.systemCommandIsInProgress = false; 22982 that.createUndoCommand(e); 22983 return true; 22984 } 22985 22986 return false; 22987 } 22988}); 22989 22990var Keyboard = Class.extend({ 22991 init: function(handlers) { 22992 this.handlers = handlers; 22993 this.typingInProgress = false; 22994 }, 22995 22996 isCharacter: function(keyCode) { 22997 return (keyCode >= 48 && keyCode <= 90) || (keyCode >= 96 && keyCode <= 111) || 22998 (keyCode >= 186 && keyCode <= 192) || (keyCode >= 219 && keyCode <= 222); 22999 }, 23000 23001 toolFromShortcut: function (tools, e) { 23002 var key = String.fromCharCode(e.keyCode), 23003 toolName, 23004 toolOptions; 23005 23006 for (toolName in tools) { 23007 toolOptions = $.extend({ ctrl: false, alt: false, shift: false }, tools[toolName].options); 23008 23009 if ((toolOptions.key == key || toolOptions.key == e.keyCode) && 23010 toolOptions.ctrl == e.ctrlKey && 23011 toolOptions.alt == e.altKey && 23012 toolOptions.shift == e.shiftKey) { 23013 return toolName; 23014 } 23015 } 23016 }, 23017 23018 isTypingKey: function (e) { 23019 var keyCode = e.keyCode; 23020 return (this.isCharacter(keyCode) && !e.ctrlKey && !e.altKey) || 23021 keyCode == 32 || keyCode == 13 || keyCode == 8 || 23022 (keyCode == 46 && !e.shiftKey && !e.ctrlKey && !e.altKey); 23023 }, 23024 23025 isModifierKey: function (e) { 23026 var keyCode = e.keyCode; 23027 return (keyCode == 17 && !e.shiftKey && !e.altKey) || 23028 (keyCode == 16 && !e.ctrlKey && !e.altKey) || 23029 (keyCode == 18 && !e.ctrlKey && !e.shiftKey); 23030 }, 23031 23032 isSystem: function (e) { 23033 return e.keyCode == 46 && e.ctrlKey && !e.altKey && !e.shiftKey; 23034 }, 23035 23036 startTyping: function (callback) { 23037 this.onEndTyping = callback; 23038 this.typingInProgress = true; 23039 }, 23040 23041 stopTyping: function() { 23042 this.typingInProgress = false; 23043 if (this.onEndTyping) { 23044 this.onEndTyping(); 23045 } 23046 }, 23047 23048 endTyping: function (force) { 23049 var that = this; 23050 that.clearTimeout(); 23051 if (force) {
23052 that.stopTyping(); 23053 } else { 23054 that.timeout = window.setTimeout($.proxy(that.stopTyping, that), 1000); 23055 } 23056 }, 23057 23058 isTypingInProgress: function () { 23059 return this.typingInProgress; 23060 }, 23061 23062 clearTimeout: function () { 23063 window.clearTimeout(this.timeout); 23064 }, 23065 23066 notify: function(e, what) { 23067 var i, handlers = this.handlers; 23068 23069 for (i = 0; i < handlers.length; i++) { 23070 if (handlers[i][what](e)) { 23071 break; 23072 } 23073 } 23074 }, 23075 23076 keydown: function (e) { 23077 this.notify(e, 'keydown'); 23078 }, 23079 23080 keyup: function (e) { 23081 this.notify(e, 'keyup'); 23082 } 23083}); 23084 23085var Clipboard = Class.extend({ 23086 init: function(editor) { 23087 this.editor = editor; 23088 this.cleaners = [new MSWordFormatCleaner()]; 23089 }, 23090 23091 htmlToFragment: function(html) { 23092 var editor = this.editor, 23093 doc = editor.document, 23094 container = dom.create(doc, 'div'), 23095 fragment = doc.createDocumentFragment(); 23096 23097 container.innerHTML = html; 23098 23099 while (container.firstChild) { 23100 fragment.appendChild(container.firstChild); 23101 } 23102 23103 return fragment; 23104 }, 23105 23106 isBlock: function(html) { 23107 return (/<(div|p|ul|ol|table|h[1-6])/i).test(html); 23108 }, 23109 23110 oncut: function(e) { 23111 var editor = this.editor, 23112 startRestorePoint = new RestorePoint(editor.getRange()); 23113 setTimeout(function() { 23114 editor.undoRedoStack.push(new GenericCommand(startRestorePoint, new RestorePoint(editor.getRange()))); 23115 }); 23116 }, 23117 23118 onpaste: function(e) { 23119 var editor = this.editor, 23120 range = editor.getRange(), 23121 bom = "\ufeff", 23122 startRestorePoint = new RestorePoint(range), 23123 clipboardNode = dom.create(editor.document, 'div', {className:'k-paste-container', innerHTML: bom }); 23124 23125 editor.body.appendChild(clipboardNode); 23126 23127 if (editor.body.createTextRange) { 23128 e.preventDefault(); 23129 var r = editor.createRange(); 23130 r.selectNodeContents(clipboardNode); 23131 editor.selectRange(r); 23132 var textRange = editor.body.createTextRange(); 23133 textRange.moveToElementText(clipboardNode); 23134 $(editor.body).unbind('paste'); 23135 textRange.execCommand('Paste'); 23136 $(editor.body).bind('paste', $.proxy(arguments.callee, this)); 23137 } else { 23138 var clipboardRange = editor.createRange(); 23139 clipboardRange.selectNodeContents(clipboardNode); 23140 selectRange(clipboardRange); 23141 } 23142 23143 setTimeout(function() { 23144 var html, args = { html: "" }; 23145 23146 selectRange(range); 23147 dom.remove(clipboardNode); 23148 23149 if (clipboardNode.lastChild && dom.is(clipboardNode.lastChild, 'br')) { 23150 dom.remove(clipboardNode.lastChild); 23151 } 23152 23153 html = clipboardNode.innerHTML; 23154 23155 if (html != bom) { 23156 args.html = html; 23157 } 23158 23159 editor.trigger("paste", args); 23160 editor.clipboard.paste(args.html, true); 23161 editor.undoRedoStack.push(new GenericCommand(startRestorePoint, new RestorePoint(editor.getRange()))); 23162 Editor.EditorUtils.select(editor); 23163 }); 23164 }, 23165 23166 splittableParent: function(block, node) { 23167 var parentNode, body; 23168 23169 if (block) { 23170 return dom.parentOfType(node, ['p', 'ul', 'ol']) || node.parentNode; 23171 } 23172 23173 parentNode = node.parentNode; 23174 body = node.ownerDocument.body; 23175 23176 if (dom.isInline(parentNode)) { 23177 while (parentNode.parentNode != body && !dom.isBlock(parentNode.parentNode)) { 23178 parentNode = parentNode.parentNode; 23179 } 23180 } 23181 23182 return parentNode; 23183 }, 23184 23185 paste: function (html, clean) { 23186 var editor = this.editor, 23187 i, l; 23188 23189 for (i = 0, l = this.cleaners.length; i < l; i++) { 23190 if (this.cleaners[i].applicable(html)) { 23191 html = this.cleaners[i].clean(html); 23192 } 23193 } 23194 23195 if (clean) { 23196 // remove br elements which immediately precede block elements 23197 html = html.replace(/(<br>(\s| )*)+(<\/?(div|p|li|col|t))/ig, "$3"); 23198 // remove empty inline elements 23199 html = html.replace(/<(a|span)[^>]*><\/\1>/ig, ""); 23200 } 23201 23202 // It is possible in IE to copy just <li> tags 23203 html = html.replace(/^<li/i, '<ul><li').replace(/li>$/g, 'li></ul>'); 23204 23205 var block = this.isBlock(html); 23206 23207 var range = editor.getRange(); 23208 range.deleteContents(); 23209 23210 if (range.startContainer == editor.document) { 23211 range.selectNodeContents(editor.body); 23212 } 23213 23214 var marker = new Marker(); 23215 var caret = marker.addCaret(range); 23216 23217 var parent = this.splittableParent(block, caret); 23218 var unwrap = false; 23219 23220 if (!/body|td/.test(dom.name(parent)) && (block || dom.isInline(parent))) { 23221 range.selectNode(caret); 23222 RangeUtils.split(range, parent, true); 23223 unwrap = true; 23224 } 23225 23226 var fragment = this.htmlToFragment(html); 23227 23228 if (fragment.firstChild && fragment.firstChild.className === "k-paste-container") { 23229 var fragmentsHtml = []; 23230 for (i = 0, l = fragment.childNodes.length; i < l; i++) { 23231 fragmentsHtml.push(fragment.childNodes[i].innerHTML); 23232 } 23233 23234 fragment = this.htmlToFragment(fragmentsHtml.join('<br />')); 23235 } 23236
23237 range.insertNode(fragment); 23238 23239 parent = this.splittableParent(block, caret); 23240 if (unwrap) { 23241 while (caret.parentNode != parent) { 23242 dom.unwrap(caret.parentNode); 23243 } 23244 23245 dom.unwrap(caret.parentNode); 23246 } 23247 23248 dom.normalize(range.commonAncestorContainer); 23249 caret.style.display = 'inline'; 23250 dom.scrollTo(caret); 23251 marker.removeCaret(range); 23252 selectRange(range); 23253 } 23254}); 23255 23256var MSWordFormatCleaner = Class.extend({ 23257 init: function() { 23258 this.replacements = [ 23259 /<\?xml[^>]*>/gi, '', 23260 /<!--(.|\n)*?-->/g, '', /* comments */ 23261 /"/g, "'", /* encoded quotes (in attributes) */ 23262 /(?:<br> [\s\r\n]+|<br>)*(<\/?(h[1-6]|hr|p|div|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|address|pre|form|blockquote|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br> [\s\r\n]+|<br>)*/g, '$1', 23263 /<br><br>/g, '<BR><BR>', 23264 /<br>/g, ' ', 23265 /<table([^>]*)>(\s| )+<t/gi, '<table$1><t', 23266 /<tr[^>]*>(\s| )*<\/tr>/gi, '', 23267 /<tbody[^>]*>(\s| )*<\/tbody>/gi, '', 23268 /<table[^>]*>(\s| )*<\/table>/gi, '', 23269 /<BR><BR>/g, '<br>', 23270 /^\s*( )+/gi, '', 23271 /( |<br[^>]*>)+\s*$/gi, '', 23272 /mso-[^;"]*;?/ig, '', /* office-related CSS attributes */ 23273 /<(\/?)b(\s[^>]*)?>/ig, '<$1strong$2>', 23274 /<(\/?)i(\s[^>]*)?>/ig, '<$1em$2>', 23275 /<\/?(meta|link|style|o:|v:|x:)[^>]*>((?:.|\n)*?<\/(meta|link|style|o:|v:|x:)[^>]*>)?/ig, '', /* external references and namespaced tags */ 23276 /style=(["|'])\s*\1/g, '' /* empty style attributes */ 23277 ]; 23278 }, 23279 23280 applicable: function(html) { 23281 return (/class="?Mso|style="[^"]*mso-/i).test(html); 23282 }, 23283 23284 listType: function(html) { 23285 if (/^[\u2022\u00b7\u00a7\u00d8o]\u00a0+/.test(html)) { 23286 return 'ul'; 23287 } 23288 23289 if (/^\s*\w+[\.\)]\u00a0{2,}/.test(html)) { 23290 return 'ol'; 23291 } 23292 }, 23293 23294 lists: function(html) { 23295 var placeholder = dom.create(document, 'div', {innerHTML: html}); 23296 var blockChildren = $(dom.blockElements.join(','), placeholder); 23297 23298 var lastMargin = -1, lastType, levels = {'ul':{}, 'ol':{}}, li = placeholder; 23299 23300 for (var i = 0; i < blockChildren.length; i++) { 23301 var p = blockChildren[i]; 23302 html = p.innerHTML.replace(/<\/?\w+[^>]*>/g, '').replace(/ /g, '\u00a0'); 23303 var type = this.listType(html); 23304 23305 if (!type || dom.name(p) != 'p') { 23306 if (!p.innerHTML) { 23307 dom.remove(p); 23308 } else { 23309 levels = {'ul':{}, 'ol':{}}; 23310 li = placeholder; 23311 lastMargin = -1; 23312 } 23313 continue; 23314 } 23315 23316 var margin = parseFloat(p.style.marginLeft || 0); 23317 var list = levels[type][margin]; 23318 23319 if (margin > lastMargin || !list) { 23320 list = dom.create(document, type); 23321 23322 if (li == placeholder) { 23323 dom.insertBefore(list, p); 23324 } else { 23325 li.appendChild(list); 23326 } 23327 23328 levels[type][margin] = list; 23329 } 23330 23331 if (lastType != type) { 23332 for (var key in levels) { 23333 for (var child in levels[key]) { 23334 if ($.contains(list, levels[key][child])) { 23335 delete levels[key][child]; 23336 } 23337 } 23338 } 23339 } 23340 23341 dom.remove(p.firstChild); 23342 li = dom.create(document, 'li', {innerHTML:p.innerHTML}); 23343 list.appendChild(li); 23344 dom.remove(p); 23345 lastMargin = margin; 23346 lastType = type; 23347 } 23348 return placeholder.innerHTML; 23349 }, 23350 23351 stripEmptyAnchors: function(html) { 23352 return html.replace(/<a([^>]*)>\s*<\/a>/ig, function(a, attributes) { 23353 if (!attributes || attributes.indexOf("href") < 0) { 23354 return ""; 23355 } 23356 23357 return a; 23358 }); 23359 }, 23360 23361 clean: function(html) { 23362 var that = this, 23363 replacements = that.replacements, 23364 i, l; 23365 23366 for (i = 0, l = replacements.length; i < l; i += 2) { 23367 html = html.replace(replacements[i], replacements[i+1]); 23368 } 23369 23370 html = that.stripEmptyAnchors(html); 23371 html = that.lists(html); 23372 html = html.replace(/\s+class="?[^"\s>]*"?/ig, ''); 23373 23374 return html; 23375 } 23376}); 23377 23378extend(Editor, { 23379 Command: Command, 23380 GenericCommand: GenericCommand, 23381 InsertHtmlCommand: InsertHtmlCommand, 23382 InsertHtmlTool: InsertHtmlTool, 23383 UndoRedoStack: UndoRedoStack, 23384 TypingHandler: TypingHandler, 23385 SystemHandler: SystemHandler, 23386 Keyboard: Keyboard, 23387 Clipboard: Clipboard, 23388 MSWordFormatCleaner: MSWordFormatCleaner 23389}); 23390 23391registerTool("insertHtml", new InsertHtmlTool({template: new ToolTemplate({template: EditorUtils.dropDownListTemplate, title: "Insert HTML", initialValue: "Insert HTML"})})); 23392 23393})(jQuery); 23394(function($) { 23395 23396var kendo = window.kendo, 23397 Class = kendo.Class, 23398 Editor = kendo.ui.editor, 23399 formats = kendo.ui.Editor.fn.options.formats, 23400 EditorUtils = Editor.EditorUtils, 23401 Tool = Editor.Tool, 23402 ToolTemplate = Editor.ToolTemplate, 23403 FormatTool = Editor.FormatTool, 23404 dom = Editor.Dom, 23405 RangeUtils = Editor.RangeUtils, 23406 extend = $.extend, 23407 registerTool = Editor.EditorUtils.registerTool, 23408 registerFormat = Editor.EditorUtils.registerFormat, 23409 KMARKER = "k-marker"; 23410 23411var InlineFormatFinder = Class.extend({ 23412 init: function(format) { 23413 this.format = format; 23414 }, 23415 23416 numberOfSiblings: function(referenceNode) { 23417 var textNodesCount = 0, 23418 elementNodesCount = 0, 23419 markerCount = 0, 23420 parentNode = referenceNode.parentNode, 23421 node; 23422 23423 for (node = parentNode.firstChild; node; node = node.nextSibling) { 23424 if (node != referenceNode) { 23425 if (node.className == KMARKER) { 23426 markerCount++; 23427 } else if (node.nodeType == 3) { 23428 textNodesCount++; 23429 } else { 23430 elementNodesCount++; 23431 } 23432 } 23433 } 23434 23435 if (markerCount > 1 && parentNode.firstChild.className == KMARKER && parentNode.lastChild.className == KMARKER) { 23436 // full node selection 23437 return 0; 23438 } else { 23439 return elementNodesCount + textNodesCount; 23440 } 23441 }, 23442 23443 findSuitable: function (sourceNode, skip) { 23444 if (!skip && this.numberOfSiblings(sourceNode) > 0) { 23445 return null; 23446 } 23447 23448 return dom.parentOfType(sourceNode, this.format[0].tags); 23449 }, 23450 23451 findFormat: function (sourceNode) { 23452 var format = this.format, 23453 attrEquals = dom.attrEquals, 23454 i, len, node, tags, attributes; 23455 23456 for (i = 0, len = format.length; i < len; i++) { 23457 node = sourceNode; 23458 tags = format[i].tags;
23459 attributes = format[i].attr; 23460 23461 if (node && dom.ofType(node, tags) && attrEquals(node, attributes)) { 23462 return node; 23463 } 23464 23465 while (node) { 23466 node = dom.parentOfType(node, tags); 23467 if (node && attrEquals(node, attributes)) { 23468 return node; 23469 } 23470 } 23471 } 23472 23473 return null; 23474 }, 23475 23476 isFormatted: function (nodes) { 23477 var i, len; 23478 23479 for (i = 0, len = nodes.length; i < len; i++) { 23480 if (this.findFormat(nodes[i])) { 23481 return true; 23482 } 23483 } 23484 return false; 23485 } 23486}); 23487 23488var InlineFormatter = Class.extend({ 23489 init: function(format, values) { 23490 var that = this; 23491 that.finder = new InlineFormatFinder(format); 23492 that.attributes = extend({}, format[0].attr, values); 23493 that.tag = format[0].tags[0]; 23494 }, 23495 23496 wrap: function(node) { 23497 return dom.wrap(node, dom.create(node.ownerDocument, this.tag, this.attributes)); 23498 }, 23499 23500 activate: function(range, nodes) { 23501 var that = this; 23502 if (that.finder.isFormatted(nodes)) { 23503 that.split(range); 23504 that.remove(nodes); 23505 } else { 23506 that.apply(nodes); 23507 } 23508 }, 23509 23510 toggle: function (range) { 23511 var nodes = RangeUtils.textNodes(range); 23512 23513 if (nodes.length > 0) { 23514 this.activate(range, nodes); 23515 } 23516 }, 23517 23518 apply: function (nodes) { 23519 var that = this, 23520 formatNodes = [], 23521 i, l, node, formatNode; 23522 23523 for (i = 0, l = nodes.length; i < l; i++) { 23524 node = nodes[i]; 23525 formatNode = that.finder.findSuitable(node); 23526 if (formatNode) { 23527 dom.attr(formatNode, that.attributes); 23528 } else { 23529 formatNode = that.wrap(node); 23530 } 23531 23532 formatNodes.push(formatNode); 23533 } 23534 23535 that.consolidate(formatNodes); 23536 }, 23537 23538 remove: function (nodes) { 23539 var that = this, 23540 i, l, formatNode; 23541 23542 for (i = 0, l = nodes.length; i < l; i++) { 23543 formatNode = that.finder.findFormat(nodes[i]); 23544 if (formatNode) { 23545 if (that.attributes && that.attributes.style) { 23546 dom.unstyle(formatNode, that.attributes.style); 23547 if (!formatNode.style.cssText) { 23548 dom.unwrap(formatNode); 23549 } 23550 } else { 23551 dom.unwrap(formatNode); 23552 } 23553 } 23554 } 23555 }, 23556 23557 split: function (range) { 23558 var nodes = RangeUtils.textNodes(range), 23559 l = nodes.length, 23560 i, formatNode; 23561 23562 if (l > 0) { 23563 for (i = 0; i < l; i++) { 23564 formatNode = this.finder.findFormat(nodes[i]); 23565 if (formatNode) { 23566 RangeUtils.split(range, formatNode, true); 23567 } 23568 } 23569 } 23570 }, 23571 23572 consolidate: function (nodes) { 23573 var node, last; 23574 23575 while (nodes.length > 1) { 23576 node = nodes.pop(); 23577 last = nodes[nodes.length - 1]; 23578 23579 if (node.previousSibling && node.previousSibling.className == KMARKER) { 23580 last.appendChild(node.previousSibling); 23581 } 23582 23583 if (node.tagName == last.tagName && node.previousSibling == last && node.style.cssText == last.style.cssText) { 23584 while (node.firstChild) { 23585 last.appendChild(node.firstChild); 23586 } 23587 dom.remove(node); 23588 } 23589 } 23590 } 23591}); 23592 23593var GreedyInlineFormatFinder = InlineFormatFinder.extend({ 23594 init: function(format, greedyProperty) { 23595 var that = this; 23596 that.format = format; 23597 that.greedyProperty = greedyProperty; 23598 InlineFormatFinder.fn.init.call(that, format); 23599 }, 23600 23601 getInlineCssValue: function(node) { 23602 var attributes = node.attributes, 23603 trim = $.trim, 23604 i, l, attribute, name, attributeValue, css, pair, cssIndex, len, propertyAndValue, property, value; 23605 23606 if (!attributes) { 23607 return; 23608 } 23609 23610 for (i = 0, l = attributes.length; i < l; i++) { 23611 attribute = attributes[i];
23612 name = attribute.nodeName; 23613 attributeValue = attribute.nodeValue; 23614 23615 if (attribute.specified && name == "style") { 23616 23617 css = trim(attributeValue || node.style.cssText).split(";"); 23618 23619 for (cssIndex = 0, len = css.length; cssIndex < len; cssIndex++) { 23620 pair = css[cssIndex]; 23621 if (pair.length) { 23622 propertyAndValue = pair.split(":"); 23623 property = trim(propertyAndValue[0].toLowerCase()); 23624 value = trim(propertyAndValue[1]); 23625 23626 if (property != this.greedyProperty) { 23627 continue; 23628 } 23629 23630 return property.indexOf("color") >= 0 ? dom.toHex(value) : value; 23631 } 23632 } 23633 } 23634 } 23635 23636 return; 23637 }, 23638 23639 getFormatInner: function (node) { 23640 var $node = $(dom.isDataNode(node) ? node.parentNode : node), 23641 parents = $node.parents().andSelf(), 23642 i, len, value; 23643 23644 for (i = 0, len = parents.length; i < len; i++) { 23645 value = this.greedyProperty == "className" ? parents[i].className : this.getInlineCssValue(parents[i]); 23646 if (value) { 23647 return value; 23648 } 23649 } 23650 23651 return "inherit"; 23652 }, 23653 23654 getFormat: function (nodes) { 23655 var result = this.getFormatInner(nodes[0]), 23656 i, len; 23657 23658 for (i = 1, len = nodes.length; i < len; i++) { 23659 if (result != this.getFormatInner(nodes[i])) { 23660 return ""; 23661 } 23662 } 23663 23664 return result; 23665 }, 23666 23667 isFormatted: function (nodes) { 23668 return this.getFormat(nodes) !== ""; 23669 } 23670}); 23671 23672var GreedyInlineFormatter = InlineFormatter.extend({ 23673 init: function(format, values, greedyProperty) { 23674 var that = this; 23675 23676 InlineFormatter.fn.init.call(that, format, values); 23677 23678 that.greedyProperty = greedyProperty; 23679 that.values = values; 23680 that.finder = new GreedyInlineFormatFinder(format, greedyProperty); 23681 }, 23682 23683 activate: function(range, nodes) { 23684 var that = this; 23685 that.split(range); 23686 23687 if (that.greedyProperty) { 23688 var camelCase = that.greedyProperty.replace(/-([a-z])/, function(all, letter) { return letter.toUpperCase(); }); 23689 that[that.values.style[camelCase] == "inherit" ? "remove" : "apply"](nodes); 23690 } else { 23691 that.apply(nodes); 23692 } 23693 } 23694}); 23695 23696function inlineFormatWillDelayExecution (range) { 23697 return range.collapsed && !RangeUtils.isExpandable(range); 23698} 23699 23700var InlineFormatTool = FormatTool.extend({ 23701 init: function(options) { 23702 FormatTool.fn.init.call(this, extend(options, { 23703 finder: new InlineFormatFinder(options.format), 23704 formatter: function () { return new InlineFormatter(options.format); } 23705 })); 23706 23707 this.willDelayExecution = inlineFormatWillDelayExecution; 23708 } 23709}); 23710 23711var FontTool = Tool.extend({ 23712 init: function(options) { 23713 var that = this; 23714 Tool.fn.init.call(that, options); 23715 23716 // IE has single selection hence we are using select box instead of combobox 23717 that.options = options; 23718 that.type = ($.browser.msie || kendo.support.touch) ? "kendoDropDownList" : "kendoComboBox"; 23719 that.format = [{ tags: ["span"] }], 23720 that.finder = new GreedyInlineFormatFinder(that.format, options.cssAttr); 23721 }, 23722 23723 command: function (commandArguments) { 23724 var options = this.options, 23725 format = this.format, 23726 style = {}; 23727 23728 return new Editor.FormatCommand(extend(commandArguments, { 23729 formatter: function () { 23730 style[options.domAttr] = commandArguments.value; 23731 23732 return new GreedyInlineFormatter(format, { style: style }, options.cssAttr); 23733 } 23734 })); 23735 }, 23736 23737 willDelayExecution: inlineFormatWillDelayExecution, 23738 23739 update: function(ui, nodes, pendingFormats) { 23740 var that = this, 23741 list = ui.data(that.type), 23742 pendingFormat = pendingFormats.getPending(that.name), 23743 format = (pendingFormat && pendingFormat.params) ? pendingFormat.params.value : that.finder.getFormat(nodes); 23744 23745 list.close(); 23746 list.value(format); 23747 }, 23748 23749 initialize: function (ui, initOptions) { 23750 var editor = initOptions.editor, 23751 toolName = this.options.name; 23752 23753 ui[this.type]({ 23754 dataTextField: "Text", 23755 dataValueField: "Value", 23756 dataSource: editor.options[toolName], 23757 change: function (e) { 23758 Tool.exec(editor, toolName, this.value()); 23759 }, 23760 highlightFirst: false 23761 }); 23762 23763 ui.closest(".k-widget").removeClass("k-" + toolName).find("*").andSelf().attr("unselectable", "on"); 23764 23765 ui.data(this.type).value("inherit"); 23766 } 23767 23768}); 23769 23770var ColorTool = Tool.extend({ 23771 init: function(options) { 23772 Tool.fn.init.call(this, options); 23773 23774 this.options = options; 23775 this.format = [{ tags: dom.inlineElements }]; 23776 }, 23777 23778 update: function(ui) { 23779 ui.data("kendoColorPicker").close(); 23780 }, 23781 23782 command: function (commandArguments) { 23783 var options = this.options, 23784 format = this.format, 23785 style = {}; 23786 23787 return new Editor.FormatCommand(extend(commandArguments, { 23788 formatter: function () { 23789 style[options.domAttr] = commandArguments.value; 23790 23791 return new GreedyInlineFormatter(format, { style: style }, options.cssAttr); 23792 } 23793 })); 23794 }, 23795 23796 willDelayExecution: inlineFormatWillDelayExecution, 23797 23798 initialize: function(ui, initOptions) { 23799 var editor = initOptions.editor, 23800 toolName = this.name; 23801 23802 new Editor.ColorPicker(ui, { 23803 value: "#000000", 23804 change: function (e) { 23805 Tool.exec(editor, toolName, e.value); 23806 } 23807 }); 23808 } 23809}); 23810 23811var StyleTool = Tool.extend({ 23812 init: function(options) { 23813 var that = this; 23814 Tool.fn.init.call(that, options); 23815 23816 that.format = [{ tags: ["span"] }]; 23817 that.finder = new GreedyInlineFormatFinder(that.format, "className"); 23818 }, 23819 23820 command: function (commandArguments) { 23821 return new Editor.FormatCommand(extend(commandArguments, { 23822 formatter: function () { 23823 return new GreedyInlineFormatter(this.format, { className: commandArguments.value });
23824 } 23825 })); 23826 }, 23827 23828 update: function(ui, nodes) { 23829 var list = ui.data("kendoDropDownList"); 23830 list.close(); 23831 list.value(this.finder.getFormat(nodes)); 23832 }, 23833 23834 initiliaze: function(ui, initOptions) { 23835 var editor = initOptions.editor; 23836 23837 ui.kendoDropDownList({ 23838 data: editor.style, 23839 title: editor.options.localization.style, 23840 itemCreate: function (e) { 23841 var style = dom.inlineStyle(editor.document, "span", {className : e.dataItem.Value}); 23842 23843 e.html = '<span unselectable="on" style="display:block;' + style +'">' + e.html + '</span>'; 23844 }, 23845 change: function (e) { 23846 Tool.exec(editor, "style", e.value); 23847 } 23848 }); 23849 } 23850 23851}); 23852 23853extend(Editor, { 23854 InlineFormatFinder: InlineFormatFinder, 23855 InlineFormatter: InlineFormatter, 23856 GreedyInlineFormatFinder: GreedyInlineFormatFinder, 23857 GreedyInlineFormatter: GreedyInlineFormatter, 23858 InlineFormatTool: InlineFormatTool, 23859 FontTool: FontTool, 23860 ColorTool: ColorTool, 23861 StyleTool: StyleTool 23862}); 23863 23864registerTool("style", new Editor.StyleTool({template: new ToolTemplate({template: EditorUtils.dropDownListTemplate, title: "Indent", initialValue: "Styles"})})); 23865 23866registerFormat("bold", [ { tags: ["strong"] }, { tags: ["span"], attr: { style: { fontWeight: "bold"}} } ]); 23867registerTool("bold", new InlineFormatTool({ key: "B", ctrl: true, format: formats.bold, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Bold"}) })); 23868 23869registerFormat("italic", [ { tags: ["em"] }, { tags: ["span"], attr: { style: { fontStyle: "italic"}} } ]); 23870registerTool("italic", new InlineFormatTool({ key: "I", ctrl: true, format: formats.italic, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Italic"})})); 23871 23872registerFormat("underline", [ { tags: ["span"], attr: { style: { textDecoration: "underline"}} } ]); 23873registerTool("underline", new InlineFormatTool({ key: "U", ctrl: true, format: formats.underline, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Underline"})})); 23874 23875registerFormat("strikethrough", [ { tags: ["del"] }, { tags: ["span"], attr: { style: { textDecoration: "line-through"}} } ]); 23876registerTool("strikethrough", new InlineFormatTool({format: formats.strikethrough, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Strikethrough"})})); 23877 23878registerFormat("superscript", [ { tags: ["sup"] } ]); 23879registerTool("superscript", new InlineFormatTool({format: formats.superscript, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Superscript"})})); 23880 23881registerFormat("subscript", [ { tags: ["sub"] } ]); 23882registerTool("subscript", new InlineFormatTool({format: formats.subscript, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Subscript"})})); 23883 23884registerTool("foreColor", new ColorTool({cssAttr:"color", domAttr:"color", name:"foreColor", template: new ToolTemplate({template: EditorUtils.colorPickerTemplate, title: "Color"})})); 23885 23886registerTool("backColor", new ColorTool({cssAttr:"background-color", domAttr: "backgroundColor", name:"backColor", template: new ToolTemplate({template: EditorUtils.colorPickerTemplate, title: "Background Color"})})); 23887 23888registerTool("fontName", new FontTool({cssAttr:"font-family", domAttr: "fontFamily", name:"fontName", template: new ToolTemplate({template: EditorUtils.comboBoxTemplate, title: "Font Name", initialValue: "(inherited font)"})})); 23889 23890registerTool("fontSize", new FontTool({cssAttr:"font-size", domAttr:"fontSize", name:"fontSize", template: new ToolTemplate({template: EditorUtils.comboBoxTemplate, title: "Font Size", initialValue: "(inherited size)"})})); 23891 23892})(jQuery); 23893(function($) { 23894 23895var kendo = window.kendo, 23896 Class = kendo.Class, 23897 extend = $.extend, 23898 Editor = kendo.ui.editor, 23899 formats = kendo.ui.Editor.fn.options.formats, 23900 dom = Editor.Dom, 23901 Command = Editor.Command, 23902 Tool = Editor.Tool, 23903 ToolTemplate = Editor.ToolTemplate, 23904 FormatTool = Editor.FormatTool, 23905 EditorUtils = Editor.EditorUtils, 23906 registerTool = EditorUtils.registerTool, 23907 registerFormat = EditorUtils.registerFormat, 23908 RangeUtils = Editor.RangeUtils; 23909 23910var BlockFormatFinder = Class.extend({ 23911 init: function(format) { 23912 this.format = format; 23913 }, 23914 23915 contains: function(node, children) { 23916 var i, len, child; 23917 23918 for (i = 0, len = children.length; i < len; i++) { 23919 child = children[i]; 23920 if (!child || !dom.isAncestorOrSelf(node, child)) { 23921 return false; 23922 } 23923 } 23924 23925 return true; 23926 }, 23927 23928 findSuitable: function (nodes) { 23929 var format = this.format, 23930 suitable = [], 23931 i, len, candidate; 23932 23933 for (i = 0, len = nodes.length; i < len; i++) { 23934 candidate = dom.ofType(nodes[i], format[0].tags) ? nodes[i] : dom.parentOfType(nodes[i], format[0].tags); 23935 if (!candidate) { 23936 return []; 23937 } 23938 23939 if ($.inArray(candidate, suitable) < 0) { 23940 suitable.push(candidate); 23941 } 23942 } 23943 23944 for (i = 0, len = suitable.length; i < len; i++) { 23945 if (this.contains(suitable[i], suitable)) { 23946 return [suitable[i]]; 23947 } 23948 } 23949 23950 return suitable; 23951 }, 23952 23953 findFormat: function (sourceNode) { 23954 var format = this.format, 23955 i, len, node, tags, attributes; 23956 23957 for (i = 0, len = format.length; i < len; i++) { 23958 node = sourceNode; 23959 tags = format[i].tags;
23960 attributes = format[i].attr; 23961 23962 while (node) { 23963 if (dom.ofType(node, tags) && dom.attrEquals(node, attributes)) { 23964 return node; 23965 } 23966 23967 node = node.parentNode; 23968 } 23969 } 23970 return null; 23971 }, 23972 23973 getFormat: function (nodes) { 23974 var that = this, 23975 findFormat = function(node) { 23976 return that.findFormat(dom.isDataNode(node) ? node.parentNode : node); 23977 }, 23978 result = findFormat(nodes[0]), 23979 i, len; 23980 23981 if (!result) { 23982 return ""; 23983 } 23984 23985 for (i = 1, len = nodes.length; i < len; i++) { 23986 if (result != findFormat(nodes[i])) { 23987 return ""; 23988 } 23989 } 23990 23991 return result.nodeName.toLowerCase(); 23992 }, 23993 23994 isFormatted: function (nodes) { 23995 for (var i = 0, len = nodes.length; i < len; i++) { 23996 if (!this.findFormat(nodes[i])) { 23997 return false; 23998 } 23999 } 24000 24001 return true; 24002 } 24003}); 24004 24005var BlockFormatter = Class.extend({ 24006 init: function (format, values) { 24007 this.format = format; 24008 this.values = values; 24009 this.finder = new BlockFormatFinder(format); 24010 }, 24011 24012 wrap: function(tag, attributes, nodes) { 24013 var commonAncestor = nodes.length == 1 ? dom.blockParentOrBody(nodes[0]) : dom.commonAncestor.apply(null, nodes); 24014 24015 if (dom.isInline(commonAncestor)) { 24016 commonAncestor = dom.blockParentOrBody(commonAncestor); 24017 } 24018 24019 var ancestors = dom.significantChildNodes(commonAncestor), 24020 position = dom.findNodeIndex(ancestors[0]), 24021 wrapper = dom.create(commonAncestor.ownerDocument, tag, attributes), 24022 i, ancestor; 24023 24024 for (i = 0; i < ancestors.length; i++) { 24025 ancestor = ancestors[i]; 24026 if (dom.isBlock(ancestor)) { 24027 dom.attr(ancestor, attributes); 24028 24029 if (wrapper.childNodes.length) { 24030 dom.insertBefore(wrapper, ancestor); 24031 wrapper = wrapper.cloneNode(false); 24032 } 24033 24034 position = dom.findNodeIndex(ancestor) + 1; 24035 24036 continue; 24037 } 24038 24039 wrapper.appendChild(ancestor); 24040 } 24041 24042 if (wrapper.firstChild) { 24043 dom.insertAt(commonAncestor, wrapper, position); 24044 } 24045 }, 24046 24047 apply: function (nodes) { 24048 var that = this, 24049 formatNodes = dom.is(nodes[0], "img") ? [nodes[0]] : that.finder.findSuitable(nodes), 24050 formatToApply = formatNodes.length ? EditorUtils.formatByName(dom.name(formatNodes[0]), that.format) : that.format[0], 24051 tag = formatToApply.tags[0], 24052 attributes = extend({}, formatToApply.attr, that.values), 24053 i, len; 24054 24055 if (formatNodes.length) { 24056 for (i = 0, len = formatNodes.length; i < len; i++) { 24057 dom.attr(formatNodes[i], attributes); 24058 } 24059 } else { 24060 that.wrap(tag, attributes, nodes); 24061 } 24062 }, 24063 24064 remove: function (nodes) { 24065 var i, l, formatNode, namedFormat; 24066 24067 for (i = 0, l = nodes.length; i < l; i++) { 24068 formatNode = this.finder.findFormat(nodes[i]); 24069 if (formatNode) { 24070 if (dom.ofType(formatNode, ["p", "img", "li"])) { 24071 namedFormat = EditorUtils.formatByName(dom.name(formatNode), this.format); 24072 if (namedFormat.attr.style) { 24073 dom.unstyle(formatNode, namedFormat.attr.style); 24074 } 24075 if (namedFormat.attr.className) { 24076 dom.removeClass(formatNode, namedFormat.attr.className); 24077 } 24078 } else { 24079 dom.unwrap(formatNode); 24080 } 24081 } 24082 } 24083 }, 24084 24085 toggle: function (range) { 24086 var that = this, 24087 nodes = RangeUtils.nodes(range); 24088 24089 if (that.finder.isFormatted(nodes)) { 24090 that.remove(nodes); 24091 } else {
24092 that.apply(nodes); 24093 } 24094 } 24095}); 24096 24097var GreedyBlockFormatter = Class.extend({ 24098 init: function (format, values) { 24099 var that = this; 24100 that.format = format; 24101 that.values = values; 24102 that.finder = new BlockFormatFinder(format); 24103 }, 24104 24105 apply: function (nodes) { 24106 var format = this.format, 24107 blocks = dom.blockParents(nodes), 24108 formatTag = format[0].tags[0], 24109 i, len, list, formatter, range; 24110 24111 if (blocks.length) { 24112 for (i = 0, len = blocks.length; i < len; i++) { 24113 if (dom.is(blocks[i], "li")) { 24114 list = blocks[i].parentNode; 24115 formatter = new Editor.ListFormatter(list.nodeName.toLowerCase(), formatTag); 24116 range = this.editor.createRange(); 24117 range.selectNode(blocks[i]); 24118 formatter.toggle(range); 24119 } else { 24120 dom.changeTag(blocks[i], formatTag); 24121 } 24122 } 24123 } else { 24124 new BlockFormatter(format, this.values).apply(nodes); 24125 } 24126 }, 24127 24128 toggle: function (range) { 24129 var nodes = RangeUtils.textNodes(range); 24130 if (!nodes.length) { 24131 range.selectNodeContents(range.commonAncestorContainer); 24132 nodes = RangeUtils.textNodes(range); 24133 if (!nodes.length) { 24134 nodes = dom.significantChildNodes(range.commonAncestorContainer); 24135 } 24136 } 24137 24138 this.apply(nodes); 24139 } 24140}); 24141 24142var FormatCommand = Command.extend({ 24143 init: function (options) { 24144 options.formatter = options.formatter(); 24145 Command.fn.init.call(this, options); 24146 } 24147}); 24148 24149var BlockFormatTool = FormatTool.extend({ 24150 init: function (options) { 24151 FormatTool.fn.init.call(this, extend(options, { 24152 finder: new BlockFormatFinder(options.format), 24153 formatter: function () { 24154 return new BlockFormatter(options.format); 24155 } 24156 })); 24157 } 24158}); 24159 24160var FormatBlockTool = Tool.extend({ 24161 init: function (options) { 24162 Tool.fn.init.call(this, options); 24163 this.finder = new BlockFormatFinder([{ tags: dom.blockElements }]); 24164 }, 24165 24166 command: function (commandArguments) { 24167 return new FormatCommand(extend(commandArguments, { 24168 formatter: function () { return new GreedyBlockFormatter([{ tags: [commandArguments.value] }], {}); } 24169 })); 24170 }, 24171 24172 update: function(ui, nodes) { 24173 var list; 24174 if (ui.is("select")) { 24175 list = ui.data("kendoSelectBox"); 24176 } else { 24177 list = ui.find("select").data("kendoSelectBox"); 24178 } 24179 list.close(); 24180 list.value(this.finder.getFormat(nodes)); 24181 }, 24182 24183 initialize: function(ui, initOptions) { 24184 var editor = initOptions.editor, 24185 toolName = "formatBlock"; 24186 24187 new Editor.SelectBox(ui, { 24188 dataTextField: "Text", 24189 dataValueField: "Value", 24190 dataSource: editor.options.formatBlock, 24191 title: editor.options.localization.formatBlock, 24192 change: function (e) { 24193 Tool.exec(editor, toolName, this.value()); 24194 }, 24195 highlightFirst: false 24196 }); 24197 24198 ui.closest(".k-widget").removeClass("k-" + toolName).find("*").andSelf().attr("unselectable", "on"); 24199 } 24200}); 24201 24202extend(Editor, { 24203 BlockFormatFinder: BlockFormatFinder, 24204 BlockFormatter: BlockFormatter, 24205 GreedyBlockFormatter: GreedyBlockFormatter, 24206 FormatCommand: FormatCommand, 24207 BlockFormatTool: BlockFormatTool, 24208 FormatBlockTool: FormatBlockTool 24209}); 24210 24211registerTool("formatBlock", new FormatBlockTool({template: new ToolTemplate({template: EditorUtils.dropDownListTemplate, title: "Format Block", initialValue: "Select Block Type"})})); 24212 24213registerFormat("justifyLeft", [ { tags: dom.blockElements, attr: { style: { textAlign: "left"}} }, { tags: ["img"], attr: { style: { "float": "left"}} } ]); 24214registerTool("justifyLeft", new BlockFormatTool({format: formats.justifyLeft, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Justify Left"})})); 24215 24216registerFormat("justifyCenter", [ { tags: dom.blockElements, attr: { style: { textAlign: "center"}} }, { tags: ["img"], attr: { style: { display: "block", marginLeft: "auto", marginRight: "auto"}} } ]); 24217registerTool("justifyCenter", new BlockFormatTool({format: formats.justifyCenter, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Justify Center"})})); 24218 24219registerFormat("justifyRight", [ { tags: dom.blockElements, attr: { style: { textAlign: "right"}} }, { tags: ["img"], attr: { style: { "float": "right"}} } ]); 24220registerTool("justifyRight", new BlockFormatTool({format: formats.justifyRight, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Justify Right"})})); 24221 24222registerFormat("justifyFull", [ { tags: dom.blockElements, attr: { style: { textAlign: "justify"}} } ]); 24223registerTool("justifyFull", new BlockFormatTool({format: formats.justifyFull, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Justify Full"})})); 24224 24225})(jQuery); 24226(function($) { 24227 24228// Imports ================================================================ 24229var kendo = window.kendo, 24230 extend = $.extend, 24231 Editor = kendo.ui.editor, 24232 dom = Editor.Dom, 24233 Command = Editor.Command, 24234 Tool = Editor.Tool, 24235 BlockFormatter = Editor.BlockFormatter, 24236 normalize = dom.normalize, 24237 RangeUtils = Editor.RangeUtils, 24238 registerTool = Editor.EditorUtils.registerTool; 24239 24240var ParagraphCommand = Command.extend({ 24241 init: function(options) { 24242 this.options = options; 24243 Command.fn.init.call(this, options); 24244 }, 24245 24246 exec: function () { 24247 var range = this.getRange(), 24248 doc = RangeUtils.documentFromRange(range), 24249 parent, previous, next, 24250 container, 24251 emptyParagraphContent = $.browser.msie ? '' : '<br _moz_dirty="" />', 24252 paragraph, marker, li, heading, rng, 24253 // necessary while the emptyParagraphContent is empty under IE 24254 blocks = 'p,h1,h2,h3,h4,h5,h6'.split(','), 24255 startInBlock = dom.parentOfType(range.startContainer, blocks), 24256 endInBlock = dom.parentOfType(range.endContainer, blocks), 24257 shouldTrim = (startInBlock && !endInBlock) || (!startInBlock && endInBlock); 24258 24259 function clean(node) { 24260 if (node.firstChild && dom.is(node.firstChild, 'br')) { 24261 dom.remove(node.firstChild); 24262 } 24263 24264 if (dom.isDataNode(node) && !node.nodeValue) { 24265 node = node.parentNode; 24266 } 24267 24268 if (node && !dom.is(node, 'img')) { 24269 while (node.firstChild && node.firstChild.nodeType == 1) { 24270 node = node.firstChild; 24271 } 24272 24273 if (!node.innerHTML) { 24274 node.innerHTML = emptyParagraphContent; 24275 } 24276 } 24277 } 24278 24279 range.deleteContents(); 24280 24281 marker = dom.create(doc, 'a');
24282 range.insertNode(marker); 24283 24284 if (!marker.parentNode) { 24285 // inserting paragraph in Firefox full body range 24286 container = range.commonAncestorContainer; 24287 container.innerHTML = ""; 24288 container.appendChild(marker); 24289 } 24290 24291 normalize(marker.parentNode); 24292 24293 li = dom.parentOfType(marker, ['li']); 24294 heading = dom.parentOfType(marker, 'h1,h2,h3,h4,h5,h6'.split(',')); 24295 24296 if (li) { 24297 rng = range.cloneRange(); 24298 rng.selectNode(li); 24299 24300 // hitting 'enter' in empty li 24301 if (!RangeUtils.textNodes(rng).length) { 24302 paragraph = dom.create(doc, 'p'); 24303 24304 if (li.nextSibling) { 24305 RangeUtils.split(rng, li.parentNode); 24306 } 24307 24308 dom.insertAfter(paragraph, li.parentNode); 24309 dom.remove(li.parentNode.childNodes.length == 1 ? li.parentNode : li); 24310 paragraph.innerHTML = emptyParagraphContent; 24311 next = paragraph; 24312 } 24313 } else if (heading && !marker.nextSibling) { 24314 paragraph = dom.create(doc, 'p'); 24315 24316 dom.insertAfter(paragraph, heading); 24317 paragraph.innerHTML = emptyParagraphContent; 24318 dom.remove(marker); 24319 next = paragraph; 24320 } 24321 24322 if (!next) { 24323 if (!(li || heading)) { 24324 new BlockFormatter([{ tags: ['p']}]).apply([marker]); 24325 } 24326 24327 range.selectNode(marker); 24328 24329 parent = dom.parentOfType(marker, [li ? 'li' : heading ? dom.name(heading) : 'p']); 24330 24331 RangeUtils.split(range, parent, shouldTrim); 24332 24333 previous = parent.previousSibling; 24334 24335 if (dom.is(previous, 'li') && previous.firstChild && !dom.is(previous.firstChild, 'br')) { 24336 previous = previous.firstChild; 24337 } 24338 24339 next = parent.nextSibling; 24340 24341 if (dom.is(next, 'li') && next.firstChild && !dom.is(next.firstChild, 'br')) { 24342 next = next.firstChild; 24343 } 24344 24345 dom.remove(parent); 24346 24347 clean(previous); 24348 clean(next); 24349 24350 // normalize updates the caret display in Gecko 24351 normalize(previous); 24352 } 24353 24354 normalize(next); 24355 24356 if (dom.is(next, 'img')) { 24357 range.setStartBefore(next); 24358 } else { 24359 range.selectNodeContents(next); 24360 24361 var textNode = RangeUtils.textNodes(range)[0]; 24362 24363 if (textNode) { 24364 range.selectNodeContents(textNode); 24365 } 24366 } 24367 24368 range.collapse(true); 24369 24370 dom.scrollTo(next); 24371 24372 RangeUtils.selectRange(range); 24373 } 24374 24375}); 24376 24377var NewLineCommand = Command.extend({ 24378 init: function(options) { 24379 this.options = options; 24380 Command.fn.init.call(this, options); 24381 }, 24382 24383 exec: function () { 24384 var range = this.getRange(); 24385 range.deleteContents(); 24386 var br = dom.create(RangeUtils.documentFromRange(range), 'br'); 24387 range.insertNode(br); 24388 normalize(br.parentNode); 24389 24390 if (!$.browser.msie && (!br.nextSibling || dom.isWhitespace(br.nextSibling))) { 24391 //Gecko and WebKit cannot put the caret after only one br. 24392 var filler = br.cloneNode(true); 24393 filler.setAttribute('_moz_dirty', ''); 24394 dom.insertAfter(filler, br); 24395 } 24396 range.setStartAfter(br); 24397 range.collapse(true); 24398 RangeUtils.selectRange(range); 24399 } 24400}); 24401 24402extend(Editor, { 24403 ParagraphCommand: ParagraphCommand, 24404 NewLineCommand: NewLineCommand 24405}); 24406 24407registerTool("insertLineBreak", new Tool({ key: 13, shift: true, command: NewLineCommand })); 24408registerTool("insertParagraph", new Tool({ key: 13, command: ParagraphCommand })); 24409 24410})(jQuery); 24411(function($) { 24412 24413// Imports ================================================================ 24414var kendo = window.kendo, 24415 Class = kendo.Class, 24416 extend = $.extend, 24417 Editor = kendo.ui.editor, 24418 dom = Editor.Dom, 24419 RangeUtils = Editor.RangeUtils, 24420 EditorUtils = Editor.EditorUtils, 24421 Command = Editor.Command, 24422 ToolTemplate = Editor.ToolTemplate, 24423 FormatTool = Editor.FormatTool, 24424 BlockFormatFinder = Editor.BlockFormatFinder, 24425 textNodes = RangeUtils.textNodes, 24426 registerTool = Editor.EditorUtils.registerTool; 24427 24428var ListFormatFinder = BlockFormatFinder.extend({ 24429 init: function(tag) { 24430 this.tag = tag; 24431 var tags = this.tags = [tag == 'ul' ? 'ol' : 'ul', tag]; 24432 24433 BlockFormatFinder.fn.init.call(this, [{ tags: tags}]); 24434 }, 24435 24436 isFormatted: function (nodes) { 24437 var formatNodes = [], formatNode; 24438 24439 for (var i = 0; i < nodes.length; i++) {
24440 if ((formatNode = this.findFormat(nodes[i])) && dom.name(formatNode) == this.tag) { 24441 formatNodes.push(formatNode); 24442 } 24443 } 24444 24445 if (formatNodes.length < 1) { 24446 return false; 24447 } 24448 24449 if (formatNodes.length != nodes.length) { 24450 return false; 24451 } 24452 24453 // check if sequential lists are selected 24454 for (i = 0; i < formatNodes.length; i++) { 24455 if (formatNodes[i].parentNode != formatNode.parentNode) { 24456 break; 24457 } 24458 24459 if (formatNodes[i] != formatNode) { 24460 return false; 24461 } 24462 } 24463 24464 return true; 24465 }, 24466 24467 findSuitable: function (nodes) { 24468 var candidate = dom.parentOfType(nodes[0], this.tags); 24469 24470 if (candidate && dom.name(candidate) == this.tag) { 24471 return candidate; 24472 } 24473 24474 return null; 24475 } 24476 24477}); 24478 24479var ListFormatter = Class.extend({ 24480 init: function(tag, unwrapTag) { 24481 var that = this; 24482 that.finder = new ListFormatFinder(tag); 24483 that.tag = tag; 24484 that.unwrapTag = unwrapTag; 24485 }, 24486 24487 wrap: function(list, nodes) { 24488 var li = dom.create(list.ownerDocument, "li"), 24489 i, node; 24490 24491 for (i = 0; i < nodes.length; i++) { 24492 node = nodes[i]; 24493 24494 if (dom.is(node, 'li')) { 24495 list.appendChild(node); 24496 continue; 24497 } 24498 24499 if (dom.is(node, 'ul') || dom.is(node, 'ol')) { 24500 while (node.firstChild) { 24501 list.appendChild(node.firstChild); 24502 } 24503 continue; 24504 } 24505 24506 if (dom.is(node, "td")) { 24507 while (node.firstChild) { 24508 li.appendChild(node.firstChild); 24509 } 24510 list.appendChild(li); 24511 node.appendChild(list); 24512 list = list.cloneNode(false); 24513 li = li.cloneNode(false); 24514 continue; 24515 } 24516 24517 li.appendChild(node); 24518 24519 if (dom.isBlock(node)) { 24520 list.appendChild(li); 24521 dom.unwrap(node); 24522 li = li.cloneNode(false); 24523 } 24524 } 24525 24526 if (li.firstChild) { 24527 list.appendChild(li); 24528 } 24529 }, 24530 24531 containsAny: function(parent, nodes) { 24532 for (var i = 0; i < nodes.length; i++) { 24533 if (dom.isAncestorOrSelf(parent, nodes[i])) { 24534 return true; 24535 } 24536 } 24537 24538 return false; 24539 }, 24540 24541 suitable: function (candidate, nodes) { 24542 if (candidate.className == "k-marker") { 24543 var sibling = candidate.nextSibling; 24544 24545 if (sibling && dom.isBlock(sibling)) { 24546 return false; 24547 } 24548 24549 sibling = candidate.previousSibling; 24550 24551 if (sibling && dom.isBlock(sibling)) { 24552 return false; 24553 } 24554 } 24555 24556 return this.containsAny(candidate, nodes) || dom.isInline(candidate) || candidate.nodeType == 3; 24557 }, 24558 24559 split: function (range) { 24560 var nodes = textNodes(range), 24561 start, end; 24562 24563 if (nodes.length) { 24564 start = dom.parentOfType(nodes[0], ['li']); 24565 end = dom.parentOfType(nodes[nodes.length - 1], ['li']); 24566 range.setStartBefore(start); 24567 range.setEndAfter(end); 24568 24569 for (var i = 0, l = nodes.length; i < l; i++) { 24570 var formatNode = this.finder.findFormat(nodes[i]); 24571 if (formatNode) { 24572 var parents = $(formatNode).parents("ul,ol"); 24573 if (parents[0]) { 24574 RangeUtils.split(range, parents.last()[0], true); 24575 } else { 24576 RangeUtils.split(range, formatNode, true); 24577 } 24578 } 24579 } 24580 } 24581 }, 24582 24583 merge: function(tag, formatNode) { 24584 var prev = formatNode.previousSibling, next; 24585 24586 while (prev && (prev.className == "k-marker" || (prev.nodeType == 3 && dom.isWhitespace(prev)))) { 24587 prev = prev.previousSibling; 24588 } 24589 24590 // merge with previous list 24591 if (prev && dom.name(prev) == tag) { 24592 while(formatNode.firstChild) { 24593 prev.appendChild(formatNode.firstChild); 24594 } 24595 dom.remove(formatNode); 24596 formatNode = prev; 24597 } 24598 24599 next = formatNode.nextSibling; 24600 while (next && (next.className == "k-marker" || (next.nodeType == 3 && dom.isWhitespace(next)))) { 24601 next = next.nextSibling; 24602 } 24603 24604 // merge with next list 24605 if (next && dom.name(next) == tag) { 24606 while(formatNode.lastChild) { 24607 next.insertBefore(formatNode.lastChild, next.firstChild); 24608 } 24609 dom.remove(formatNode); 24610 } 24611 }, 24612 24613 applyOnSection: function (section, nodes) { 24614 var tag = this.tag, 24615 commonAncestor; 24616 24617 if (nodes.length == 1) { 24618 commonAncestor = dom.parentOfType(nodes[0], ["ul","ol"]); 24619 } else { 24620 commonAncestor = dom.commonAncestor.apply(null, nodes); 24621 } 24622 24623 if (!commonAncestor) { 24624 commonAncestor = dom.parentOfType(nodes[0], ["td"]) || nodes[0].ownerDocument.body; 24625 } 24626 24627 if (dom.isInline(commonAncestor)) { 24628 commonAncestor = dom.blockParentOrBody(commonAncestor); 24629 } 24630 24631 var ancestors = []; 24632 24633 var formatNode = this.finder.findSuitable(nodes); 24634 24635 if (!formatNode) { 24636 formatNode = new ListFormatFinder(tag == "ul" ? "ol" : "ul").findSuitable(nodes); 24637 } 24638 24639 var childNodes = dom.significantChildNodes(commonAncestor); 24640 24641 if (!childNodes.length) { 24642 childNodes = nodes; 24643 } 24644 24645 if (/table|tbody/.test(dom.name(commonAncestor))) { 24646 childNodes = $.map(nodes, function(node) { 24647 return dom.parentOfType(node, ["td"]); 24648 }); 24649 } 24650
24651 function pushAncestor() { 24652 ancestors.push(this); 24653 } 24654 24655 for (var i = 0; i < childNodes.length; i++) { 24656 var child = childNodes[i]; 24657 var nodeName = dom.name(child); 24658 if (this.suitable(child, nodes) && (!formatNode || !dom.isAncestorOrSelf(formatNode, child))) { 24659 24660 if (formatNode && (nodeName == "ul" || nodeName == "ol")) { 24661 // merging lists 24662 //Array.prototype.push.apply(ancestors, $.toArray(child.childNodes)); 24663 $.each(child.childNodes, pushAncestor); 24664 dom.remove(child); 24665 } else { 24666 ancestors.push(child); 24667 } 24668 } 24669 } 24670 24671 if (ancestors.length == childNodes.length && commonAncestor != nodes[0].ownerDocument.body && !/table|tbody|tr|td/.test(dom.name(commonAncestor))) { 24672 ancestors = [commonAncestor]; 24673 } 24674 24675 if (!formatNode) { 24676 formatNode = dom.create(commonAncestor.ownerDocument, tag); 24677 dom.insertBefore(formatNode, ancestors[0]); 24678 } 24679 24680 this.wrap(formatNode, ancestors); 24681 24682 if (!dom.is(formatNode, tag)) { 24683 dom.changeTag(formatNode, tag); 24684 } 24685 24686 this.merge(tag, formatNode); 24687 }, 24688 24689 apply: function (nodes) { 24690 var i = 0, 24691 sections = [], 24692 lastSection, 24693 lastNodes, 24694 section; 24695 24696 // split nodes into sections that need to be different lists 24697 do { 24698 section = dom.parentOfType(nodes[i], ["td","body"]); 24699 24700 if (!lastSection || section != lastSection) { 24701 if (lastSection) { 24702 sections.push({ 24703 section: lastSection, 24704 nodes: lastNodes 24705 }); 24706 } 24707 24708 lastNodes = [nodes[i]]; 24709 lastSection = section; 24710 } else { 24711 lastNodes.push(nodes[i]); 24712 } 24713 24714 i++; 24715 } while (i < nodes.length); 24716 24717 sections.push({ 24718 section: lastSection, 24719 nodes: lastNodes 24720 }); 24721 24722 for (i = 0; i < sections.length; i++) { 24723 this.applyOnSection(sections[i].section, sections[i].nodes); 24724 } 24725 }, 24726 24727 unwrap: function(ul) { 24728 var fragment = ul.ownerDocument.createDocumentFragment(), 24729 unwrapTag = this.unwrapTag, 24730 parents, 24731 li, 24732 p, 24733 child; 24734 24735 for (li = ul.firstChild; li; li = li.nextSibling) { 24736 p = dom.create(ul.ownerDocument, unwrapTag || 'p'); 24737 24738 while(li.firstChild) { 24739 child = li.firstChild; 24740 24741 if (dom.isBlock(child)) { 24742 24743 if (p.firstChild) { 24744 fragment.appendChild(p); 24745 p = dom.create(ul.ownerDocument, unwrapTag || 'p'); 24746 } 24747 24748 fragment.appendChild(child); 24749 } else { 24750 p.appendChild(child); 24751 } 24752 } 24753 24754 if (p.firstChild) { 24755 fragment.appendChild(p); 24756 } 24757 } 24758 24759 parents = $(ul).parents('ul,ol'); 24760 24761 if (parents[0]) { 24762 dom.insertAfter(fragment, parents.last()[0]); 24763 parents.last().remove(); 24764 } else { 24765 dom.insertAfter(fragment, ul); 24766 } 24767 24768 dom.remove(ul); 24769 }, 24770 24771 remove: function (nodes) { 24772 var formatNode; 24773 for (var i = 0, l = nodes.length; i < l; i++) { 24774 formatNode = this.finder.findFormat(nodes[i]); 24775 24776 if (formatNode) { 24777 this.unwrap(formatNode); 24778 } 24779 } 24780 }, 24781 24782 toggle: function (range) { 24783 var that = this, 24784 nodes = textNodes(range), 24785 ancestor = range.commonAncestorContainer; 24786 24787 if (!nodes.length) { 24788 range.selectNodeContents(ancestor); 24789 nodes = textNodes(range); 24790 if (!nodes.length) { 24791 var text = ancestor.ownerDocument.createTextNode(""); 24792 range.startContainer.appendChild(text); 24793 nodes = [text]; 24794 range.selectNode(text.parentNode); 24795 } 24796 } 24797 24798 if (that.finder.isFormatted(nodes)) { 24799 that.split(range); 24800 that.remove(nodes); 24801 } else {
24802 that.apply(nodes); 24803 } 24804 } 24805 24806}); 24807 24808var ListCommand = Command.extend({ 24809 init: function(options) { 24810 options.formatter = new ListFormatter(options.tag); 24811 Command.fn.init.call(this, options); 24812 } 24813}); 24814 24815var ListTool = FormatTool.extend({ 24816 init: function(options) { 24817 this.options = options; 24818 FormatTool.fn.init.call(this, extend(options, { 24819 finder: new ListFormatFinder(options.tag) 24820 })); 24821 }, 24822 24823 command: function (commandArguments) { 24824 return new ListCommand(extend(commandArguments, { tag: this.options.tag })); 24825 } 24826}); 24827 24828extend(Editor, { 24829 ListFormatFinder: ListFormatFinder, 24830 ListFormatter: ListFormatter, 24831 ListCommand: ListCommand, 24832 ListTool: ListTool 24833}); 24834 24835registerTool("insertUnorderedList", new ListTool({tag:'ul', template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Remove Link"})})); 24836registerTool("insertOrderedList", new ListTool({tag:'ol', template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Remove Link"})})); 24837 24838})(jQuery); 24839(function($, undefined) { 24840 24841var kendo = window.kendo, 24842 Class = kendo.Class, 24843 extend = $.extend, 24844 Editor = kendo.ui.editor, 24845 dom = Editor.Dom, 24846 RangeUtils = Editor.RangeUtils, 24847 EditorUtils = Editor.EditorUtils, 24848 Command = Editor.Command, 24849 Tool = Editor.Tool, 24850 ToolTemplate = Editor.ToolTemplate, 24851 InlineFormatter = Editor.InlineFormatter, 24852 InlineFormatFinder = Editor.InlineFormatFinder, 24853 textNodes = RangeUtils.textNodes, 24854 registerTool = Editor.EditorUtils.registerTool; 24855 24856var LinkFormatFinder = Class.extend({ 24857 findSuitable: function (sourceNode) { 24858 return dom.parentOfType(sourceNode, ['a']); 24859 } 24860}); 24861 24862var LinkFormatter = Class.extend({ 24863 init: function() { 24864 this.finder = new LinkFormatFinder(); 24865 }, 24866 24867 apply: function (range, attributes) { 24868 var nodes = textNodes(range); 24869 if (attributes.innerHTML) { 24870 var markers = RangeUtils.getMarkers(range); 24871 var document = RangeUtils.documentFromRange(range); 24872 range.deleteContents(); 24873 var a = dom.create(document, 'a', attributes); 24874 range.insertNode(a); 24875 24876 if (markers.length > 1) { 24877 dom.insertAfter(markers[markers.length - 1], a); 24878 dom.insertAfter(markers[1], a); 24879 dom[nodes.length > 0 ? 'insertBefore' : 'insertAfter'](markers[0], a); 24880 } 24881 } else { 24882 var formatter = new InlineFormatter([{ tags: ['a']}], attributes); 24883 formatter.finder = this.finder; 24884 formatter.apply(nodes); 24885 } 24886 } 24887}); 24888 24889var UnlinkCommand = Command.extend({ 24890 init: function(options) { 24891 options.formatter = /** @ignore */ { 24892 toggle : function(range) { 24893 new InlineFormatter([{ tags: ['a']}]).remove(textNodes(range)); 24894 } 24895 }; 24896 this.options = options; 24897 Command.fn.init.call(this, options); 24898 } 24899}); 24900 24901var LinkCommand = Command.extend({ 24902 init: function(options) { 24903 var cmd = this; 24904 cmd.options = options; 24905 Command.fn.init.call(cmd, options); 24906 cmd.attributes = null; 24907 cmd.async = true; 24908 cmd.formatter = new LinkFormatter(); 24909 }, 24910 24911 exec: function () { 24912 var range = this.getRange(); 24913 24914 var collapsed = range.collapsed; 24915 24916 range = this.lockRange(true); 24917 24918 var nodes = textNodes(range); 24919 24920 var initialText = null; 24921 24922 var that = this; 24923 24924 function apply(e) { 24925 var href = $('#k-editor-link-url', dialog.element).val(); 24926 24927 if (href && href != 'http://') { 24928 that.attributes = { href: href }; 24929 24930 var title = $('#k-editor-link-title', dialog.element).val(); 24931 if (title) { 24932 that.attributes.title = title; 24933 } 24934 24935 var text = $('#k-editor-link-text', dialog.element).val(); 24936 if (text !== initialText) { 24937 that.attributes.innerHTML = text || href; 24938 } 24939 24940 var target = $('#k-editor-link-target', dialog.element).is(':checked'); 24941 if (target) { 24942 that.attributes.target = '_blank'; 24943 } 24944 24945 that.formatter.apply(range, that.attributes); 24946 } 24947 24948 close(e); 24949 24950 if (that.change) { 24951 that.change(); 24952 } 24953 } 24954 24955 function close(e) { 24956 e.preventDefault(); 24957 dialog.destroy(); 24958 24959 dom.windowFromDocument(RangeUtils.documentFromRange(range)).focus(); 24960 24961 that.releaseRange(range); 24962 } 24963 24964 var a = nodes.length ? that.formatter.finder.findSuitable(nodes[0]) : null; 24965 24966 var shouldShowText = nodes.length <= 1 || (nodes.length == 2 && collapsed); 24967 24968 var windowContent = 24969 '<div class="k-editor-dialog">' + 24970 '<ol>' + 24971 '<li class="k-form-text-row"><label for="k-editor-link-url">Web address</label><input type="text" class="k-input" id="k-editor-link-url"/></li>' + 24972 (shouldShowText ? '<li class="k-form-text-row"><label for="k-editor-link-text">Text</label><input type="text" class="k-input" id="k-editor-link-text"/></li>' : '') + 24973 '<li class="k-form-text-row"><label for="k-editor-link-title">Tooltip</label><input type="text" class="k-input" id="k-editor-link-title"/></li>' + 24974 '<li class="k-form-checkbox-row"><input type="checkbox" id="k-editor-link-target"/><label for="k-editor-link-target">Open link in new window</label></li>' + 24975 '</ol>' + 24976 '<div class="k-button-wrapper">' + 24977 '<button class="k-dialog-insert k-button">Insert</button>' + 24978 ' or ' + 24979 '<a href="#" class="k-dialog-close k-link">Close</a>' + 24980 '</div>' + 24981 '</div>'; 24982 24983 var dialog = $(windowContent).appendTo(document.body).kendoWindow($.extend({}, this.editor.options.dialogOptions, { 24984 title: "Insert link", 24985 close: close 24986 })) 24987 .hide() 24988 .find('.k-dialog-insert').click(apply).end() 24989 .find('.k-dialog-close').click(close).end() 24990 .find('.k-form-text-row input').keydown(function (e) { 24991 if (e.keyCode == 13) { 24992 apply(e); 24993 } else if (e.keyCode == 27) { 24994 close(e); 24995 } 24996 }).end() 24997 // IE < 8 returns absolute url if getAttribute is not used 24998 .find('#k-editor-link-url').val(a ? a.getAttribute('href', 2) : 'http://').end() 24999 .find('#k-editor-link-text').val(nodes.length > 0 ? (nodes.length == 1 ? nodes[0].nodeValue : nodes[0].nodeValue + nodes[1].nodeValue) : '').end() 25000 .find('#k-editor-link-title').val(a ? a.title : '').end() 25001 .find('#k-editor-link-target').attr('checked', a ? a.target == '_blank' : false).end() 25002 .show() 25003 .data('kendoWindow') 25004 .center(); 25005 25006 if (shouldShowText && nodes.length > 0) { 25007 initialText = $('#k-editor-link-text', dialog.element).val(); 25008 } 25009 25010 $('#k-editor-link-url', dialog.element).focus().select(); 25011 }, 25012 25013 redo: function () { 25014 var that = this, 25015 range = that.lockRange(true); 25016 25017 that.formatter.apply(range, that.attributes); 25018 that.releaseRange(range); 25019 } 25020 25021}); 25022 25023var UnlinkTool = Tool.extend({ 25024 init: function(options) { 25025 this.options = options; 25026 this.finder = new InlineFormatFinder([{tags:['a']}]); 25027 25028 Tool.fn.init.call(this, $.extend(options, {command:UnlinkCommand})); 25029 }, 25030 25031 initialize: function($ui) { 25032 $ui.attr('unselectable', 'on') 25033 .addClass('k-state-disabled'); 25034 }, 25035 25036 update: function ($ui, nodes) { 25037 $ui.toggleClass('k-state-disabled', !this.finder.isFormatted(nodes)) 25038 .removeClass('k-state-hover'); 25039 } 25040}); 25041 25042extend(kendo.ui.editor, { 25043 LinkFormatFinder: LinkFormatFinder, 25044 LinkFormatter: LinkFormatter, 25045 UnlinkCommand: UnlinkCommand, 25046 LinkCommand: LinkCommand, 25047 UnlinkTool: UnlinkTool 25048}); 25049 25050registerTool("createLink", new Tool({ key: 'K', ctrl: true, command: LinkCommand, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Create Link"})})); 25051registerTool("unlink", new UnlinkTool({ key: 'K', ctrl: true, shift: true, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Remove Link"})})); 25052 25053})(jQuery); 25054(function($, undefined) { 25055 25056var kendo = window.kendo, 25057 extend = $.extend, 25058 Editor = kendo.ui.editor, 25059 EditorUtils = Editor.EditorUtils, 25060 dom = Editor.Dom, 25061 registerTool = EditorUtils.registerTool, 25062 ToolTemplate = Editor.ToolTemplate, 25063 RangeUtils = Editor.RangeUtils, 25064 Command = Editor.Command, 25065 keys = kendo.keys, 25066 INSERTIMAGE = "Insert Image", 25067 KEDITORIMAGEURL = "#k-editor-image-url",
25068 KEDITORIMAGETITLE = "#k-editor-image-title"; 25069 25070var ImageCommand = Command.extend({ 25071 init: function(options) { 25072 var that = this; 25073 Command.fn.init.call(that, options); 25074 25075 that.async = true; 25076 that.attributes = {}; 25077 }, 25078 25079 insertImage: function(img, range) { 25080 var attributes = this.attributes; 25081 if (attributes.src && attributes.src != "http://") { 25082 if (!img) { 25083 img = dom.create(RangeUtils.documentFromRange(range), "img", attributes); 25084 img.onload = img.onerror = function () { 25085 img.removeAttribute("complete"); 25086 img.removeAttribute("width"); 25087 img.removeAttribute("height"); 25088 }; 25089 25090 range.deleteContents(); 25091 range.insertNode(img); 25092 range.setStartAfter(img); 25093 range.setEndAfter(img); 25094 RangeUtils.selectRange(range); 25095 return true; 25096 } else { 25097 dom.attr(img, attributes); 25098 } 25099 } 25100 25101 return false; 25102 }, 25103 25104 redo: function () { 25105 var that = this, 25106 range = that.lockRange(); 25107 25108 if (!that.insertImage(RangeUtils.image(range), range)) { 25109 that.releaseRange(range); 25110 } 25111 }, 25112 25113 exec: function () { 25114 var that = this, 25115 range = that.lockRange(), 25116 applied = false, 25117 img = RangeUtils.image(range), 25118 windowContent, dialog; 25119 25120 function apply(e) { 25121 that.attributes = { 25122 src: $(KEDITORIMAGEURL, dialog.element).val(), 25123 alt: $(KEDITORIMAGETITLE, dialog.element).val() 25124 }; 25125 25126 applied = that.insertImage(img, range); 25127 25128 close(e); 25129 25130 if (that.change) { 25131 that.change(); 25132 } 25133 } 25134 25135 function close(e) { 25136 e.preventDefault(); 25137 dialog.destroy(); 25138 25139 dom.windowFromDocument(RangeUtils.documentFromRange(range)).focus(); 25140 if (!applied) { 25141 that.releaseRange(range); 25142 } 25143 } 25144 25145 function keyDown(e) { 25146 if (e.keyCode == keys.ENTER) { 25147 apply(e); 25148 } else if (e.keyCode == keys.ESC) { 25149 close(e); 25150 } 25151 } 25152 25153 //var fileBrowser = that.editor.fileBrowser; 25154 //var showBrowser = fileBrowser && fileBrowser.selectUrl !== undefined; 25155 25156 windowContent = 25157 '<div class="k-editor-dialog">' + 25158 '<ol>' + 25159 '<li class="k-form-text-row"><label for="k-editor-image-url">Web address</label><input type="text" class="k-input" id="k-editor-image-url"/></li>' + 25160 '<li class="k-form-text-row"><label for="k-editor-image-title">Tooltip</label><input type="text" class="k-input" id="k-editor-image-title"/></li>' + 25161 '</ol>' + 25162 '<div class="k-button-wrapper">' + 25163 '<button class="k-dialog-insert k-button">Insert</button>' + 25164 ' or ' + 25165 '<a href="#" class="k-dialog-close k-link">Close</a>' + 25166 '</div>' + 25167 '</div>'; 25168 25169 dialog = $(windowContent) 25170 .appendTo(document.body) 25171 .kendoWindow(extend({}, that.editor.options.dialogOptions, { 25172 title: INSERTIMAGE, 25173 close: close, 25174 activate: function() { 25175 //if (showBrowser) { 25176 //new $t.imageBrowser( 25177 //$(this).find(".k-image-browser"), 25178 //extend(fileBrowser, { 25179 //apply: apply, 25180 //element: that.editor.element, 25181 //localization: that.editor.options.localization 25182 //})); 25183 //} 25184 } 25185 })) 25186 .hide() 25187 .find(".k-dialog-insert").click(apply).end() 25188 .find(".k-dialog-close").click(close).end() 25189 .find(".k-form-text-row input").keydown(keyDown).end() 25190 //.toggleClass("k-imagebrowser", showBrowser) 25191 // IE < 8 returns absolute url if getAttribute is not used 25192 .find(KEDITORIMAGEURL).val(img ? img.getAttribute("src", 2) : "http://").end() 25193 .find(KEDITORIMAGETITLE).val(img ? img.alt : "").end() 25194 .show() 25195 .data("kendoWindow") 25196 .center(); 25197 25198 $(KEDITORIMAGEURL, dialog.element).focus().select(); 25199 } 25200 25201}); 25202 25203kendo.ui.editor.ImageCommand = ImageCommand; 25204 25205registerTool("insertImage", new Editor.Tool({ command: ImageCommand, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: INSERTIMAGE}) })); 25206 25207})(jQuery); 25208(function($, undefined) { 25209 25210var kendo = window.kendo, 25211 Widget = kendo.ui.Widget, 25212 DropDownList = kendo.ui.DropDownList, 25213 Editor = kendo.ui.editor, 25214 dom = Editor.Dom, 25215 CHANGE = "change", 25216 KSTATESELECTED = "k-state-selected", 25217 SELECTEDCLASS = "." + KSTATESELECTED, 25218 SELECTEDCOLORCLASS = ".k-selected-color", 25219 UNSELECTABLE = "unselectable", 25220 BACKGROUNDCOLOR = "background-color", 25221 keys = kendo.keys, 25222 template = kendo.template( 25223'<div class="k-colorpicker-popup">' + 25224 '<ul class="k-reset">'+ 25225 '# for(var i = 0; i < colors.length; i++) { #' + 25226 '<li class="k-item #= colors[i] == value ? "k-state-selected" : "" #">
25226' + 25227 '<div style="background-color:\\##= colors[i] #"></div>'+ 25228 '</li>' + 25229 '# } #' + 25230 '</ul>' + 25231'</div>'); 25232 25233var ColorPicker = Widget.extend({ 25234 init: function(element, options) { 25235 var that = this; 25236 25237 Widget.fn.init.call(that, element, options); 25238 25239 element = that.element; 25240 options = that.options; 25241 25242 that._value = options.value; 25243 25244 that.popup = $(template({ 25245 colors: options.colors, 25246 value: options.value.substring(1) 25247 })) 25248 .kendoPopup({ 25249 anchor: element, 25250 toggleTarget: element.find(".k-icon") 25251 }) 25252 .delegate(".k-item", "click", function(e) { 25253 that.select($(e.currentTarget).find("div").css(BACKGROUNDCOLOR)); 25254 }) 25255 .find("*") 25256 .attr(UNSELECTABLE, "on") 25257 .end() 25258 .data("kendoPopup"); 25259 25260 element.attr("tabIndex", 0) // add the color picker to the tab order 25261 .keydown(function(e) { 25262 that.keydown(e); 25263 }) 25264 .focus(function () { 25265 element.css("outline", "1px dotted #000"); 25266 }) 25267 .blur(function() { 25268 element.css("outline", ""); 25269 }) 25270 .delegate(".k-tool-icon", "click", function() { 25271 that.select(); 25272 }) 25273 .find("*") 25274 .attr(UNSELECTABLE, "on"); 25275 25276 if (that._value) { 25277 element.find(SELECTEDCOLORCLASS).css(BACKGROUNDCOLOR, that._value); 25278 } 25279 }, 25280 25281 options: { 25282 name: "ColorPicker", 25283 colors: "000000,7f7f7f,880015,ed1c24,ff7f27,fff200,22b14c,00a2e8,3f48cc,a349a4,ffffff,c3c3c3,b97a57,ffaec9,ffc90e,efe4b0,b5e61d,99d9ea,7092be,c8bfe7".split(","), 25284 value: null 25285 }, 25286 25287 events: [ 25288 CHANGE 25289 ], 25290 25291 select: function(color) { 25292 var that = this; 25293 25294 if (color) { 25295 color = dom.toHex(color); 25296 if (!that.trigger(CHANGE, { value: color })) { 25297 that.value(color); 25298 that.close(); 25299 } 25300 } else { 25301 that.trigger(CHANGE, { value: that._value }); 25302 } 25303 }, 25304 25305 open: function() { 25306 this.popup.open(); 25307 }, 25308 25309 close: function() { 25310 this.popup.close(); 25311 }, 25312 25313 toggle: function() { 25314 this.popup.toggle(); 25315 }, 25316 25317 keydown: function(e) { 25318 var that = this, 25319 popup = that.popup.element, 25320 visible = that.popup.visible(), 25321 selected, 25322 next, 25323 prev, 25324 preventDefault = false, 25325 keyCode = e.keyCode; 25326 25327 if (keyCode == keys.DOWN) { 25328 if (!visible) { 25329 that.open(); 25330 } else { 25331 selected = popup.find(SELECTEDCLASS); 25332 25333 if (selected[0]) { 25334 next = selected.next(); 25335 } else { 25336 next = popup.find("li:first"); 25337 } 25338 25339 if (next[0]) { 25340 selected.removeClass(KSTATESELECTED); 25341 next.addClass(KSTATESELECTED); 25342 } 25343 } 25344 25345 preventDefault = true; 25346 } else if (keyCode == keys.UP) { 25347 if (visible) { 25348 selected = popup.find(SELECTEDCLASS); 25349 prev = selected.prev(); 25350 25351 if (prev[0]) { 25352 selected.removeClass(KSTATESELECTED); 25353 prev.addClass(KSTATESELECTED); 25354 } 25355 } 25356 preventDefault = true; 25357 } else if (keyCode == keys.TAB || keyCode == keys.RIGHT || keyCode == keys.LEFT) { 25358 that.close(); 25359 } else if (keyCode == keys.ENTER) { 25360 popup.find(SELECTEDCLASS).click(); 25361 preventDefault = true; 25362 } 25363 25364 if (preventDefault) { 25365 e.preventDefault(); 25366 } 25367 }, 25368 25369 value: function(value) { 25370 var that = this; 25371 25372 if (value === undefined) { 25373 return that._value; 25374 } else { 25375 value = dom.toHex(value); 25376 25377 that._value = value; 25378 25379 that.element.find(SELECTEDCOLORCLASS) 25380 .css(BACKGROUNDCOLOR, value); 25381 } 25382 } 25383}); 25384 25385var SelectBox = DropDownList.extend({ 25386 init: function(element, options) { 25387 var that = this; 25388 25389 DropDownList.fn.init.call(that, element, options); 25390 25391 that.value(that.options.title); 25392 }, 25393 options: { 25394 name: "SelectBox" 25395 }, 25396 value: function(value) { 25397 var that = this, 25398 result = DropDownList.fn.value.call(that, value); 25399 25400 if (value === undefined) { 25401 return result; 25402 } 25403 25404 if (value !== DropDownList.fn.value.call(that)) { 25405 that.text(that.options.title); 25406 that._current.removeClass("k-state-selected"); 25407 that.current(null); 25408 that._oldIndex = that.selectedIndex = -1; 25409 } 25410 } 25411}); 25412 25413kendo.ui.editor.ColorPicker = ColorPicker; 25414kendo.ui.editor.SelectBox = SelectBox; 25415 25416})(jQuery); 25417(function($, undefined) { 25418 25419// Imports ================================================================ 25420var kendo = window.kendo, 25421 Class = kendo.Class, 25422 extend = $.extend, 25423 Editor = kendo.ui.editor, 25424 dom = Editor.Dom, 25425 EditorUtils = Editor.EditorUtils, 25426 registerTool = EditorUtils.registerTool, 25427 Command = Editor.Command, 25428 Tool = Editor.Tool, 25429 ToolTemplate = Editor.ToolTemplate, 25430 RangeUtils = Editor.RangeUtils, 25431 blockElements = dom.blockElements, 25432 BlockFormatFinder = Editor.BlockFormatFinder, 25433 BlockFormatter = Editor.BlockFormatter; 25434 25435function indent(node, value) { 25436 var property = dom.name(node) != "td" ? "marginLeft" : "paddingLeft"; 25437 if (value === undefined) { 25438 return node.style[property] || 0; 25439 } else { 25440 if (value > 0) { 25441 node.style[property] = value + "px"; 25442 } else { 25443 node.style[property] = ""; 25444 25445 if (!node.style.cssText) { 25446 node.removeAttribute("style"); 25447 } 25448 } 25449 } 25450} 25451 25452var IndentFormatter = Class.extend({ 25453 init: function() { 25454 this.finder = new BlockFormatFinder([{tags:dom.blockElements}]); 25455 }, 25456 25457 apply: function (nodes) { 25458 var formatNodes = this.finder.findSuitable(nodes), 25459 targets = [], 25460 i, len, formatNode, parentList, sibling; 25461 25462 if (formatNodes.length) { 25463 for (i = 0, len = formatNodes.length; i < len; i++) { 25464 if (dom.is(formatNodes[i], "li")) { 25465 if (!$(formatNodes[i]).index()) { 25466 targets.push(formatNodes[i].parentNode); 25467 } else if ($.inArray(formatNodes[i].parentNode, targets) < 0) { 25468 targets.push(formatNodes[i]); 25469 } 25470 } else { 25471 targets.push(formatNodes[i]); 25472 } 25473 } 25474 25475 while (targets.length) { 25476 formatNode = targets.shift(); 25477 if (dom.is(formatNode, "li")) { 25478 parentList = formatNode.parentNode; 25479 sibling = $(formatNode).prev("li"); 25480 var siblingList = sibling.find("ul,ol").last(); 25481 25482 var nestedList = $(formatNode).children("ul,ol")[0]; 25483 25484 if (nestedList && sibling[0]) { 25485 if (siblingList[0]) { 25486 siblingList.append(formatNode); 25487 siblingList.append($(nestedList).children()); 25488 dom.remove(nestedList); 25489 } else {
25490 sibling.append(nestedList); 25491 nestedList.insertBefore(formatNode, nestedList.firstChild); 25492 } 25493 } else { 25494 nestedList = sibling.children("ul,ol")[0]; 25495 if (!nestedList) { 25496 nestedList = dom.create(formatNode.ownerDocument, dom.name(parentList)); 25497 sibling.append(nestedList); 25498 } 25499 25500 while (formatNode && formatNode.parentNode == parentList) { 25501 nestedList.appendChild(formatNode); 25502 formatNode = targets.shift(); 25503 } 25504 } 25505 } else { 25506 var marginLeft = parseInt(indent(formatNode), 10) + 30; 25507 indent(formatNode, marginLeft); 25508 25509 for (var targetIndex = 0; targetIndex < targets.length; targetIndex++) { 25510 if ($.contains(formatNode, targets[targetIndex])) { 25511 targets.splice(targetIndex, 1); 25512 } 25513 } 25514 } 25515 } 25516 } else { 25517 var formatter = new BlockFormatter([{tags:"p"}], {style:{marginLeft:30}}); 25518 25519 formatter.apply(nodes); 25520 } 25521 }, 25522 25523 remove: function(nodes) { 25524 var formatNodes = this.finder.findSuitable(nodes), 25525 targetNode, i, len, list, listParent, siblings, 25526 formatNode, marginLeft; 25527 25528 for (i = 0, len = formatNodes.length; i < len; i++) { 25529 formatNode = $(formatNodes[i]); 25530 25531 if (formatNode.is("li")) { 25532 list = formatNode.parent(); 25533 listParent = list.parent(); 25534 // listParent will be ul or ol in case of invalid dom - <ul><li></li><ul><li></li></ul></ul> 25535 if (listParent.is("li,ul,ol") && !indent(list[0])) { 25536 // skip already processed nodes 25537 if (targetNode && $.contains(targetNode, listParent[0])) { 25538 continue; 25539 } 25540 25541 siblings = formatNode.nextAll("li"); 25542 if (siblings.length) { 25543 $(list[0].cloneNode(false)).appendTo(formatNode).append(siblings); 25544 } 25545 25546 if (listParent.is("li")) { 25547 formatNode.insertAfter(listParent); 25548 } else { 25549 formatNode.appendTo(listParent); 25550 } 25551 25552 if (!list.children("li").length) { 25553 list.remove(); 25554 } 25555 25556 continue; 25557 } else { 25558 if (targetNode == list[0]) { 25559 // removing format on sibling LI elements 25560 continue; 25561 } 25562 targetNode = list[0]; 25563 } 25564 } else { 25565 targetNode = formatNodes[i]; 25566 } 25567 25568 marginLeft = parseInt(indent(targetNode), 10) - 30; 25569 indent(targetNode, marginLeft); 25570 } 25571 } 25572 25573}); 25574 25575var IndentCommand = Command.extend({ 25576 init: function(options) { 25577 options.formatter = /** @ignore */ { 25578 toggle : function(range) { 25579 new IndentFormatter().apply(RangeUtils.nodes(range)); 25580 } 25581 }; 25582 Command.fn.init.call(this, options); 25583 } 25584}); 25585 25586var OutdentCommand = Command.extend({ 25587 init: function(options) { 25588 options.formatter = { 25589 toggle : function(range) { 25590 new IndentFormatter().remove(RangeUtils.nodes(range)); 25591 } 25592 }; 25593 Command.fn.init.call(this, options); 25594 } 25595}); 25596 25597var OutdentTool = Tool.extend({ 25598 init: function(options) { 25599 Tool.fn.init.call(this, extend(options, {command:OutdentCommand})); 25600 25601 this.finder = new BlockFormatFinder([{tags:blockElements}]); 25602 }, 25603 25604 initialize: function(ui) { 25605 ui.attr("unselectable", "on") 25606 .addClass("k-state-disabled"); 25607 }, 25608 25609 update: function (ui, nodes) { 25610 var suitable = this.finder.findSuitable(nodes), 25611 isOutdentable, listParentsCount, i, len; 25612 25613 for (i = 0, len = suitable.length; i < len; i++) { 25614 isOutdentable = indent(suitable[i]); 25615 25616 if (!isOutdentable) { 25617 listParentsCount = $(suitable[i]).parents("ul,ol").length;
25618 isOutdentable = (dom.is(suitable[i], "li") && (listParentsCount > 1 || indent(suitable[i].parentNode))) || 25619 (dom.ofType(suitable[i], ["ul","ol"]) && listParentsCount > 0); 25620 } 25621 25622 if (isOutdentable) { 25623 ui.removeClass("k-state-disabled"); 25624 return; 25625 } 25626 } 25627 25628 ui.addClass("k-state-disabled").removeClass("k-state-hover"); 25629 } 25630}); 25631 25632extend(Editor, { 25633 IndentFormatter: IndentFormatter, 25634 IndentCommand: IndentCommand, 25635 OutdentCommand: OutdentCommand, 25636 OutdentTool: OutdentTool 25637}); 25638 25639registerTool("indent", new Tool({ command: IndentCommand, template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Indent"}) })); 25640registerTool("outdent", new OutdentTool({template: new ToolTemplate({template: EditorUtils.buttonTemplate, title: "Outdent"})})); 25641 25642})(jQuery); 25643(function($) { 25644 25645// Imports ================================================================ 25646var kendo = window.kendo, 25647 Class = kendo.Class, 25648 extend = $.extend, 25649 Editor = kendo.ui.editor, 25650 RangeUtils = Editor.RangeUtils, 25651 Marker = Editor.Marker; 25652 25653var PendingFormats = Class.extend({ 25654 init: function(editor) { 25655 this.editor = editor; 25656 this.formats = []; 25657 }, 25658 25659 apply: function(range) { 25660 if (!this.hasPending()) { 25661 return; 25662 } 25663 25664 var marker = new Marker(); 25665 25666 marker.addCaret(range); 25667 25668 var caret = range.startContainer.childNodes[range.startOffset]; 25669 25670 var target = caret.previousSibling; 25671 25672 /* under IE, target is a zero-length text node. go figure. */ 25673 if (!target.nodeValue) { 25674 target = target.previousSibling; 25675 } 25676 25677 range.setStart(target, target.nodeValue.length - 1); 25678 25679 marker.add(range); 25680 25681 if (!RangeUtils.textNodes(range).length) { 25682 marker.remove(range); 25683 range.collapse(true); 25684 this.editor.selectRange(range); 25685 return; 25686 } 25687 25688 var textNode = marker.end.previousSibling.previousSibling; 25689 25690 var pendingFormat, 25691 formats = this.formats; 25692 25693 for (var i = 0; i < formats.length; i++) { 25694 pendingFormat = formats[i]; 25695 25696 var command = pendingFormat.command(extend({ range: range }, pendingFormat.options.params)); 25697 command.editor = this.editor; 25698 command.exec(); 25699 25700 range.selectNode(textNode); 25701 } 25702 25703 marker.remove(range); 25704 25705 if (textNode.parentNode) { 25706 range.setStart(textNode, 1); 25707 range.collapse(true); 25708 } 25709 25710 this.clear(); 25711 25712 this.editor.selectRange(range); 25713 }, 25714 25715 hasPending: function() { 25716 return this.formats.length > 0; 25717 }, 25718 25719 isPending: function(format) { 25720 return !!this.getPending(format); 25721 }, 25722 25723 getPending: function(format) { 25724 var formats = this.formats; 25725 for (var i = 0; i < formats.length; i++) { 25726 if (formats[i].name == format) { 25727 return formats[i]; 25728 } 25729 } 25730 25731 return; 25732 }, 25733 25734 toggle: function(format) { 25735 var formats = this.formats; 25736 25737 for (var i = 0; i < formats.length; i++) { 25738 if (formats[i].name == format.name) { 25739 if (formats[i].params && formats[i].params.value != format.params.value) { 25740 formats[i].params.value = format.params.value; 25741 } else { 25742 formats.splice(i, 1); 25743 } 25744 25745 return; 25746 } 25747 } 25748 25749 formats.push(format); 25750 }, 25751 25752 clear: function() { 25753 this.formats = []; 25754 } 25755 25756}); 25757 25758extend(Editor, { 25759 PendingFormats: PendingFormats 25760}); 25761 25762})(jQuery); 25763(function($, undefined) { 25764 /** 25765 * @name kendo.ui.NumericTextBox.Description 25766 * 25767 * @section 25768 * <p> 25769 * The NumericTextBox widget can convert an INPUT element into a numeric, percentage or currency textbox. 25770 * The type is defined depending on the specified format. The widget renders spin buttons and with their help you can 25771 * increment/decrement the value with a predefined step. The NumericTextBox widget accepts only numeric entries. 25772 * The widget uses <em>kendo.culture.current</em> culture in order to determine number precision and other culture 25773 * specific properties. 25774 * </p> 25775 * 25776 * <h3>Getting Started</h3> 25777 * 25778 * @exampleTitle Creating a NumericTextBox from existing INPUT element 25779 * @example 25780 * <input id="textBox" /> 25781 * 25782 * @exampleTitle NumericTextBox initialization 25783 * @example 25784 * $(document).ready(function(){ 25785 * $("#textBox").kendoNumericTextBox(); 25786 * }); 25787 * 25788 * @section 25789 * <p> 25790 * When a <b>NumericTextBox</b> is initialized, it will automatically 25791 * wraps the input element with span element and will render spin 25792 * buttons. 25793 * </p> 25794 * <h3>Configuring NumericTextBox behaviors</h3> 25795 * <p> 25796 * The <b>NumericTextBox</b> provides configuration options that can be 25797 * easily set during initialization. Among the properties that can be 25798 * controlled: 25799 * </p> 25800 * <ul> 25801 * <li>Value of the <b>NumericTextBox</b></li> 25802 * <li>Minimum and/or maximum values</li> 25803 * <li>Increment step</li> 25804 * <li>Precision of the number</li> 25805 * <li>Number format (any valid number format is allowed)</li> 25806 * </ul> 25807 * <p> 25808 * To see a full list of available properties and values, review the 25809 * Slider Configuration API documentation tab. 25810 * </p> 25811 * @exampleTitle Customizing NumericTextBox defaults 25812 * @example 25813 * $("#textbox").kendoNumericTextBox({ 25814 * value: 10, 25815 * min: -10, 25816 * max: 100, 25817 * step: 0.75, 25818 * format: "n", 25819 * decimals: 3 25820 * }); 25821 * @section 25822 * @exampleTitle Create Currency NumericTextBox widget 25823 * @example 25824 * $("#textbox").kendoNumericTextBox({ 25825 * format: "c2" //Define currency type and 2 digits precision 25826 * }); 25827 * @section 25828 * @exampleTitle Create Percentage NumericTextBox widget 25829 * @example 25830 * $("#textbox").kendoNumericTextBox({ 25831 * format: "p", 25832 * value: 0.15 // 15 % 25833 * }); 25834 * 25835 * @section 25836 * <h3>Accessing an Existing NumericTextBox</h3> 25837 * <p> 25838 * You can reference an existing <b>NumericTextBox</b> instance via 25839 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. 25840 * Once a reference has been established, you can use the API to control 25841 * its behavior. 25842 * </p> 25843 * 25844 * @exampleTitle Accessing an existing NumericTextBox instance 25845 * @example 25846 * var numericTextBox = $("#numericTextBox").data("kendoNumericTextBox"); 25847 * 25848 */ 25849 var kendo = window.kendo, 25850 keys = kendo.keys, 25851 ui = kendo.ui, 25852 Widget = ui.Widget, 25853 parse = kendo.parseFloat, 25854 touch = kendo.support.touch, 25855 CHANGE = "change", 25856 DISABLED = "disabled", 25857 INPUT = "k-input", 25858 SPIN = "spin", 25859 TOUCHEND = "touchend", 25860 MOUSEDOWN = touch ? "touchstart" : "mousedown", 25861 MOUSEUP = touch ? "touchmove " + TOUCHEND : "mouseup mouseleave", 25862 DEFAULT = "k-state-default", 25863 FOCUSED = "k-state-focused", 25864 HOVER = "k-state-hover", 25865 HOVEREVENTS = "mouseenter mouseleave", 25866 POINT = ".", 25867 SELECTED = "k-state-selected", 25868 STATEDISABLED = "k-state-disabled", 25869 TYPE = touch ? "number" : "text", 25870 NULL = null, 25871 proxy = $.proxy, 25872 decimals = { 25873 190 : ".", 25874 188 : "," 25875 }; 25876 25877 var NumericTextBox = Widget.extend(/** @lends kendo.ui.NumericTextBox.prototype */{ 25878 /** 25879 * @constructs 25880 * @extends kendo.ui.Widget 25881 * @param {DomElement} element DOM element 25882 * @param {Object} options Configuration options 25883 * @option {Number} [value] <null> Specifies the value of the NumericTextBox widget. 25884 * _example 25885 * // specify in the HTML 25886 * <input id="numeric" value="10" type="number" min="-100" max="100" step="10"/> 25887 * <br /> 25888 * // specify on widget initialization 25889 * $("#numeric").kendoNumericTextBox({ 25890 * min: 0, 25891 * max: 100, 25892 * value: 50 25893 * }); 25894 * @option {Number} [min] <null> Specifies the smallest value the user can enter. 25895 * _example 25896 * // specify in the HTML 25897 * <input id="numeric" value="10" type="number" min="-100" max="100" step="10"/> 25898 * <br /> 25899 * // specify on widget initialization 25900 * $("#numeric").kendoNumericTextBox({ 25901 * min: 0, 25902 * max: 100, 25903 * value: 50 25904 * }); 25905 * @option {Number} [max] <null> Specifies the largest value the user can enter. 25906 * _example 25907 * // specify in the HTML 25908 * <input id="numeric" value="10" type="number" min="-100" max="100" step="10"/> 25909 * <br /> 25910 * // specify on widget initialization 25911 * $("#numeric").kendoNumericTextBox({ 25912 * min: 0, 25913 * max: 100, 25914 * value: 50 25915 * }); 25916 * @option {Number} [decimals] <null> Specifies the number precision. If not set precision defined by current culture is used. 25917 * _example 25918 * // specify on widget initialization 25919 * $("#numeric").kendoNumericTextBox({ 25920 * min: 0, 25921 * max: 1, 25922 * step: 0.1, 25923 * decimals: 1 25924 * }); 25925 * @option {Number} [step] <1> Specifies the increment/decrement step. 25926 * _example 25927 * // specify in the HTML 25928 * <input id="numeric" value="10" type="number" /> 25929 * <br /> 25930 * // specify on widget initialization 25931 * $("#numeric").kendoNumericTextBox({ 25932 * min: 0, 25933 * max: 1, 25934 * step: 0.1 25935 * }); 25936 * @option {String} [format] <n> Specifies the format of the number. Any valid number format is allowed. 25937 * _example 25938 * $("#numeric").kendoNumericTextBox({ 25939 * format: "p0", // format as percentage with % sign 25940 * min: 0, 25941 * max: 1, 25942 * step: 0.01 25943 * }); 25944 * @option {String} [placeholder] <""> Specifies the text displayed when the input is empty. 25945 * _example 25946 * // specify on widget initialization 25947 * $("#numeric").kendoNumericTextBox({ 25948 * min: 0, 25949 * max: 100, 25950 * value: 50, 25951 * placeholder: "Select A Value" 25952 * }); 25953 * @option {String} [upArrowText] <Increase value> Specifies the text of the tooltip on the up arrow. 25954 * _example 25955 * // specify on widget initialization 25956 * $("#numeric").kendoNumericTextBox({ 25957 * min: 0, 25958 * max: 100, 25959 * value: 50, 25960 * upArrowText: "More", 25961 * downArrowText: "Less" 25962 * }); 25963 * @option {String} [downArrowText] <Decrease value> Specifies the text of the tooltip on the down arrow. 25964 * _example 25965 * // specify on widget initialization 25966 * $("#numeric").kendoNumericTextBox({ 25967 * min: 0, 25968 * max: 100, 25969 * value: 50, 25970 * upArrowText: "More", 25971 * downArrowText: "Less" 25972 * }); 25973 */ 25974 init: function(element, options) { 25975 var that = this, 25976 isStep = options && options.step !== undefined, 25977 min, max, step, value, format; 25978 25979 Widget.fn.init.call(that, element, options); 25980 25981 options = that.options; 25982 element = that.element.addClass(INPUT) 25983 .bind({ 25984 keydown: proxy(that._keydown, that), 25985 paste: proxy(that._paste, that), 25986 blur: proxy(that._focusout, that) 25987 }); 25988 25989 element.closest("form") 25990 .bind("reset", function() { 25991 setTimeout(function() { 25992 that.value(element[0].value); 25993 }); 25994 }); 25995 25996 that._wrapper(); 25997 that._arrows(); 25998 that._input(); 25999 26000 that._text.focus(proxy(that._click, that)); 26001 26002 min = parse(element.attr("min")); 26003 max = parse(element.attr("max")); 26004 step = parse(element.attr("step")); 26005 26006 if (options.min === NULL && min !== NULL) { 26007 options.min = min; 26008 } 26009 26010 if (options.max === NULL && max !== NULL) { 26011 options.max = max; 26012 } 26013 26014 if (!isStep && step !== NULL) { 26015 options.step = step; 26016 } 26017 26018 format = options.format; 26019 if (format.slice(0,3) === "{0:") { 26020 options.format = format.slice(3, format.length - 1); 26021 } 26022 26023 value = options.value; 26024 that.value(value !== NULL ? value : element.val()); 26025 26026 that.enable(!element.is('[disabled]')); 26027 26028 kendo.notify(that); 26029 }, 26030 26031 options: { 26032 name: "NumericTextBox", 26033 decimals: NULL, 26034 min: NULL, 26035 max: NULL, 26036 value: NULL, 26037 step: 1, 26038 format: "n", 26039 placeholder: "", 26040 upArrowText: "Increase value", 26041 downArrowText: "Decrease value" 26042 }, 26043 events: [ 26044 /** 26045 * Fires when the value is changed 26046 * @name kendo.ui.NumericTextBox#change 26047 * @event 26048 * @param {Event} e 26049 * @example 26050 * $("#numeric").kendoNumericTextBox({ 26051 * change: function(e) { 26052 * // handle event 26053 * } 26054 * }); 26055 * @exampleTitle To set after initialization 26056 * @example 26057 * // get a reference to the numeric textbox widget 26058 * var numeric = $("#numeric").data("kendoNumericTextBox"); 26059 * // bind to the change event 26060 * numeric.bind("change", function(e) { 26061 * // handle event 26062 * }); 26063 */ 26064 CHANGE, 26065 /** 26066 * Fires when the value is changed from the spin buttons 26067 * @name kendo.ui.NumericTextBox#spin 26068 * @event 26069 * @param {Event} e 26070 * @example 26071 * $("#numeric").kendoNumericTextBox({ 26072 * spin: function(e) { 26073 * // handle event 26074 * } 26075 * }); 26076 * @exampleTitle To set after initialization 26077 * @example 26078 * // get a reference to the numeric textbox widget 26079 * var numeric = $("#numeric").data("kendoNumericTextBox"); 26080 * // bind to the spin event 26081 * numeric.bind("spin", function(e) { 26082 * // handle event 26083 * }); 26084 */ 26085 SPIN 26086 ], 26087 /** 26088 * Enable/Disable the numerictextbox widget. 26089 * @param {Boolean} enable The argument, which defines whether to enable/disable tha numerictextbox. 26090 * @example 26091 * // get a reference to the numeric textbox 26092 * var textbox = $("#textbox").data("kendoNumericTextBox"); 26093 * 26094 * // disables the numerictextbox 26095 * numerictextbox.enable(false); 26096 * 26097 * // enables the numerictextbox 26098 * numerictextbox.enable(true); 26099 */ 26100 enable: function(enable) { 26101 var that = this, 26102 text = that._text.add(that.element), 26103 wrapper = that._inputWrapper.unbind(HOVEREVENTS), 26104
26104 upArrow = that._upArrow.unbind(MOUSEDOWN), 26105 downArrow = that._downArrow.unbind(MOUSEDOWN); 26106 26107 that._toggleText(true); 26108 26109 if (enable === false) { 26110 wrapper 26111 .removeClass(DEFAULT) 26112 .addClass(STATEDISABLED); 26113 26114 text.attr(DISABLED, DISABLED); 26115 } else { 26116 wrapper 26117 .addClass(DEFAULT) 26118 .removeClass(STATEDISABLED) 26119 .bind(HOVEREVENTS, that._toggleHover); 26120 26121 text.removeAttr(DISABLED); 26122 26123 upArrow.bind(MOUSEDOWN, function(e) { 26124 e.preventDefault(); 26125 that._spin(1); 26126 that._upArrow.addClass(SELECTED); 26127 }); 26128 26129 downArrow.bind(MOUSEDOWN, function(e) { 26130 e.preventDefault(); 26131 that._spin(-1); 26132 that._downArrow.addClass(SELECTED); 26133 }); 26134 } 26135 }, 26136 26137 /** 26138 * Gets/Sets the value of the numerictextbox. 26139 * @param {Number | String} value The value to set. 26140 * @returns {Number} The value of the numerictextbox. 26141 * @example 26142 * // get a referene to the numeric textbox 26143 * var numerictextbox = $("#textbox").data("kendoNumericTextBox"); 26144 * 26145 * // get the value of the numerictextbox. 26146 * var value = numerictextbox.value(); 26147 * 26148 * // set the value of the numerictextbox. 26149 * numerictextbox.value("10.20"); 26150 */ 26151 value: function(value) { 26152 var that = this, adjusted; 26153 26154 if (value === undefined) { 26155 return that._value; 26156 } 26157 26158 value = parse(value); 26159 adjusted = that._adjust(value); 26160 26161 if (value !== adjusted) { 26162 return; 26163 } 26164 26165 that._update(value); 26166 that._old = that._value; 26167 }, 26168 26169 _adjust: function(value) { 26170 var that = this, 26171 options = that.options, 26172 min = options.min, 26173 max = options.max; 26174 26175 if (min !== NULL && value < min) { 26176 value = min; 26177 } else if (max !== NULL && value > max) { 26178 value = max; 26179 } 26180 26181 return value; 26182 }, 26183 26184 _arrows: function() { 26185 var that = this, 26186 arrows, 26187 options = that.options, 26188 element = that.element; 26189 26190 arrows = element.siblings(".k-icon"); 26191 26192 if (!arrows[0]) { 26193 arrows = $(buttonHtml("up", options.upArrowText) + buttonHtml("down", options.downArrowText)) 26194 .insertAfter(element); 26195 26196 arrows.wrapAll('<span class="k-select"/>'); 26197 } 26198 26199 arrows.bind(MOUSEUP, function(e) { 26200 if (!touch || kendo.eventTarget(e) != e.currentTarget || e.type === TOUCHEND) { 26201 clearTimeout( that._spinning ); 26202 } 26203 arrows.removeClass(SELECTED); 26204 }); 26205 26206 that._upArrow = arrows.eq(0); 26207 that._downArrow = arrows.eq(1); 26208 }, 26209 26210 _blur: function() { 26211 var that = this; 26212 26213 that._toggleText(true); 26214 that._change(that.element.val()); 26215 }, 26216 26217 _click: function(e) { 26218 var that = this; 26219 26220 clearTimeout(that._focusing); 26221 that._focusing = setTimeout(function() { 26222 var input = e.target, 26223 idx = caret(input), 26224 value = input.value.substring(0, idx), 26225 format = that._format(that.options.format), 26226 group = format[","], 26227 groupRegExp = new RegExp("\\" + group, "g"), 26228 extractRegExp = new RegExp("([\\d\\" + group + "]+)(\\" + format[POINT] + ")?(\\d+)?"), 26229 result = extractRegExp.exec(value), 26230 caretPosition = 0; 26231 26232 if (result) { 26233 caretPosition = result[0].replace(groupRegExp, "").length; 26234 26235 if (value.indexOf("(") != -1 && that._value < 0) { 26236 caretPosition++; 26237 } 26238 } 26239 26240 that._focusin(); 26241 26242 caret(that.element[0], caretPosition); 26243 }); 26244 }, 26245 26246 _change: function(value) { 26247 var that = this; 26248 26249 that._update(value); 26250 value = that._value; 26251 26252 if (that._old != value) { 26253 that._old = value; 26254 that.trigger(CHANGE); 26255 26256 // trigger the DOM change event so any subscriber gets notified 26257 that.element.trigger(CHANGE); 26258 } 26259 }, 26260 26261 _focusin: function() { 26262 var that = this; 26263 that._toggleText(false); 26264 that.element.focus(); 26265 that._inputWrapper.addClass(FOCUSED); 26266 }, 26267 26268 _focusout: function() { 26269 var that = this; 26270 26271 clearTimeout(that._focusing); 26272 that._inputWrapper.removeClass(FOCUSED); 26273 that._blur(); 26274 }, 26275 26276 _format: function(format) { 26277 var numberFormat = kendo.culture().numberFormat; 26278 26279 if (format.toLowerCase().indexOf("c") > -1) { 26280 numberFormat = numberFormat.currency; 26281 } else if (format.toLowerCase().indexOf("p") > -1) { 26282 numberFormat = numberFormat.percent; 26283 } 26284 26285 return numberFormat; 26286 }, 26287 26288 _input: function() { 26289 var that = this, 26290 CLASSNAME = "k-formatted-value", 26291 element = that.element.show()[0], 26292 wrapper = that.wrapper, 26293 text; 26294 26295 26296 text = wrapper.find(POINT + CLASSNAME); 26297 26298 if (!text[0]) { 26299 text = $("<input />").insertBefore(element).addClass(CLASSNAME); 26300 } 26301 26302 element.type = TYPE; 26303 text[0].type = "text"; 26304 26305 text[0].style.cssText = element.style.cssText; 26306 that._text = text.attr("readonly", true).addClass(element.className); 26307 }, 26308 26309 _keydown: function(e) { 26310 var that = this, 26311 key = e.keyCode; 26312 26313 if (key == keys.DOWN) { 26314 that._step(-1); 26315 } else if (key == keys.UP) { 26316 that._step(1); 26317 } else if (key == keys.ENTER) { 26318 that._change(that.element.val()); 26319 } 26320 26321 if (that._prevent(key) && !e.ctrlKey) { 26322 e.preventDefault(); 26323 } 26324 }, 26325 26326 _paste: function(e) { 26327 var that = this, 26328 element = e.target, 26329 value = element.value; 26330 26331 setTimeout(function() { 26332 if (parse(element.value) === NULL) { 26333 that._update(value); 26334 } 26335 }); 26336 }, 26337 26338 _prevent: function(key) { 26339 var that = this, 26340 element = that.element[0], 26341 value = element.value, 26342 options = that.options, 26343 min = options.min,
26344 numberFormat = that._format(options.format), 26345 separator = numberFormat[POINT], 26346 precision = options.decimals, 26347 idx = caret(element), 26348 prevent = true, 26349 end; 26350 26351 if (precision === NULL) { 26352 precision = numberFormat.decimals; 26353 } 26354 26355 if ((key > 16 && key < 21) || 26356 (key > 32 && key < 37) || 26357 (key > 47 && key < 58) || 26358 (key > 95 && key < 106) || 26359 key == keys.INSERT || 26360 key == keys.DELETE || 26361 key == keys.LEFT || 26362 key == keys.RIGHT || 26363 key == keys.TAB || 26364 key == keys.BACKSPACE || 26365 key == keys.ENTER) { 26366 prevent = false; 26367 } else if (decimals[key] === separator && precision > 0 && value.indexOf(separator) == -1) { 26368 prevent = false; 26369 } else if ((min === NULL || min < 0) && value.indexOf("-") == -1 && (key == 189 || key == 109) && idx === 0) { //sign 26370 prevent = false; 26371 } else if (key == 110 && precision > 0 && value.indexOf(separator) == -1) { 26372 end = value.substring(idx); 26373 26374 element.value = value.substring(0, idx) + separator + end; 26375 } 26376 26377 return prevent; 26378 }, 26379 26380 _spin: function(step, timeout) { 26381 var that = this; 26382 26383 timeout = timeout || 500; 26384 26385 clearTimeout( that._spinning ); 26386 that._spinning = setTimeout(function() { 26387 that._spin(step, 50); 26388 }, timeout ); 26389 26390 that._step(step); 26391 }, 26392 26393 _step: function(step) { 26394 var that = this, 26395 element = that.element, 26396 value = parse(element.val()) || 0; 26397 26398 if (document.activeElement != element[0]) { 26399 that._focusin(); 26400 } 26401 26402 value += that.options.step * parse(step); 26403 26404 that._update(that._adjust(value)); 26405 26406 that.trigger(SPIN); 26407 }, 26408 26409 _toggleHover: function(e) { 26410 if (!touch) { 26411 $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter"); 26412 } 26413 }, 26414 26415 _toggleText: function(toggle) { 26416 var that = this; 26417 26418 toggle = !!toggle; 26419 that._text.toggle(toggle); 26420 that.element.toggle(!toggle); 26421 }, 26422 26423 _update: function(value) { 26424 var that = this, 26425 options = that.options, 26426 format = options.format, 26427 decimals = options.decimals, 26428 numberFormat = that._format(format), 26429 isNotNull; 26430 26431 if (decimals === NULL) { 26432 decimals = numberFormat.decimals; 26433 } 26434 26435 value = parse(value); 26436 26437 isNotNull = value !== NULL; 26438 26439 if (isNotNull) { 26440 value = parseFloat(value.toFixed(decimals)); 26441 } 26442 26443 that._value = value = that._adjust(value); 26444 that._text.val(isNotNull ? kendo.toString(value, format) : options.placeholder); 26445 that.element.val(isNotNull ? value.toString().replace(POINT, numberFormat[POINT]) : ""); 26446 }, 26447 26448 _wrapper: function() { 26449 var that = this, 26450 element = that.element, 26451 wrapper; 26452 26453 wrapper = element.parents(".k-numerictextbox"); 26454 26455 if (!wrapper.is("span.k-numerictextbox")) { 26456 wrapper = element.hide().wrap('<span class="k-numeric-wrap k-state-default" />').parent(); 26457 wrapper = wrapper.wrap("<span/>").parent(); 26458 } 26459 26460 wrapper[0].style.cssText = element[0].style.cssText; 26461 element[0].style.width = ""; 26462 that.wrapper = wrapper.addClass("k-widget k-numerictextbox").show(); 26463 that._inputWrapper = $(wrapper[0].firstChild); 26464 } 26465 }); 26466 26467 function buttonHtml(className, text) { 26468 return '<span unselectable="on" class="k-link"><span unselectable="on" class="k-icon k-arrow-' + className + '" title="' + text
26468+ '">' + text + '</span></span>'; 26469 } 26470 26471 function caret(element, position) { 26472 var range, 26473 isPosition = position !== undefined; 26474 26475 if (document.selection) { 26476 if ($(element).is(":visible")) { 26477 element.focus(); 26478 } 26479 range = document.selection.createRange(); 26480 if (isPosition) { 26481 range.move("character", position); 26482 range.select(); 26483 } else { 26484 var rangeElement = element.createTextRange(), 26485 rangeDuplicated = rangeElement.duplicate(); 26486 rangeElement.moveToBookmark(range.getBookmark()); 26487 rangeDuplicated.setEndPoint('EndToStart', rangeElement); 26488 26489 position = rangeDuplicated.text.length; 26490 26491 } 26492 } else if (element.selectionStart !== undefined) { 26493 if (isPosition) { 26494 element.focus(); 26495 element.setSelectionRange(position, position); 26496 } else { 26497 position = element.selectionStart; 26498 } 26499 } 26500 26501 return position; 26502 } 26503 26504 ui.plugin(NumericTextBox); 26505})(jQuery); 26506(function ($, undefined) { 26507 /** 26508 * @name kendo.ui.Menu.Description 26509 * 26510 * @section 26511 * <p> 26512 * The <b>Menu</b> displays hierarchical data as a multi-level menu. It provides rich styling for unordered lists 26513 * of items, and can be used for both navigation and executing JavaScript commands. Items can be defined and 26514 * initialized from HTML, or the API can be used to add and remove items. 26515 * </p> 26516 * <h3>Getting Started</h3> 26517 * 26518 * @exampleTitle Create a HTML hierarchical list of items 26519 * @example 26520 * <ul id="menu"> 26521 * <li>Item 1 26522 * <ul> 26523 * <li>Item 1.1</li> 26524 * <li>Item 1.2</li> 26525 * </ul> 26526 * </li> 26527 * <li>Item 2</li> 26528 * </ul> 26529 * 26530 * @section 26531 * <p> 26532 * Initialization of a <strong>Menu</strong> should occur after the DOM is fully loaded. It is recommended that 26533 * initialization the <strong>Menu</strong> occur within a handler is provided to $(document).ready(). 26534 * </p> 26535 * 26536 * @exampleTitle Initialize a Menu using a selector within $(document).ready() 26537 * @example 26538 * $(document).ready(function() { 26539 * $("#menu").kendoMenu(); 26540 * }); 26541 * 26542 * @exampleTitle Initialize the Menu using JSON data object 26543 * @example 26544 * $(document).ready(function() { 26545 * $("#menu").kendoMenu({ 26546 * dataSource: 26547 * [{ 26548 * text: "Item 1", 26549 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 26550 * }, 26551 * { 26552 * text: "<b>Item 2</b>", 26553 * encoded: false, // Allows use of HTML for item text 26554 * content: "text" // content within an item 26555 * }, 26556 * { 26557 * text: "Item 3", 26558 * imageUrl: "http://www.kendoui.com/test.jpg", // Item image URL, optional. 26559 * items: [{ // Sub item collection 26560 * text: "Sub Item 1" 26561 * }, 26562 * { 26563 * text: "Sub Item 2" 26564 * }] 26565 * }, 26566 * { 26567 * text: "Item 4", 26568 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 26569 * }] 26570 * }) 26571 * }); 26572 * 26573 * @section 26574 * <h3>Customizing Menu Animations</h3> 26575 * <p> 26576 * By default, the <b>Menu</b> uses a slide animation to expand and 26577 * reveal sub-items as the mouse hovers. Animations can be easily 26578 * customized using configuration properties, changing the animation
26579 * style and delay. Menu items can also be configured to open on click 26580 * instead of on hover. 26581 * </p> 26582 * 26583 * @exampleTitle Changing Menu animation and open behavior 26584 * @example 26585 * $("#menu").kendoMenu({ 26586 * animation: { 26587 * open: { effects: "fadeIn" }, 26588 * hoverDelay: 500 26589 * }, 26590 * openOnClick: true 26591 * }); 26592 * 26593 * @section 26594 * <h3>Dynamically configuring Menu items</h3> 26595 * <p> 26596 * The <b>Menu</b> API provides several methods for dynamically adding 26597 * or removing Items. To add items, provide the new item as a JSON 26598 * object along with a reference item that will be used to determine the 26599 * placement in the hierarchy. 26600 * </p> 26601 * <p> 26602 * A reference item is simply a target Menu Item HTML element that 26603 * already exists in the Menu. Any valid jQuery selector can be used to 26604 * obtain a reference to the target item. For examples, see the 26605 * <a href="../menu/api.html" title="Menu API demos">Menu API demos</a>. 26606 * Removing an item only requires a reference to the target element that 26607 * should be removed. 26608 * </p> 26609 * 26610 * @exampleTitle Dynamically add a new root Menu item 26611 * @example 26612 * var menu = $("#menu").kendoMenu().data("kendoMenu"); 26613 * menu.insertAfter( 26614 * { text: "New Menu Item" }, 26615 * menu.element.children("li:last") 26616 * ); 26617 * 26618 * @section 26619 * <h3>Accessing an Existing Menu</h3> 26620 * <p> 26621 * You can reference an existing <b>Menu</b> instance via 26622 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. 26623 * Once a reference has been established, you can use the API to control 26624 * its behavior. 26625 * </p> 26626 * 26627 * @exampleTitle Accessing an existing Menu instance 26628 * @example 26629 * var menu = $("#menu").data("kendoMenu"); 26630 * 26631 */ 26632 var kendo = window.kendo, 26633 ui = kendo.ui, 26634 touch = kendo.support.touch, 26635 extend = $.extend, 26636 proxy = $.proxy, 26637 each = $.each, 26638 template = kendo.template, 26639 Widget = ui.Widget, 26640 excludedNodesRegExp = /^(ul|a|div)$/i, 26641 IMG = "img", 26642 OPEN = "open", 26643 MENU = "k-menu", 26644 LINK = "k-link", 26645 LAST = "k-last", 26646 CLOSE = "close", 26647 CLICK = touch ? "touchend" : "click", 26648 TIMER = "timer", 26649 FIRST = "k-first", 26650 IMAGE = "k-image", 26651 SELECT = "select", 26652 ZINDEX = "zIndex", 26653 MOUSEENTER = "mouseenter", 26654 MOUSELEAVE = "mouseleave", 26655 KENDOPOPUP = "kendoPopup", 26656 DEFAULTSTATE = "k-state-default", 26657 DISABLEDSTATE = "k-state-disabled", 26658 groupSelector = ".k-group", 26659 allItemsSelector = ".k-item", 26660 disabledSelector = ".k-item.k-state-disabled", 26661 itemSelector = ".k-item:not(.k-state-disabled)", 26662 linkSelector = ".k-item:not(.k-state-disabled) > .k-link", 26663 26664 templates = { 26665 content: template( 26666 "<div class='k-content k-group'>#= content(item) #</div>" 26667 ), 26668 group: template( 26669 "<ul class='#= groupCssClass(group) #'#= groupAttributes(group) #>" + 26670 "#= renderItems(data) #" + 26671 "</ul>" 26672 ), 26673 itemWrapper: template( 26674 "<#= tag(item) # class='#= textClass(item) #'#= textAttributes(item) #>" + 26675 "#= image(item) ##= sprite(item) ##= text(item) #" + 26676 "#= arrow(data) #" + 26677 "</#= tag(item) #>" 26678 ), 26679 item: template( 26680 "<li class='#= wrapperCssClass(group, item) #'>" + 26681 "#= itemWrapper(data) #" + 26682 "# if (item.items) { #" + 26683 "#= subGroup({ items: item.items, menu: menu, group: { expanded: item.expanded } }) #" + 26684 "# } #" + 26685 "</li>" 26686 ), 26687 image: template("<img class='k-image' alt='' src='#= imageUrl #' />"), 26688 arrow: template("<span class='#= arrowClass(item, group) #'></span>"), 26689 sprite: template("<span class='k-sprite #= spriteCssClass #'></span>"), 26690 empty: template("") 26691 }, 26692 26693 rendering = { 26694 /** @ignore */ 26695 wrapperCssClass: function (group, item) { 26696 var result = "k-item", 26697 index = item.index; 26698 26699 if (item.enabled === false) { 26700 result += " k-state-disabled"; 26701 } else { 26702 result += " k-state-default"; 26703 } 26704 26705 if (group.firstLevel && index === 0) { 26706 result += " k-first"; 26707 } 26708 26709 if (index == group.length-1) { 26710 result += " k-last"; 26711 } 26712 26713 return result; 26714 }, 26715 /** @ignore */ 26716 textClass: function(item) { 26717 return LINK; 26718 }, 26719 /** @ignore */ 26720 textAttributes: function(item) { 26721 return item.url ? " href='" + item.url + "'" : ""; 26722 }, 26723 /** @ignore */ 26724 arrowClass: function(item, group) { 26725 var result = "k-icon"; 26726 26727 if (group.horizontal) { 26728 result += " k-arrow-down"; 26729 } else { 26730 result += " k-arrow-right"; 26731 } 26732 26733 return result; 26734 }, 26735 /** @ignore */ 26736 text: function(item) { 26737 return item.encoded === false ? item.text : kendo.htmlEncode(item.text); 26738 }, 26739 /** @ignore */ 26740 tag: function(item) { 26741 return item.url ? "a" : "span"; 26742 }, 26743 /** @ignore */ 26744 groupAttributes: function(group) { 26745 return group.expanded !== true ? " style='display:none'" : ""; 26746 }, 26747 /** @ignore */ 26748 groupCssClass: function(group) { 26749 return "k-group"; 26750 }, 26751 /** @ignore */ 26752 content: function(item) { 26753 return item.content ? item.content : " "; 26754 } 26755 }; 26756 26757 function getEffectDirection(direction, root) { 26758 direction = direction.split(" ")[!root+0] || direction; 26759 return direction.replace("top", "up").replace("bottom", "down"); 26760 } 26761 26762 function parseDirection(direction, root) { 26763 direction = direction.split(" ")[!root+0] || direction; 26764 var output = { origin: [ "bottom", "left" ], position: [ "top", "left" ] }, 26765 horizontal = /left|right/.test(direction); 26766 26767 if (horizontal) { 26768 output.origin = [ "top", direction ]; 26769 output.position[1] = kendo.directions[direction].reverse; 26770 } else { 26771 output.origin[0] = direction; 26772 output.position[0] = kendo.directions[direction].reverse; 26773 } 26774 26775 output.origin = output.origin.join(" "); 26776 output.position = output.position.join(" "); 26777 26778 return output; 26779 } 26780 26781 function contains(parent, child) { 26782 try { 26783 return $.contains(parent, child); 26784 } catch (e) { 26785 return false; 26786 } 26787 } 26788 26789 function updateItemClasses (item) { 26790 item = $(item); 26791 26792 item.addClass("k-item") 26793 .children(IMG) 26794 .addClass(IMAGE); 26795 item 26796 .children("a") 26797 .addClass(LINK) 26798 .children(IMG) 26799 .addClass(IMAGE); 26800 item 26801 .filter(":not([disabled])") 26802 .addClass(DEFAULTSTATE); 26803 item 26804 .filter(".k-separator:empty") 26805 .append(" "); 26806 item 26807 .filter("li[disabled]") 26808 .addClass(DISABLEDSTATE) 26809 .removeAttr("disabled"); 26810 item 26811 .children("a:focus") 26812 .parent() 26813 .addClass("k-state-active"); 26814 26815 if (!item.children("." + LINK).length) { 26816 item 26817 .contents() // exclude groups, real links, templates and empty text nodes 26818 .filter(function() { return (!this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !$.trim(this.nodeValue))); }) 26819 .wrapAll("<span class='" + LINK + "'/>"); 26820 } 26821 26822 updateArrow(item); 26823 updateFirstLast(item); 26824 } 26825 26826 function updateArrow (item) { 26827 item = $(item); 26828 26829 item.find(".k-icon").remove(); 26830 26831 item.filter(":has(.k-group)") 26832 .children(".k-link:not(:has([class*=k-arrow]))") 26833 .each(function () { 26834 var item = $(this), 26835 parent = item.parent().parent(); 26836 26837 item.append("<span class='k-icon " + (parent.hasClass(MENU + "-horizontal") ? "k-arrow-down" : "k-arrow-next") + "'/>"); 26838 }); 26839 } 26840 26841 function updateFirstLast (item) { 26842 item = $(item); 26843 26844 item.filter(".k-first:not(:first-child)").removeClass(FIRST); 26845 item.filter(".k-last:not(:last-child)").removeClass(LAST); 26846 item.filter(":first-child").addClass(FIRST); 26847 item.filter(":last-child").addClass(LAST); 26848 } 26849
26850 var Menu = Widget.extend({/** @lends kendo.ui.Menu.prototype */ 26851 /** 26852 * Creates a Menu instance. 26853 * @constructs 26854 * @extends kendo.ui.Widget 26855 * @class Menu UI widget 26856 * @param {Selector} element DOM element 26857 * @param {Object} options Configuration options. 26858 * @option {Object} [animation] A collection of <b>Animation</b> objects, used to change default animations. A value of false will disable all animations in the widget. 26859 * <p>Available animations for the <b>Menu</b> are listed below. Each animation has a reverse options which is used for the <b>close</b> effect by default, but can be over-ridden 26860 * by setting the <b>close</b> animation. Each animation also has a direction which can be set off the animation (i.e. <b>slideIn:Down</b>).</p> 26861 * <div class="details-list"> 26862 * <dl> 26863 * <dt><b>slideIn</b></dt> 26864 * <dd>Menu content slides in from the top</dd> 26865 * <dt><b>fadeIn</b></dt> 26866 * <dd>Menu content fades in</dd> 26867 * <dt><b>expand</b></dt> 26868 * <dd>Menu content expands from the top down. Similar to slideIn.</dd> 26869 * </dl> 26870 * </div> 26871 * _example 26872 * $("#menu").kendoMenu({ 26873 * animation: { open: { effects: "fadeIn" } } 26874 * }); 26875 * @option {Animation} [animation.open] The animation that will be used when opening sub menus. 26876 * @option {Animation} [animation.close] The animation that will be used when closing sub menus. 26877 * @option {String} [orientation] <"horizontal"> Root menu orientation. Could be horizontal or vertical. 26878 * _example 26879 * $("#menu").kendoMenu({ 26880 * orientation: "vertical" 26881 * }); 26882 * @option {Boolean} [closeOnClick] <true> Specifies that sub menus should close after item selection (provided they won't navigate). 26883 * _example 26884 * $("#menu").kendoMenu({ 26885 * closeOnClick: false 26886 * }); 26887 * @option {Boolean} [openOnClick] <false> Specifies that the root sub menus will be opened on item click. 26888 * _example 26889 * $("#menu").kendoMenu({ 26890 * openOnClick: true 26891 * }); 26892 * @option {Number} [hoverDelay] <100> Specifies the delay in ms before the menu is opened/closed - used to avoid accidental closure on leaving. 26893 * _example 26894 * $("#menu").kendoMenu({ 26895 * hoverDelay: 200 26896 * }); 26897 * @option {String} [direction] <"default"> Specifies Menu opening direction. Can be "top", "bottom", "left", "right". 26898 * You can also specify different direction for root and sub menu items, separating them with space. The example below will initialize the root menu to open upwards and 26899 * its sub menus to the left. 26900 * _example 26901 * $("#menu").kendoMenu({ 26902 * direction: "top left" 26903 * }); 26904 * @option {String} [popupCollision] <> Specifies how Menu should adjust to screen boundaries. By default the strategy is <b>"fit"</b> for a sub menu with a horizontal parent, 26905 * meaning it will move to fit in screen boundaries in all directions, and <b>"fit flip"</b> for a sub menu with vertical parent, meaning it will fit vertically and flip over 26906 * its parent horizontally. You can also switch off the screen boundary detection completely if you set the <b>popupCollision</b> to false. 26907 * _example 26908 * $("#menu").kendoMenu({ 26909 * popupCollision: false 26910 * }); 26911 */ 26912 init: function(element, options) { 26913 var that = this; 26914 26915 Widget.fn.init.call(that, element, options); 26916 26917 element = that.wrapper = that.element; 26918 options = that.options; 26919 26920 if (options.dataSource) { 26921 that.element.empty(); 26922 that.append(options.dataSource, element); 26923 } 26924 26925 that._updateClasses(); 26926 26927 if (options.animation === false) { 26928 options.animation = { open: { show: true, effects: {} }, close: { hide: true, effects: {} } }; 26929 } 26930 26931 that.nextItemZIndex = 100; 26932 26933 element.delegate(disabledSelector, CLICK, false) 26934 .delegate(itemSelector, CLICK, proxy(that._click , that)); 26935 26936 if (!touch) { 26937 element.delegate(itemSelector, MOUSEENTER, proxy(that._mouseenter, that)) 26938 .delegate(itemSelector, MOUSELEAVE, proxy(that._mouseleave, that)) 26939 .delegate(linkSelector, MOUSEENTER + " " + MOUSELEAVE, that._toggleHover); 26940 } else { 26941 options.openOnClick = true; 26942 element.delegate(linkSelector, "touchstart touchend", that._toggleHover); 26943 } 26944 26945 $(document).click(proxy( that._documentClick, that )); 26946 that.clicked = false; 26947 26948 kendo.notify(that); 26949 }, 26950 26951 events: [ 26952 /** 26953 * Fires before a sub menu gets opened. 26954 * @name kendo.ui.Menu#open 26955 * @event 26956 * @param {Event} e 26957 * @param {Element} e.item The opened item 26958 * @example 26959 * $("#menu").kendoMenu({ 26960 * open: function(e) { 26961 * // handle event 26962 * } 26963 * }); 26964 * @exampleTitle To set after initialization 26965 * @example 26966 * // get a reference to the menu widget 26967 * var menu = $("#menu").data("kendoMenu"); 26968 * // bind to the open event 26969 * menu.bind("open", function(e) { 26970 * // handle event 26971 * }); 26972 */ 26973 OPEN, 26974 /** 26975 * Fires after a sub menu gets closed. 26976 * @name kendo.ui.Menu#close 26977 * @event 26978 * @param {Event} e 26979 * @param {Element} e.item The closed item 26980 * @example 26981 * $("#menu").kendoMenu({ 26982 * close: function(e) { 26983 * // handle event 26984 * } 26985 * }); 26986 * @exampleTitle To set after initialization 26987 * @example 26988 * // get a reference to the menu widget 26989 * var menu = $("#menu").data("kendoMenu"); 26990 * // bind to the close event 26991 * menu.bind("close", function(e) { 26992 * // handle event 26993 * }); 26994 */ 26995 CLOSE, 26996 /** 26997 * Fires when a menu item gets selected. 26998 * @name kendo.ui.Menu#select 26999 * @event 27000 * @param {Event} e 27001 * @param {Element} e.item The selected item 27002 * @example 27003 * $("#menu").kendoMenu({ 27004 * select: function(e) { 27005 * // handle event 27006 * } 27007 * }); 27008 * @exampleTitle To set after initialization 27009 * @example 27010 * // get a reference to the menu widget 27011 * var menu = $("#menu").data("kendoMenu"); 27012 * // bind to the select event 27013 * menu.bind("select", function(e) { 27014 * // handle event 27015 * }); 27016 */ 27017 SELECT 27018 ], 27019 27020 options: { 27021 name: "Menu", 27022 animation: { 27023 open: { 27024 duration: 200, 27025 show: true 27026 }, 27027 close: { // if close animation effects are defined, they will be used instead of open.reverse 27028 duration: 100 27029 } 27030 }, 27031 orientation: "horizontal", 27032 direction: "default", 27033 openOnClick: false, 27034 closeOnClick: true, 27035 hoverDelay: 100 27036 }, 27037 27038 /** 27039 * 27040 * Enables or disables an item of a <strong>Menu</strong>. This can optionally be accomplished on 27041 * initialization by setting the <b>disabled="disabled"</b> on the desired menu item html element. 27042 * 27043 * @param {Selector} element 27044 * Target element 27045 * 27046 * @param {Boolean} enable 27047 * Desired state 27048 * 27049 * @returns {Menu}
27050 * Returns the Menu object to support chaining. 27051 * 27052 * @example 27053 * // get a reference to the menu widget 27054 * var menu = $("#menu").data("kendoMenu"); 27055 * // disable the li menu item with the id "secondItem" 27056 * menu.enable("#secondItem", false); 27057 */ 27058 enable: function (element, enable) { 27059 this._toggleDisabled(element, enable !== false); 27060 27061 return this; 27062 }, 27063 27064 disable: function (element) { 27065 this._toggleDisabled(element, false); 27066 27067 return this; 27068 }, 27069 27070 /** 27071 * 27072 * Appends an item to a <strong>Menu</strong> in the specified referenceItem's sub menu. 27073 * 27074 * @param {Selector} item 27075 * Target item, specified as a JSON object. Can also handle an array of such objects. 27076 * 27077 * @param {Item} referenceItem 27078 * A reference item to append the new item in. 27079 * 27080 * @returns {Menu} 27081 * Returns the Menu object to support chaining. 27082 * 27083 * @example 27084 * // get a reference to the menu widget 27085 * var menu = $("#menu").data("kendoMenu"); 27086 * // 27087 * menu.append( 27088 * [{ 27089 * text: "Item 1", 27090 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 27091 * }, 27092 * { 27093 * text: "<b>Item 2</b>", 27094 * encoded: false, // Allows use of HTML for item text 27095 * content: "text" // content within an item 27096 * }, 27097 * { 27098 * text: "Item 3", 27099 * imageUrl: "http://www.kendoui.com/test.jpg", // Item image URL, optional. 27100 * items: [{ // Sub item collection 27101 * text: "Sub Item 1" 27102 * }, 27103 * { 27104 * text: "Sub Item 2" 27105 * }] 27106 * }, 27107 * { 27108 * text: "Item 4", 27109 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 27110 * }], 27111 * referenceItem 27112 * ); 27113 */ 27114 append: function (item, referenceItem) { 27115 referenceItem = this.element.find(referenceItem); 27116 27117 var inserted = this._insert(item, referenceItem, referenceItem.length ? referenceItem.find("> .k-group, .k-animation-container > .k-group") : null); 27118 27119 each(inserted.items, function (idx) { 27120 inserted.group.append(this); 27121 27122 var contents = inserted.contents[idx]; 27123 if (contents) { 27124 $(this).append(contents); 27125 } 27126 27127 updateArrow(this); 27128 }); 27129 27130 updateArrow(referenceItem); 27131 updateFirstLast(inserted.group.find(".k-first, .k-last").add(inserted.items)); 27132 27133 return this; 27134 }, 27135 27136 /** 27137 * 27138 * Inserts an item into a <strong>Menu</strong> before the specified referenceItem. 27139 * 27140 * @param {Selector} item 27141 * Target item, specified as a JSON object. Can also handle an array of such objects. 27142 * 27143 * @param {Selector} referenceItem 27144 * A reference item to insert the new item before 27145 * 27146 * @returns {Menu} 27147 * Returns the Menu object to support chaining. 27148 * 27149 * @example 27150 * // get a reference to the menu widget 27151 * var menu = $("#menu").data("kendoMenu"); 27152 * // 27153 * menu.insertBefore( 27154 * [{ 27155 * text: "Item 1", 27156 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 27157 * }, 27158 * { 27159 * text: "<b>Item 2</b>", 27160 * encoded: false, // Allows use of HTML for item text 27161 * content: "text" // content within an item 27162 * }, 27163 * { 27164 * text: "Item 3", 27165 * imageUrl: "http://www.kendoui.com/test.jpg", // Item image URL, optional. 27166 * items: [{ // Sub item collection 27167 * text: "Sub Item 1" 27168 * }, 27169 * { 27170 * text: "Sub Item 2" 27171 * }] 27172 * }, 27173 * { 27174 * text: "Item 4", 27175 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 27176 * }], 27177 * referenceItem 27178 * ); 27179 */ 27180 insertBefore: function (item, referenceItem) { 27181 referenceItem = this.element.find(referenceItem); 27182 27183 var inserted = this._insert(item, referenceItem, referenceItem.parent()); 27184 27185 each(inserted.items, function (idx) { 27186 referenceItem.before(this); 27187 27188 var contents = inserted.contents[idx]; 27189 if (contents) { 27190 $(this).append(contents); 27191 } 27192 27193 updateArrow(this); 27194 updateFirstLast(this); 27195 }); 27196 27197 updateFirstLast(referenceItem); 27198 27199 return this; 27200 }, 27201 27202 /** 27203 * 27204 * Inserts an item into a <strong>Menu</strong> after the specified referenceItem. 27205 * 27206 * @param {Selector} item 27207 * Target item, specified as a JSON object. Can also handle an array of such objects. 27208 * 27209 * @param {Selector} referenceItem 27210 * A reference item to insert the new item after. 27211 * 27212 * @returns {Menu}
27213 * Returns the Menu object to support chaining. 27214 * 27215 * @example 27216 * // get a reference to the menu widget 27217 * var menu = $("#menu").data("kendoMenu"); 27218 * // 27219 * menu.insertAfter( 27220 * [{ 27221 * text: "Item 1", 27222 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 27223 * }, 27224 * { 27225 * text: "<b>Item 2</b>", 27226 * encoded: false, // Allows use of HTML for item text 27227 * content: "text" // content within an item 27228 * }, 27229 * { 27230 * text: "Item 3", 27231 * imageUrl: "http://www.kendoui.com/test.jpg", // Item image URL, optional. 27232 * items: [{ // Sub item collection 27233 * text: "Sub Item 1" 27234 * }, 27235 * { 27236 * text: "Sub Item 2" 27237 * }] 27238 * }, 27239 * { 27240 * text: "Item 4", 27241 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 27242 * }], 27243 * referenceItem 27244 * ); 27245 * 27246 */ 27247 insertAfter: function (item, referenceItem) { 27248 referenceItem = this.element.find(referenceItem); 27249 27250 var inserted = this._insert(item, referenceItem, referenceItem.parent()); 27251 27252 each(inserted.items, function (idx) { 27253 referenceItem.after(this); 27254 27255 var contents = inserted.contents[idx]; 27256 if (contents) { 27257 $(this).append(contents); 27258 } 27259 27260 updateArrow(this); 27261 updateFirstLast(this); 27262 }); 27263 27264 updateFirstLast(referenceItem); 27265 27266 return this; 27267 }, 27268 27269 _insert: function (item, referenceItem, parent) { 27270 var that = this, 27271 items, groups, contents = []; 27272 27273 if (!referenceItem || !referenceItem.length) { 27274 parent = that.element; 27275 } 27276 27277 var plain = $.isPlainObject(item), 27278 groupData = { 27279 firstLevel: parent.hasClass(MENU), 27280 horizontal: parent.hasClass(MENU + "-horizontal"), 27281 expanded: true, 27282 length: parent.children().length 27283 }; 27284 27285 if (referenceItem && !parent.length) { 27286 parent = $(Menu.renderGroup({ group: groupData })).appendTo(referenceItem); 27287 } 27288 27289 if (plain || $.isArray(item)) { // is JSON 27290 items = $.map(plain ? [ item ] : item, function (value, idx) { 27291 if (typeof value === "string") { 27292 return $(value); 27293 } else { 27294 return $(Menu.renderItem({ 27295 group: groupData, 27296 item: extend(value, { index: idx }) 27297 })); 27298 } 27299 }); 27300 contents = $.map(plain ? [ item ] : item, function (value, idx) { 27301 if (value.content || value.contentUrl) { 27302 return $(Menu.renderContent({ 27303 item: extend(value, { index: idx }) 27304 })); 27305 } else { 27306 return false; 27307 } 27308 }); 27309 } else { 27310 items = $(item); 27311 groups = items.find("> ul") 27312 .addClass("k-group"); 27313 items = items.filter("li"); 27314 27315 items.add(groups.find("> li")).each(function () { 27316 updateItemClasses(this); 27317 }); 27318 } 27319 27320 return { items: items, group: parent, contents: contents }; 27321 }, 27322 27323 /** 27324 * 27325 * Removes a specified item(s) from a <strong>Menu</strong>. 27326 * 27327 * @param {Selector} element 27328 * Target item selector. 27329 * 27330 * @returns {Menu}
27331 * Returns the Menu object to support chaining. 27332 * 27333 * @example 27334 * // get a reference to the menu widget 27335 * var menu = $("#menu").data("kendoMenu"); 27336 * // remove the item with the id "Item1" 27337 * menu.remove("#Item1"); 27338 * 27339 */ 27340 remove: function (element) { 27341 element = this.element.find(element); 27342 27343 var that = this, 27344 parent = element.parentsUntil(that.element, allItemsSelector), 27345 group = element.parent("ul"); 27346 27347 element.remove(); 27348 27349 if (group && !group.children(allItemsSelector).length) { 27350 var container = group.parent(".k-animation-container"); 27351 if (container.length) { 27352 container.remove(); 27353 } else { 27354 group.remove(); 27355 } 27356 } 27357 27358 if (parent.length) { 27359 parent = parent.eq(0); 27360 27361 updateArrow(parent); 27362 updateFirstLast(parent); 27363 } 27364 27365 return that; 27366 }, 27367 27368 /** 27369 * 27370 * Opens a sub-menu of a specified item(s) in a <strong>Menu</strong>. 27371 * 27372 * @param {Selector} element 27373 * Target item selector. 27374 * 27375 * @returns {Menu} 27376 * Returns the Menu object to support chaining. 27377 * 27378 * @example 27379 * // get a reference to the menu widget 27380 * var menu = $("#menu").data("kendoMenu"); 27381 * // open the sub menu of "Item1" 27382 * menu.open("#Item1"); 27383 * 27384 */ 27385 open: function (element) { 27386 var that = this, 27387 options = that.options, 27388 horizontal = options.orientation == "horizontal", 27389 direction = options.direction; 27390 element = that.element.find(element); 27391 27392 if (/^(top|bottom|default)$/.test(direction)) { 27393 direction = horizontal ? (direction + " right").replace("default", "bottom") : "right"; 27394 } 27395 27396 element.each(function () { 27397 var li = $(this); 27398 27399 clearTimeout(li.data(TIMER)); 27400 27401 li.data(TIMER, setTimeout(function () { 27402 var ul = li.find(".k-group:first:hidden"), popup; 27403 27404 if (ul[0] && that.trigger(OPEN, { item: li[0] }) === false) { 27405 li.data(ZINDEX, li.css(ZINDEX)); 27406 li.css(ZINDEX, that.nextItemZIndex ++); 27407 27408 popup = ul.data(KENDOPOPUP); 27409 var root = li.parent().hasClass(MENU), 27410 parentHorizontal = root && horizontal, 27411 directions = parseDirection(direction, root), 27412 effects = options.animation.open.effects, 27413 openEffects = effects !== undefined ? effects : "slideIn:" + getEffectDirection(direction, root); 27414 27415 if (!popup) { 27416 popup = ul.kendoPopup({ 27417 origin: directions.origin, 27418 position: directions.position, 27419 collision: options.popupCollision !== undefined ? options.popupCollision : (parentHorizontal ? "fit" : "fit flip"), 27420 anchor: li, 27421 appendTo: li, 27422 animation: { 27423 open: extend(true, { effects: openEffects }, options.animation.open), 27424 close: options.animation.close 27425 } 27426 }).data(KENDOPOPUP); 27427 } else { 27428 popup = ul.data(KENDOPOPUP); 27429 popup.options.origin = directions.origin; 27430 popup.options.position = directions.position; 27431 popup.options.animation.open.effects = openEffects; 27432 } 27433 27434 popup.open(); 27435 } 27436 27437 }, that.options.hoverDelay)); 27438 }); 27439 27440 return that; 27441 }, 27442 27443 /** 27444 * 27445 * Closes a sub-menu of a specified item(s) in a <strong>Menu</strong>. 27446 * 27447 * @param {Selector} element Target item selector. 27448 * 27449 * @returns {Menu}
27450 * Returns the Menu object to support chaining. 27451 * 27452 * @example 27453 * // get a reference to the menu widget 27454 * var menu = $("#menu").data("kendoMenu"); 27455 * // close the sub menu of "Item1" 27456 * menu.close("#Item1"); 27457 * 27458 */ 27459 close: function (element) { 27460 var that = this; 27461 element = that.element.find(element); 27462 27463 element.each(function () { 27464 var li = $(this); 27465 27466 clearTimeout(li.data(TIMER)); 27467 27468 li.data(TIMER, setTimeout(function () { 27469 var ul = li.find(".k-group:first:visible"), popup; 27470 if (ul[0] && that.trigger(CLOSE, { item: li[0] }) === false) { 27471 li.css(ZINDEX, li.data(ZINDEX)); 27472 li.removeData(ZINDEX); 27473 27474 popup = ul.data(KENDOPOPUP); 27475 popup.close(); 27476 } 27477 }, that.options.hoverDelay)); 27478 }); 27479 27480 return that; 27481 }, 27482 27483 _toggleDisabled: function (element, enable) { 27484 element = this.element.find(element); 27485 element.each(function () { 27486 $(this) 27487 .toggleClass(DEFAULTSTATE, enable) 27488 .toggleClass(DISABLEDSTATE, !enable); 27489 }); 27490 }, 27491 27492 _toggleHover: function(e) { 27493 var target = $(kendo.eventTarget(e)).closest(allItemsSelector); 27494 27495 if (!target.parents("li." + DISABLEDSTATE).length) { 27496 target.toggleClass("k-state-hover", e.type == MOUSEENTER || e.type == "touchstart"); 27497 } 27498 }, 27499 27500 _updateClasses: function() { 27501 var that = this; 27502 27503 that.element.addClass("k-widget k-reset k-header " + MENU).addClass(MENU + "-" + that.options.orientation); 27504 27505 var items = that.element 27506 .find("li > ul") 27507 .addClass("k-group") 27508 .end() 27509 .find("> li,.k-group > li"); 27510 27511 items.each(function () { 27512 updateItemClasses(this); 27513 }); 27514 }, 27515 27516 _mouseenter: function (e) { 27517 var that = this, 27518 element = $(e.currentTarget), 27519 hasChildren = (element.children(".k-animation-container").length || element.children(groupSelector).length); 27520 27521 if (!that.options.openOnClick || that.clicked) { 27522 if (!contains(e.currentTarget, e.relatedTarget) && hasChildren) { 27523 that.open(element); 27524 } 27525 } 27526 27527 if (that.options.openOnClick && that.clicked) { 27528 element.siblings().each(proxy(function (_, sibling) { 27529 that.close(sibling); 27530 }, that)); 27531 } 27532 }, 27533 27534 _mouseleave: function (e) { 27535 var that = this, 27536 element = $(e.currentTarget), 27537 hasChildren = (element.children(".k-animation-container").length || element.children(groupSelector).length); 27538 27539 if (!that.options.openOnClick && !contains(e.currentTarget, e.relatedTarget) && hasChildren) { 27540 that.close(element); 27541 } 27542 }, 27543 27544 _click: function (e) { 27545 var that = this, openHandle, 27546 target = $(kendo.eventTarget(e)), 27547 link = target.closest("." + LINK), 27548 href = link.attr("href"), 27549 element = target.closest(allItemsSelector), 27550 isLink = (!!href && href.charAt(href.length - 1) != "#"); 27551 27552 if (element.hasClass(DISABLEDSTATE)) { 27553 e.preventDefault(); 27554 return; 27555 } 27556 27557 if (touch) { 27558 element.siblings().each(proxy(function (_, sibling) { 27559 that.close(sibling); 27560 }, that)); 27561 } 27562 27563 if (!e.handled) { // We shouldn't stop propagation. 27564 that.trigger(SELECT, { item: element[0] }); 27565 } 27566 27567 e.handled = true; 27568 27569 if (that.options.closeOnClick && !(href && href.length > 0) && !element.children(groupSelector + ",.k-animation-container").length) { 27570 that.close(link.parentsUntil(that.element, allItemsSelector)); 27571 } 27572 27573 if ((!element.parent().hasClass(MENU) || !that.options.openOnClick) && !touch) { 27574 return; 27575 } 27576 27577 if (!isLink) { 27578 e.preventDefault(); 27579 } 27580 27581 that.clicked = true; 27582 openHandle = element.children(".k-animation-container, .k-group").is(":visible") ? CLOSE : OPEN; 27583 that[openHandle](element); 27584 }, 27585 27586 _documentClick: function (e) { 27587 var that = this; 27588 27589 if (contains(that.element[0], e.target)) { 27590 return; 27591 } 27592 27593 if (that.clicked) { 27594 that.clicked = false; 27595 that.close(that.element.find(".k-item>.k-animation-container:visible").parent()); 27596 } 27597 } 27598 });
27599 27600 // client-side rendering 27601 extend(Menu, { 27602 renderItem: function (options) { 27603 options = extend({ menu: {}, group: {} }, options); 27604 27605 var empty = templates.empty, 27606 item = options.item; 27607 27608 return templates.item(extend(options, { 27609 image: item.imageUrl ? templates.image : empty, 27610 sprite: item.spriteCssClass ? templates.sprite : empty, 27611 itemWrapper: templates.itemWrapper, 27612 arrow: item.items || item.content ? templates.arrow : empty, 27613 subGroup: Menu.renderGroup 27614 }, rendering)); 27615 }, 27616 27617 renderGroup: function (options) { 27618 return templates.group(extend({ 27619 renderItems: function(options) { 27620 var html = "", 27621 i = 0, 27622 items = options.items, 27623 len = items ? items.length : 0, 27624 group = extend({ length: len }, options.group); 27625 27626 for (; i < len; i++) { 27627 html += Menu.renderItem(extend(options, { 27628 group: group, 27629 item: extend({ index: i }, items[i]) 27630 })); 27631 } 27632 27633 return html; 27634 } 27635 }, options, rendering)); 27636 }, 27637 27638 renderContent: function (options) { 27639 return templates.content(extend(options, rendering)); 27640 } 27641 }); 27642 27643 kendo.ui.plugin(Menu); 27644 27645})(jQuery); 27646(function($, undefined) { 27647 var kendo = window.kendo, 27648 ui = kendo.ui, 27649 Widget = ui.Widget, 27650 extend = $.extend, 27651 isFunction = $.isFunction, 27652 isPlainObject = $.isPlainObject, 27653 inArray = $.inArray, 27654 ERRORTEMPLATE = '<div class="k-widget k-tooltip k-tooltip-validation" style="margin:0.5em"><span class="k-icon k-warning"> </span>' + 27655 '${message}<div class="k-callout k-callout-n"></div></div>', 27656 CHANGE = "change"; 27657 27658 var specialRules = ["url", "email", "number", "date", "boolean"]; 27659 27660 function fieldType(field) { 27661 return field.type || $.type(field) || "string"; 27662 } 27663 27664 function createAttributes(options) { 27665 var field = (options.model.fields || options.model)[options.field], 27666 type = fieldType(field), 27667 validation = field.validation, 27668 ruleName, 27669 DATATYPE = kendo.attr("type"), 27670 BINDING = kendo.attr("bind"), 27671 rule, 27672 attr = { 27673 name: options.field 27674 }; 27675 27676 for (ruleName in validation) { 27677 rule = validation[ruleName]; 27678 27679 if (inArray(ruleName, specialRules) >= 0) { 27680 attr[DATATYPE] = ruleName; 27681 } else if (!isFunction(rule)) { 27682 attr[ruleName] = isPlainObject(rule) ? rule.value || ruleName : rule; 27683 } 27684 27685 attr[kendo.attr(ruleName + "-msg")] = rule.message; 27686 } 27687 27688 if (inArray(type, specialRules) >= 0) { 27689 attr[DATATYPE] = type; 27690 } 27691 27692 attr[BINDING] = (type === "boolean" ? "checked:" : "value:") + options.field; 27693 27694 return attr; 27695 } 27696 27697 var editors = { 27698 "number": function(container, options) { 27699 var attr = createAttributes(options); 27700 $('<input type="text"/>').attr(attr).appendTo(container).kendoNumericTextBox({ format: options.format }); 27701 $('<span ' + kendo.attr("for") + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container); 27702 }, 27703 "date": function(container, options) { 27704 var attr = createAttributes(options); 27705 attr[kendo.attr("format")] = options.format; 27706 27707 $('<input type="text"/>').attr(attr).appendTo(container).kendoDatePicker({ format: options.format }); 27708 $('<span ' + kendo.attr("for") + '="' + options.field + '" class="k-invalid-msg"/>').hide().appendTo(container); 27709 }, 27710 "string": function(container, options) { 27711 var attr = createAttributes(options); 27712 27713 $('<input type="text" class="k-input k-textbox"/>').attr(attr).appendTo(container); 27714 }, 27715 "boolean": function(container, options) { 27716 var attr = createAttributes(options); 27717 $('<input type="checkbox" />').attr(attr).appendTo(container); 27718 } 27719 }; 27720 27721 var Editable = Widget.extend({ 27722 init: function(element, options) { 27723 var that = this; 27724 27725 Widget.fn.init.call(that, element, options); 27726 that._validateProxy = $.proxy(that._validate, that); 27727 that.refresh();
27728 }, 27729 27730 events: [CHANGE], 27731 27732 options: { 27733 name: "Editable", 27734 editors: editors, 27735 clearContainer: true, 27736 errorTemplate: ERRORTEMPLATE 27737 }, 27738 27739 editor: function(field, modelField) { 27740 var that = this, 27741 editors = that.options.editors, 27742 isObject = isPlainObject(field), 27743 fieldName = isObject ? field.field : field, 27744 model = that.options.model || {}, 27745 type = fieldType(modelField), 27746 isCustomEditor = isObject && field.editor, 27747 editor = isCustomEditor ? field.editor : editors[type], 27748 container = that.element.find("[data-container-for=" + fieldName + "]"); 27749 27750 editor = editor ? editor : editors["string"]; 27751 27752 if (modelField) { 27753 container = container.length ? container : that.element; 27754 editor(container, extend(true, {}, isObject ? field : { field: fieldName }, { model: model })); 27755 27756 if (isCustomEditor) { 27757 container.find(":input:not(:button), select").each(function() { 27758 var bindAttr = kendo.attr("bind"), 27759 binding = this.getAttribute(bindAttr) || ""; 27760 27761 if (binding.indexOf("value:") === -1) { 27762 binding += ",value:" + fieldName; 27763 27764 $(this).attr(bindAttr, binding); 27765 } 27766 }); 27767 } 27768 } 27769 }, 27770 27771 _validate: function(e) { 27772 var that = this, 27773 isBoolean = typeof e.value === "boolean", 27774 input, 27775 preventChangeTrigger = that._validationEventInProgress, 27776 values = {}; 27777 27778 values[e.field] = e.value; 27779 27780 input = $(':input[' + kendo.attr("bind") + '="' + (isBoolean ? 'checked:' : 'value:') + e.field + '"]', that.element); 27781 27782 try { 27783 that._validationEventInProgress = true; 27784 27785 if (!that.validatable.validateInput(input) || (!preventChangeTrigger && that.trigger(CHANGE, { values: values }))) { 27786 e.preventDefault(); 27787 } 27788 27789 } finally { 27790 that._validationEventInProgress = false; 27791 } 27792 }, 27793 27794 end: function() { 27795 return this.validatable.validate(); 27796 }, 27797 27798 destroy: function() { 27799 this.options.model.unbind("set", this._validateProxy); 27800 kendo.unbind(this.element); 27801 27802 this.element.removeData("kendoValidator") 27803 .removeData("kendoEditable"); 27804 }, 27805 27806 refresh: function() { 27807 var that = this, 27808 idx, 27809 length, 27810 fields = that.options.fields || [], 27811 container = that.options.clearContainer ? that.element.empty() : that.element, 27812 model = that.options.model || {}, 27813 rules = {}; 27814 27815 if (!$.isArray(fields)) { 27816 fields = [fields]; 27817 } 27818 27819 for (idx = 0, length = fields.length; idx < length; idx++) { 27820 var field = fields[idx], 27821 isObject = isPlainObject(field), 27822 fieldName = isObject ? field.field : field, 27823 modelField = (model.fields || model)[fieldName], 27824 validation = modelField ? (modelField.validation || {}) : {}; 27825 27826 for (var rule in validation) { 27827 if (isFunction(validation[rule])) { 27828 rules[rule] = validation[rule]; 27829 } 27830 } 27831 27832 that.editor(field, modelField); 27833 } 27834 27835 kendo.bind(container, that.options.model); 27836 27837 that.options.model.bind("set", that._validateProxy); 27838 27839 that.validatable = container.kendoValidator({ 27840 validateOnBlur: false, 27841 errorTemplate: that.options.errorTemplate || undefined, 27842 rules: rules }).data("kendoValidator"); 27843 27844 container.find(":input:visible:first").focus(); 27845 } 27846 }); 27847 27848 ui.plugin(Editable); 27849})(jQuery); 27850(function($, undefined) { 27851 var kendo = window.kendo, 27852 ui = kendo.ui, 27853 DROPDOWNLIST = "kendoDropDownList", 27854 NUMERICTEXTBOX = "kendoNumericTextBox", 27855 DATEPICKER = "kendoDatePicker", 27856 proxy = $.proxy, 27857 POPUP = "kendoPopup", 27858 EQ = "Is equal to", 27859 NEQ = "Is not equal to", 27860 Widget = ui.Widget; 27861 27862 var booleanTemplate = 27863 '<div>' + 27864 '<input type="hidden" name="filters[0].field" value="#=field#"/>' + 27865 '<input type="hidden" name="filters[0].operator" value="eq"/>' + 27866 '<div class="k-filter-help-text">#=messages.info#</div>'+ 27867 '<label>#=messages.isTrue#'+ 27868 '<input type="radio" name="filters[0].value" value="true"/>' + 27869 '</label>' + 27870 '<label>#=messages.isFalse#'+ 27871 '<input type="radio" name="filters[0].value" value="false"/>' + 27872 '</label>' + 27873 '<button type="submit" class="k-button">#=messages.filter#</button>'+ 27874 '<button type="reset" class="k-button">#=messages.clear#</button>'+ 27875 '</div>'; 27876 27877 var defaultTemplate = 27878 '<div>' + 27879 '<input type="hidden" name="filters[0].field" value="#=field#"/>' + 27880 '#if(extra){#' + '<input type="hidden" name="filters[1].field" value="#=field#"/>' + '#}#'+ 27881 '<div class="k-filter-help-text">#=messages.info#</div>'+ 27882 '<select name="filters[0].operator">'+ 27883 '#for(var op in operators){#'+ 27884 '<option value="#=op#">#=operators[op]#</option>'+ 27885 '#}#'+ 27886 '</select>'+ 27887 '<input name="filters[0].value" class="k-textbox" type="text" data-#=ns#type="#=type#"/>'+ 27888 '#if(extra){#'+ 27889 '<select name="logic" class="k-filter-and">'+ 27890 '<option value="and">And</option>'+ 27891 '<option value="or">Or</option>'+ 27892 '</select>'+ 27893 '<select name="filters[1].operator">'+ 27894 '#for(var op in operators){#'+ 27895 '<option value="#=op#">#=operators[op]#</option>'+ 27896 '#}#'+ 27897 '</select>'+ 27898 '<input name="filters[1].value" class="k-textbox" type="text" data-#=ns#type="#=type#"/>'+ 27899 '#}#'+ 27900 '<button type="submit" class="k-button">#=messages.filter#</button>'+ 27901 '<button type="reset" class="k-button">#=messages.clear#</button>'+ 27902 '</div>'; 27903 27904 function removeFiltersForField(expression, field) { 27905 if (expression.filters) { 27906 expression.filters = $.grep(expression.filters, function(filter) { 27907 removeFiltersForField(filter, field); 27908 if (filter.filters) { 27909 return filter.filters.length; 27910 } else { 27911 return filter.field != field; 27912 } 27913 }); 27914 } 27915 } 27916 27917 function value(dom, val) { 27918 var widget = dom.data(DROPDOWNLIST) || dom.data(NUMERICTEXTBOX) || dom.data(DATEPICKER); 27919 27920 if (widget) { 27921 widget.value(val); 27922 } else if (dom.is(":radio")) { 27923 dom.filter("[value=" + val + "]").attr("checked", "checked"); 27924 } else { 27925 dom.val(val); 27926 } 27927 } 27928 27929 function identity(value) { 27930 return value; 27931 } 27932 27933 function toObject(array) { 27934 var result = {}, 27935 idx, 27936 length, 27937 name, 27938 members, 27939 member, 27940 value, 27941 interimResult, 27942 previousMember, 27943 parentResult; 27944 27945 for (idx = 0, length = array.length; idx < length; idx++) { 27946 members = array[idx].name.split(/[\.\[\]]+/); 27947 27948 members = $.grep(members, identity); 27949 27950 value = array[idx].value; 27951 27952 interimResult = result; 27953 27954 parentResult = result; 27955 27956 for (member = 0; member < members.length - 1; member++) { 27957 name = members[member]; 27958 27959 if (!isNaN(name)) { 27960 previousMember = members[member-1]; 27961 27962 if (!$.isArray(parentResult[previousMember])) { 27963 interimResult = parentResult[previousMember] = []; 27964 } 27965 } 27966 27967 parentResult = interimResult; 27968 27969 interimResult = interimResult[name] = interimResult[name] || {}; 27970 } 27971 27972 interimResult[members[member]] = value; 27973 } 27974 27975 return result; 27976 } 27977
27978 var FilterMenu = Widget.extend({ 27979 init: function(element, options) { 27980 var that = this, 27981 type = "string", 27982 link, 27983 field, 27984 operators; 27985 27986 Widget.fn.init.call(that, element, options); 27987 27988 operators = options.operators || {}; 27989 element = that.element; 27990 options = that.options; 27991 27992 link = element.addClass("k-filterable").find("k-grid-filter"); 27993 27994 if (!link[0]) { 27995 link = element.prepend('<a class="k-grid-filter" href="#"><span class="k-icon k-filter"/></a>').find(".k-grid-filter"); 27996 } 27997 27998 link.click(proxy(that._click, that)); 27999 28000 that.dataSource = options.dataSource.bind("change", proxy(that.refresh, that)); 28001 28002 that.field = element.attr(kendo.attr("field")); 28003 28004 that.model = that.dataSource.reader.model; 28005 28006 that._parse = function(value) { 28007 return value + ""; 28008 }; 28009 28010 if (that.model && that.model.fields) { 28011 field = that.model.fields[that.field]; 28012 28013 if (field) { 28014 type = field.type; 28015 that._parse = proxy(field.parse, field); 28016 } 28017 } 28018 28019 operators = operators[type] || options.operators[type]; 28020 28021 that.form = $('<form class="k-filter-menu k-group"/>'); 28022 that.form.html(kendo.template(type === "boolean" ? booleanTemplate : defaultTemplate)({ 28023 field: that.field, 28024 ns: kendo.ns, 28025 messages: options.messages, 28026 extra: options.extra, 28027 operators: operators, 28028 type: type 28029 })); 28030 28031 that.popup = that.form[POPUP]({ 28032 anchor: link, 28033 open: proxy(that._open, that) 28034 }).data(POPUP); 28035 28036 that.link = link; 28037 28038 that.form 28039 .bind({ 28040 submit: proxy(that._submit, that), 28041 reset: proxy(that._reset, that) 28042 }) 28043 .find("select") 28044 [DROPDOWNLIST]() 28045 .end() 28046 .find("[" + kendo.attr("type") + "=number]") 28047 [NUMERICTEXTBOX]().parent().children().removeClass("k-textbox") 28048 .end().end().end() 28049 .find("[" + kendo.attr("type") + "=date]") 28050 [DATEPICKER]().removeClass("k-textbox"); 28051 28052 that.refresh(); 28053 }, 28054 28055 refresh: function() { 28056 var that = this, 28057 expression = that.dataSource.filter() || { filters: [], logic: "and" }; 28058 28059 if (that._populateForm(expression)) { 28060 that.link.addClass("k-state-active"); 28061 } else { 28062 that.link.removeClass("k-state-active"); 28063 } 28064 }, 28065 28066 _populateForm: function(expression) { 28067 var that = this, 28068 filters = expression.filters, 28069 idx, 28070 length, 28071 form = that.form, 28072 found = false, 28073 current = 0, 28074 filter; 28075 28076 for (idx = 0, length = filters.length; idx < length; idx++) { 28077 filter = filters[idx]; 28078 if (filter.field == that.field) { 28079 value(form.find("[name='filters[" + current + "].value']"), that._parse(filter.value)); 28080 value(form.find("[name='filters[" + current + "].operator']"), filter.operator); 28081 value(form.find("[name=logic]"), expression.logic); 28082 28083 current++; 28084 found = true; 28085 } else if (filter.filters) { 28086 found = found || that._populateForm(filter); 28087 } 28088 } 28089 28090 return found; 28091 }, 28092 28093 _merge: function(expression) { 28094 var that = this, 28095 logic = expression.logic || "and", 28096 filters = expression.filters, 28097 filter, 28098 result = that.dataSource.filter() || { filters:[], logic: "and" }, 28099 idx, 28100 length; 28101 28102 removeFiltersForField(result, that.field); 28103 28104 filters = $.grep(filters, function(filter) { 28105 return filter.value !== ""; 28106 }); 28107 28108 for (idx = 0, length = filters.length; idx < length; idx++) {
28109 filter = filters[idx]; 28110 filter.value = that._parse(filter.value); 28111 } 28112 28113 if (filters.length) { 28114 if (result.filters.length) { 28115 expression.filters = filters; 28116 28117 if (result.logic !== "and") { 28118 result.filters = [ { logic: result.logic, filters: result.filters }]; 28119 result.logic = "and"; 28120 } 28121 28122 if (filters.length > 1) { 28123 result.filters.push(expression); 28124 } else { 28125 result.filters.push(filters[0]); 28126 } 28127 } else { 28128 result.filters = filters; 28129 result.logic = logic; 28130 } 28131 } 28132 28133 return result; 28134 }, 28135 28136 filter: function(expression) { 28137 expression = this._merge(expression); 28138 28139 if (expression.filters.length) { 28140 this.dataSource.filter(expression); 28141 } 28142 }, 28143 28144 clear: function() { 28145 var that = this, 28146 expression = that.dataSource.filter() || { filters:[] }; 28147 28148 expression.filters = $.grep(expression.filters, function(filter) { 28149 if (filter.filters) { 28150 filter.filters = $.grep(filter.filters, function(expr) { 28151 return expr.field != that.field; 28152 }); 28153 28154 return filter.filters.length; 28155 } 28156 28157 return filter.field != that.field; 28158 }); 28159 28160 if (!expression.filters.length) { 28161 expression = null; 28162 } 28163 28164 that.dataSource.filter(expression); 28165 }, 28166 28167 _submit: function(e) { 28168 var that = this; 28169 28170 e.preventDefault(); 28171 28172 that.filter(toObject(that.form.serializeArray())); 28173 28174 that.popup.close(); 28175 }, 28176 28177 _reset: function(e) { 28178 this.clear(); 28179 this.popup.close(); 28180 }, 28181 28182 _click: function(e) { 28183 e.preventDefault(); 28184 e.stopPropagation(); 28185 this.popup.toggle(); 28186 }, 28187 28188 _open: function() { 28189 $(".k-filter-menu").not(this.form).each(function() { 28190 $(this).data(POPUP).close(); 28191 }); 28192 }, 28193 28194 options: { 28195 name: "FilterMenu", 28196 extra: true, 28197 type: "string", 28198 operators: { 28199 string: { 28200 eq: EQ, 28201 neq: NEQ, 28202 startswith: "Starts with", 28203 contains: "Contains", 28204 endswith: "Ends with" 28205 }, 28206 number: { 28207 eq: EQ, 28208 neq: NEQ, 28209 gte: "Is greater than or equal to", 28210 gt: "Is greater than", 28211 lte: "Is less than or equal to", 28212 lt: "Is less than" 28213 }, 28214 date: { 28215 eq: EQ, 28216 neq: NEQ, 28217 gte: "Is after or equal to", 28218 gt: "Is after", 28219 lte: "Is before or equal to", 28220 lt: "Is before" 28221 } 28222 }, 28223 messages: { 28224 info: "Show rows with value that:", 28225 isTrue: "is true", 28226 isFalse: "is false", 28227 filter: "Filter", 28228 clear: "Clear" 28229 } 28230 } 28231 }); 28232 28233 ui.plugin(FilterMenu); 28234})(jQuery); 28235/** 28236 * @fileOverview Provides a PanelBar implementation which can be used to 28237 * display a hierarchical data as a multi-level, expandable panel bar. 28238 */ 28239 28240(function($, undefined) { 28241 /** 28242 * @name kendo.ui.PanelBar.Description 28243 * 28244 * @section 28245 * <p> 28246 * The <strong>PanelBar</strong> displays hierarchical data as a multi-level, expandable widget that is useful for 28247 * constained areas of a page. Its structure may be defined in HTML or c
28247onfigured dynamically through its API. The 28248 * content for items can also be loaded via AJAX by specifying a content URL. 28249 * </p> 28250 * <h3>Getting Started</h3> 28251 * <p> 28252 * A <strong>PanelBar</strong> can be created by targeting the root element of a HTML list. A 28253 * <strong>PanelBar</strong> will utilize this list to define its structure and content. 28254 * </p> 28255 * 28256 * @exampleTitle Create a list of items 28257 * @example 28258 * <ul id="panelBar"> 28259 * <li> 28260 * Item 1 28261 * <ul> 28262 * <li>Sub Item 1</li> 28263 * <li>Sub Item 2</li> 28264 * </ul> 28265 * <li> 28266 * <li>Item 2</li> 28267 * </ul> 28268 * 28269 * @section 28270 * <p></p> 28271 * <p> 28272 * Initialization of a <strong>PanelBar</strong> should occur after the DOM is fully loaded. It is recommended 28273 * that initialization the <strong>PanelBar</strong> occur within a handler is provided to $(document).ready(). 28274 * </p> 28275 * 28276 * @exampleTitle Initialize the PanelBar via an ID selector 28277 * @example 28278 * $(document).ready(function() { 28279 * $("#panelBar").kendoPanelBar(); 28280 * }); 28281 * 28282 * @section 28283 * <p> 28284 * <strong>PanelBar</strong> items may contain nested content (including markup) within a <strong>div</strong> 28285 * element. Text content located outside nested content will be used as the title of the item. 28286 * </p> 28287 * 28288 * @exampleTitle Create a list of items in HTML with nested content 28289 * @example 28290 * <ul id="panelBar"> 28291 * <li>Item with no content</li> 28292 * <li>Item with content 28293 * <div>This is nested content of a PanelBar item.</div> 28294 * </li> 28295 * </ul> 28296 * 28297 * @section 28298 * <p>A <strong>PanelBar</strong> will preserve the content defined within an item.</p> 28299 * 28300 * @exampleTitle Initialize the PanelBar via an ID selector 28301 * @example 28302 * var panelBar = $("#panelbar").kendoPanelBar(); 28303 * 28304 * @exampleTitle Initialize a PanelBar using JSON data object 28305 * @example 28306 * $("#panelbar").kendoPanelBar({ 28307 * dataSource: [ 28308 * { 28309 * text: "Item 1", 28310 * url: "http://www.kendoui.com/" // link URL if navigation is needed (optional) 28311 * }, 28312 * { 28313 * text: "<b>Item 2</b>", 28314 * encoded: false, // Allows use of HTML for item text 28315 * content: "text" // content within an item 28316 * }, 28317 * { 28318 * text: "Item 3", 28319 * contentUrl: "partialContent.html" // content URL to load within an item 28320 * }, 28321 * { 28322 * text: "Item 4", 28323 * imageUrl: "http://www.kendoui.com/test.jpg", // item image URL, optional 28324 * expanded: true, // item is rendered expanded 28325 * items: [{ // Sub item collection. 28326 * text: "Sub Item 1" 28327 * }, 28328 * { 28329 * text: "Sub Item 2" 28330 * }] 28331 * }, 28332 * { 28333 * text: "Item 5", 28334 * // item image sprite CSS class, optional 28335 * spriteCssClass: "imageClass3" 28336 * } 28337 * ] 28338 * }); 28339 * 28340 * @section 28341 * <h3>Loading Content with AJAX</h3> 28342 * <p> 28343 * While any valid technique for loading AJAX content can be used, the <strong>PanelBar</strong> provides built-in 28344 * support for asynchronously loading content from URLs. These URLs should return HTML fragments that can be 28345 * loaded in the <strong>PanelBar</strong> item content area. 28346 * </p> 28347 * 28348 * @exampleTitle Create a list of items with a target for dynamic content 28349 * @example 28350 * <ul id="panelBar"> 28351 * <li>Item 1 28352 * <ul> 28353 * <li>Sub Item 1</li> 28354 * </ul> 28355 * </li> 28356 * <li>Item 2</li> 28357 * <li> 28358 * Item with Dynamic Content 28359 * <div></div> 28360 * </li> 28361 * </ul> 28362 * 28363 * @exampleTitle Load a PanelBar item content asynchronously via AJAX 28364 * @example 28365 * $("#panelBar").kendoPanelBar({ 28366 * contentUrls:[ 28367 * null, 28368 * null, 28369 * "html-content-snippet.html" 28370 * ] 28371 * }); 28372 * 28373 * @section 28374 * <p> 28375 * When the <strong>PanelBar</strong> loads remote content via AJAX, the server response is cached in-memory so 28376 * that subsequent expand/collapse actions do not trigger subsequent AJAX requests. 28377 * </p> 28378 * <h3>Customizing PanelBar Animations</h3> 28379 * <p> 28380 * By default, a <strong>PanelBar</strong> uses animations to expand and reveal sub-items when an item header is 28381 * clicked. These animations can be modified in configuration via the open and close animation properties. A 28382 * <strong>PanelBar</strong>
28382 can also be configured to only allow one panel to remain open at a time. 28383 * </p> 28384 * 28385 * @exampleTitle Changing PanelBar animation and expandMode behavior 28386 * @example 28387 * $("#panelBar").kendoPanelBar({ 28388 * animation: { 28389 * open : { effects: "fadeIn" } 28390 * }, 28391 * expandMode: "single" 28392 * }); 28393 * 28394 * @section 28395 * <h3>Dynamically Configuring PanelBar Items</h3> 28396 * <p> 28397 * The <strong>PanelBar</strong> API provides several methods for dynamically adding or removing Items. To add 28398 * items, provide the new item as a JSON object along with a reference item that will be used to determine its 28399 * placement in the items hierarchy. Note: The reference item is optional when appending. 28400 * </p> 28401 * <p> 28402 * A reference item is a target <strong>PanelBar</strong> item HTML element that already exists in the PanelBar. 28403 * Any valid selector can be used to obtain a reference to the target item. 28404 * </p> 28405 * <p>Removing an item only requires a reference to the target element that should be removed.</p> 28406 * 28407 * @exampleTitle Dynamically adding a new root PanelBar item 28408 * @example 28409 * var panelBar = $("#panelBar").kendoPanelBar().data("kendoPanelBar"); 28410 * 28411 * panelBar.insertAfter( 28412 * { text: "New PanelBar Item" }, 28413 * panelBar.element.children("li:last") 28414 * ); 28415 * 28416 * @section 28417 * <h3>Accessing an Existing PanelBar</h3> 28418 * <p> 28419 * You can reference an existing <strong>PanelBar</strong> instance via 28420 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Once a reference has been established, you can 28421 * use the API to control its behavior. 28422 * </p> 28423 * 28424 * @exampleTitle Accessing an existing PanelBar instance 28425 * @example 28426 * var panelBar = $("#panelBar").data("kendoPanelBar"); 28427 * 28428 */ 28429 var kendo = window.kendo, 28430 ui = kendo.ui, 28431 extend = $.extend, 28432 each = $.each, 28433 template = kendo.template, 28434 Widget = ui.Widget, 28435 excludedNodesRegExp = /^(ul|a|div)$/i, 28436 IMG = "img", 28437 HREF = "href", 28438 LAST = "k-last", 28439 LINK = "k-link", 28440 ERROR = "error", 28441 CLICK = "click", 28442 ITEM = ".k-item", 28443 IMAGE = "k-image", 28444 FIRST = "k-first", 28445 EXPAND = "expand", 28446 SELECT = "select", 28447 CONTENT = "k-content", 28448 ACTIVATE = "activate", 28449 COLLAPSE = "collapse", 28450 CONTENTURL = "contentUrl", 28451 MOUSEENTER = "mouseenter", 28452 MOUSELEAVE = "mouseleave", 28453 CONTENTLOAD = "contentLoad", 28454 ACTIVECLASS = ".k-state-active", 28455 GROUPS = "> .k-panel", 28456 CONTENTS = "> .k-content", 28457 SELECTEDCLASS = ".k-state-selected", 28458 DISABLEDCLASS = ".k-state-disabled", 28459 HIGHLIGHTEDCLASS = ".k-state-highlighted", 28460 clickableItems = ITEM + ":not(.k-state-disabled) .k-link", 28461 disabledItems = ITEM + ".k-state-disabled .k-link", 28462 defaultState = "k-state-default", 28463 VISIBLE = ":visible", 28464 EMPTY = ":empty", 28465 SINGLE = "single", 28466 28467 templates = { 28468 content: template( 28469 "<div class='k-content'#= contentAttributes(data) #>#= content(item) #</div>" 28470 ), 28471 group: template( 28472 "<ul class='#= groupCssClass(group) #'#= groupAttributes(group) #>" + 28473 "#= renderItems(data) #" + 28474 "</ul>" 28475 ), 28476 itemWrapper: template( 28477 "<#= tag(item) # class='#= textClass(item, group) #'#= contentUrl(item) ##= textAttributes(item) #>" + 28478 "#= image(item) ##= sprite(item) ##= text(item) #" + 28479 "#= arrow(data) #" + 28480 "</#= tag(item) #>" 28481 ), 28482 item: template( 28483 "<li class='#= wrapperCssClass(group, item) #'>" + 28484 "#= itemWrapper(data) #" + 28485 "# if (item.items) { #" + 28486 "#= subGroup({ items: item.items, panelBar: panelBar, group: { expanded: item.expanded } }) #" + 28487 "# } #" + 28488 "</li>" 28489 ), 28490 image: template("<img class='k-image' alt='' src='#= imageUrl #' />"), 28491 arrow: template("<span class='#= arrowClass(item) #'></span>"), 28492 sprite: template("<span class='k-sprite #= spriteCssClass #'></span>"), 28493 empty: template("") 28494 }, 28495 28496 rendering = { 28497 wrapperCssClass: function (group, item) { 28498 var result = "k-item", 28499 index = item.index; 28500 28501 if (item.enabled === false) { 28502 result += " k-state-disabled"; 28503 } else if (item.expanded === true) { 28504 result += " k-state-active"; 28505 } else { 28506 result += " k-state-default"; 28507 } 28508 28509 if (index === 0) { 28510 result += " k-first"; 28511 } 28512 28513 if (index == group.length-1) { 28514 result += " k-last"; 28515 } 28516 28517 return result; 28518 }, 28519 textClass: function(item, group) { 28520 var result = LINK; 28521 28522 if (group.firstLevel) { 28523 result += " k-header"; 28524 } 28525 28526 return result; 28527 }, 28528 textAttributes: function(item) { 28529 return item.url ? " href='" + item.url + "'" : ""; 28530 }, 28531 arrowClass: function(item) { 28532 var result = "k-icon"; 28533 28534 result += item.expanded ? " k-arrow-up k-panelbar-collapse" : " k-arrow-down k-panelbar-expand"; 28535 28536 return result; 28537 }, 28538 text: function(item) { 28539 return item.encoded === false ? item.text : kendo.htmlEncode(item.text); 28540 }, 28541 tag: function(item) { 28542 return item.url ? "a" : "span"; 28543 }, 28544 groupAttributes: function(group) { 28545 return group.expanded !== true ? " style='display:none'" : ""; 28546 }, 28547 groupCssClass: function(group) { 28548 return "k-group k-panel"; 28549 }, 28550 contentAttributes: function(content) { 28551 return content.item.expanded !== true ? " style='display:none'" : ""; 28552 }, 28553 content: function(item) { 28554 return item.content ? item.content : item.contentUrl ? "" : " "; 28555 }, 28556 contentUrl: function(item) { 28557 return item.contentUrl ? kendo.attr("content-url") + '="' + item.contentUrl + '"' : ""; 28558 } 28559 }; 28560 28561 function updateItemClasses (item, panelElement) { 28562 item = $(item).addClass("k-item"); 28563 28564 item 28565 .children(IMG) 28566 .addClass(IMAGE); 28567 item 28568 .children("a") 28569 .addClass(LINK) 28570 .children(IMG) 28571 .addClass(IMAGE); 28572 item 28573 .filter(":not([disabled]):not([class*=k-state])") 28574 .addClass("k-state-default"); 28575 item 28576 .filter("li[disabled]") 28577 .addClass("k-state-disabled") 28578 .removeAttr("disabled"); 28579 item 28580 .filter(":not([class*=k-state])") 28581 .children("a:focus") 28582 .parent() 28583 .addClass(ACTIVECLASS.substr(1)); 28584 item 28585 .find(">div") 28586 .addClass(CONTENT) 28587 .css({ display: "none" }); 28588 28589 item.each(function() { 28590 var item = $(this); 28591 28592 if (!item.children("." + LINK).length) { 28593 item 28594 .contents() // exclude groups, real links, templates and empty text nodes 28595 .filter(function() { return (!this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !$.trim(this.nodeValue))); }) 28596 .wrapAll("<span class='" + LINK + "'/>"); 28597 } 28598 }); 28599 28600 panelElement 28601 .find(" > li > ." + LINK) 28602 .addClass("k-header"); 28603 } 28604 28605 function updateArrow (items) { 28606 items = $(items); 28607 28608 items.children(".k-link").children(".k-icon").remove(); 28609 28610 items 28611 .filter(":has(.k-panel),:has(.k-content)") 28612 .children(".k-link:not(:has([class*=k-arrow]))") 28613 .each(function () { 28614 var item = $(this), 28615 parent = item.parent(); 28616 28617 item.append("<span class='k-icon " + (parent.hasClass(ACTIVECLASS.substr(1)) ? "k-arrow-up k-panelbar-collapse" : "k-arrow-down k-panelbar-expand") + "'/>"); 28618 }); 28619 } 28620 28621 function updateFirstLast (items) { 28622 items = $(items); 28623 28624 items.filter(".k-first:not(:first-child)").removeClass(FIRST); 28625 items.filter(".k-last:not(:last-child)").removeClass(LAST); 28626 items.filter(":first-child").addClass(FIRST); 28627 items.filter(":last-child").addClass(LAST); 28628 } 28629 28630 var PanelBar = Widget.extend({/** @lends kendo.ui.PanelBar.prototype */ 28631 /** 28632 * 28633 * Creates a PanelBar instance. 28634 * 28635 * @constructs 28636 * @extends kendo.ui.Widget 28637 * 28638 * @param {Selector} element DOM element 28639 * @param {Object} options Configuration options. 28640 * 28641 * @option {Object} [animation] 28642 * A collection of visual animations used when <strong>PanelBar</strong> items are opened or closed through 28643 * user interactions. Setting this option to <strong>false</strong> will disable all animations. 28644 * 28645 * _exampleTitle Defining custom animations when opening and closing items 28646 * _example 28647 * $("#panelBar").kendoPanelBar({ 28648 * animation: { 28649 * // fade-out closing items over 1000 milliseconds 28650 * close: { 28651 * duration: 1000, 28652 * effects: "fadeOut" 28653 * }, 28654 * // fade-in and expand opening items over 500 milliseconds 28655 * open: { 28656 * duration: 500, 28657 * effects: "expandVertical fadeIn" 28658 * } 28659 * } 28660 * }); 28661 * 28662 * @option {Object} [animation.open] 28663 * The visual animation(s) that will be used when opening items. 28664 * 28665 * _exampleTitle Defining a custom animation when opening items that executes over 200 milliseconds 28666 * _example 28667 * $("#panelBar").kendoPanelBar({ 28668 * animation: { 28669 * open: { 28670 * duration: 200, 28671 * effects: "expandVertical" 28672 * } 28673 * } 28674 * }); 28675 * 28676 * @option {Number} [animation.open.duration] <200> 28677 * The number of milliseconds used for the visual animation when an item is opened. 28678 * 28679 * _exampleTitle Defining a custom animation for opening items that executes over 1000 milliseconds 28680 * _example 28681 * $("#panelBar").kendoPanelBar({ 28682 * animation: { 28683 * open: { 28684 * duration: 1000 28685 * } 28686 * } 28687 * }); 28688 * 28689 * @option {String} [animation.open.effects] <"expandVertical"> 28690 * A whitespace-delimited string of animation effects that are used when an item is expanded. Options include 28691 * <strong>"expandVertical"</strong> and <strong>"fadeIn"</strong>. 28692 * 28693 * @option {Boolean} [animation.open.show] <true> 28694 * 28695 * @option {Object} [animation.close] 28696 * The visual animation(s) that will be used when <strong>PanelBar</strong> items are closed. 28697 * 28698 * _exampleTitle Defining a custom animation for closing items that 28699 * executes over 200 milliseconds 28700 * _example 28701 * $("#panelBar").kendoPanelBar({ 28702 * animation: { 28703 * close: { 28704 * duration: 200, 28705 * effects: "fadeOut" 28706 * } 28707 * } 28708 * }); 28709 * 28710 * @option {Number} [animation.close.duration] <200> 28711 * The number of milliseconds used for the visual animation when a <strong>PanelBar</strong> item is closed. 28712 * 28713 * _exampleTitle Animating all closing items for 1000 milliseconds 28714 * _example 28715 * $("#panelBar").kendoPanelBar({ 28716 * animation: { 28717 * close: { 28718 duration: 1000 28719 } 28720 * } 28721 * }); 28722 * 28723 * @option {String} [animation.close.effects] 28724 * A whitespace-delimited string of animation effects that are utilized when a <strong>PanelBar</strong> item 28725 * is closed. Options include <strong>"fadeOut"</strong>. 28726 * 28727 * _exampleTitle Fading-out all closing items for 1000 milliseconds 28728 * _example 28729 * $("#panelBar").kendoPanelBar({ 28730 * animation: { 28731 * close: { 28732 * duration: 1000, 28733 * effects: "fadeOut" 28734 * } 28735 * } 28736 * }); 28737 * 28738 * @option {String} [expandMode] <"multiple"> 28739 * Specifies how the <strong>PanelBar</strong> items are displayed when opened and closed. The following values 28740 * are available: 28741 * <div class="details-list"> 28742 * <dl> 28743 * <dt>"single"</dt>
28744 * <dd>Display one item at a time when an item is opened; opening an item will close the previously opened item.</dd> 28745 * <dt>"multiple"</dt> 28746 * <dd>Display multiple values at one time; opening an item has no visual impact on any other items in the <strong>PanelBar</strong>.</dd> 28747 * </dl> 28748 * </div> 28749 * 28750 * _example 28751 * $("#panelBar").kendoPanelBar({ 28752 * expandMode: "single" 28753 * }); 28754 * 28755 */ 28756 init: function(element, options) { 28757 var that = this, 28758 content; 28759 28760 Widget.fn.init.call(that, element, options); 28761 28762 element = that.wrapper = that.element; 28763 options = that.options; 28764 28765 if (options.dataSource) { 28766 that.element.empty(); 28767 that.append(options.dataSource, element); 28768 } 28769 28770 that._updateClasses(); 28771 28772 if (options.animation === false) { 28773 options.animation = { expand: { show: true, effects: {} }, collapse: { hide:true, effects: {} } }; 28774 } 28775 28776 element 28777 .delegate(clickableItems, CLICK, $.proxy(that._click, that)) 28778 .delegate(clickableItems, MOUSEENTER + " " + MOUSELEAVE, that._toggleHover) 28779 .delegate(disabledItems, CLICK, false); 28780 28781 if (options.contentUrls) { 28782 element.find("> .k-item") 28783 .each(function(index, item) { 28784 $(item).find("." + LINK).data(CONTENTURL, options.contentUrls[index]); 28785 }); 28786 } 28787 28788 content = element.find("li" + ACTIVECLASS + " > ." + CONTENT); 28789 28790 if (content.length > 0) { 28791 that.expand(content.parent(), false); 28792 } 28793 28794 kendo.notify(that); 28795 }, 28796 28797 events: [ 28798 /** 28799 * 28800 * Triggered when an item of a PanelBar is expanded. 28801 * 28802 * @name kendo.ui.PanelBar#expand 28803 * @event 28804 * 28805 * @param {Event} e 28806 * 28807 * @param {Element} e.item 28808 * The expanding item of the PanelBar. 28809 * 28810 * @exampleTitle Attach expand event handler during initialization; detach via unbind() 28811 * @example 28812 * // event handler for expand 28813 * var onExpand = function(e) { 28814 * // access the expanded item via e.item (HTMLElement) 28815 * }; 28816 * 28817 * // attach expand event handler during initialization 28818 * var panelBar = $("#panelBar").kendoPanelBar({ 28819 * expand: onExpand 28820 * }); 28821 * 28822 * // detach expand event handler via unbind() 28823 * panelBar.data("kendoPanelBar").unbind("expand", onExpand); 28824 * 28825 * @exampleTitle Attach expand event handler via bind(); detach via unbind() 28826 * @example 28827 * // event handler for expand 28828 * var onExpand = function(e) { 28829 * // access the expanded item via e.item (HTMLElement) 28830 * }; 28831 * 28832 * // attach expand event handler via bind() 28833 * $("#panelBar").data("kendoPanelBar").bind("expand", onExpand); 28834 * 28835 * // detach expand event handler via unbind() 28836 * $("#panelBar").data("kendoPanelBar").unbind("expand", onExpand); 28837 * 28838 */ 28839 EXPAND, 28840 28841 /** 28842 * 28843 * Triggered when an item of a PanelBar is collapsed. 28844 * 28845 * @name kendo.ui.PanelBar#collapse 28846 * @event 28847 * 28848 * @param {Event} e 28849 * 28850 * @param {Element} e.item 28851 * The collapsing item of the PanelBar. 28852 * 28853 * @exampleTitle Attach collapse event handler during initialization; detach via unbind() 28854 * @example 28855 * // event handler for collapse 28856 * var onCollapse = function(e) { 28857 * // access the collapsed item via e.item (HTMLElement) 28858 * }; 28859 * 28860 * // attach collapse event handler during initialization 28861 * var panelBar = $("#panelBar").kendoPanelBar({ 28862 * collapse: onCollapse 28863 * }); 28864 * 28865 * // detach collapse event handler via unbind() 28866 * panelBar.data("kendoPanelBar").unbind("collapse", onCollapse); 28867 * 28868 * @exampleTitle Attach collapse event handler via bind(); detach via unbind() 28869 * @example 28870 * // event handler for collapse 28871 * var onCollapse = function(e) { 28872 * // access the collapsed item via e.item (HTMLElement) 28873 * }; 28874 * 28875 * // attach collapse event handler via bind() 28876 * $("#panelBar").data("kendoPanelBar").bind("collapse", onCollapse); 28877 * 28878 * // detach collapse event handler via unbind() 28879 * $("#panelBar").data("kendoPanelBar").unbind("collapse", onCollapse); 28880 * 28881 */ 28882 COLLAPSE, 28883 28884 /** 28885 * 28886 * Triggered when an item of a PanelBar is selected. 28887 * 28888 * @name kendo.ui.PanelBar#select 28889 * @event 28890 * 28891 * @param {Event} e 28892 * 28893 * @param {Element} e.item 28894 * The selected item of the PanelBar. 28895 * 28896 * @exampleTitle Attach select event handler during initialization; detach via unbind() 28897 * @example 28898 * // event handler for select 28899 * var onSelect = function(e) { 28900 * // access the selected item via e.item (HTMLElement) 28901 * }; 28902 * 28903 * // attach select event handler during initialization 28904 * var panelBar = $("#panelBar").kendoPanelBar({ 28905 * select: onSelect 28906 * }); 28907 * 28908 * // detach select event handler via unbind() 28909 * panelBar.data("kendoPanelBar").unbind("select", onSelect); 28910 * 28911 * @exampleTitle Attach select event handler via bind(); detach via unbind() 28912 * @example 28913 * // event handler for select 28914 * var onSelect = function(e) { 28915 * // access the selected item via e.item (HTMLElement) 28916 * }; 28917 * 28918 * // attach select event handler via bind() 28919 * $("#panelBar").data("kendoPanelBar").bind("select", onSelect); 28920 * 28921 * // detach select event handler via unbind() 28922 * $("#panelBar").data("kendoPanelBar").unbind("select", onSelect); 28923 * 28924 */ 28925 SELECT, 28926 28927 /** 28928 * 28929 * Triggered when an item of a PanelBar is activated. 28930 * 28931 * @name kendo.ui.PanelBar#activate 28932 * @event 28933 * 28934 * @param {Event} e 28935 * 28936 * @param {Element} e.item 28937 * The activated item of the PanelBar. 28938 * 28939 * @exampleTitle Attach activate event handler during initialization; detach via unbind() 28940 * @example 28941 * // event handler for activate 28942 * var onActivate = function(e) { 28943 * // access the activated item via e.item (HTMLElement) 28944 * }; 28945 * 28946 * // attach activate event handler during initialization 28947 * var panelBar = $("#panelBar").kendoPanelBar({ 28948 * activate: onActivate 28949 * }); 28950 * 28951 * // detach activate event handler via unbind() 28952 * panelBar.data("kendoPanelBar").unbind("activate", onActivate); 28953 * 28954 * @exampleTitle Attach activate event handler via bind(); detach via unbind() 28955 * @example 28956 * // event handler for activate 28957 * var onActivate = function(e) { 28958 * // access the activated item via e.item (HTMLElement) 28959 * }; 28960 * 28961 * // attach activate event handler via bind() 28962 * $("#panelBar").data("kendoPanelBar").bind("activate", onActivate); 28963 * 28964 * // detach activate event handler via unbind() 28965 * $("#panelBar").data("kendoPanelBar").unbind("activate", onActivate); 28966 * 28967 */ 28968 ACTIVATE, 28969 28970 /** 28971 * Fires when AJAX request results in an error. 28972 * @name kendo.ui.PanelBar#error 28973 * @event 28974 * @param {Event} e 28975 * @param {jqXHR} e.xhr The jqXHR object used to load the content 28976 * @param {String} e.status The returned status. 28977 * @example 28978 * $("#panelBar").kendoPanelBar({ 28979 * error: function(e) { 28980 * // handle event 28981 * } 28982 * }); 28983 * 28984 * @exampleTitle To set after intialization 28985 * @example 28986 * // get a reference to the panel bar 28987 * var panelBar = $("#panelBar").data("kendoPanelBar"); 28988 * // bind the error ajax event 28989 * panelBar.bind("error", function(e) { 28990 * // handle event 28991 * }); 28992 */ 28993 ERROR, 28994 /** 28995 * Fires when content is fetched from an AJAX request. 28996 * @name kendo.ui.PanelBar#contentLoad 28997 * @event 28998 * @param {Event} e 28999 * @param {Element} e.item The selected item 29000 * @param {Element} e.contentElement The loaded content element 29001 * @example 29002 * $("#panelBar").kendoPanelBar({ 29003 * contentLoad: function(e) { 29004 * // handle event 29005 * } 29006 * }); 29007 * @exampleTitle To set after intialization 29008 * @example 29009 * // get a reference to the panel bar 29010 * var panelBar = $("#panelBar").data("kendoPanelBar"); 29011 * // bind the contentLoad event 29012 * panelBar.bind("contentLoad", function(e) { 29013 * // handle event 29014 * }); 29015 */ 29016 CONTENTLOAD 29017 ], 29018 options: { 29019 name: "PanelBar", 29020 animation: { 29021 expand: { 29022 effects: "expand:vertical", 29023 duration: 200, 29024 show: true 29025 }, 29026 collapse: { // if collapse animation effects are defined, they will be used instead of expand.reverse 29027 duration: 200 29028 } 29029 }, 29030 expandMode: "multiple" 29031 }, 29032 29033 /** 29034 * 29035 * Expands the specified item(s) of a <strong>PanelBar</strong>. 29036 * 29037 * @example 29038 * // access an existing PanelBar instance 29039 * var panelBar = $("#panelBar").data("kendoPanelBar"); 29040 * // expand the element with ID, "item1"
29041 * panelBar.expand($("#item1")); 29042 * // expand the element with ID, "item2" without visual animations 29043 * panelBar.expand($("#item2"), false); 29044 * // expand all list items that start with ID, "item" 29045 * panelBar.expand($('[id^="item"]')); 29046 * 29047 * @param {Selector} element 29048 * The <strong>PanelBar</strong> item(s) to be expanded, expressed as a selector. 29049 * 29050 * @param {Boolean} [useAnimation] 29051 * Temporariliy enables (<b>true</b>) or disables (<b>false</b>) any visual animation(s) when expanding items. 29052 * 29053 * @returns {PanelBar} 29054 * Returns the PanelBar object to support chaining. 29055 * 29056 */ 29057 expand: function (element, useAnimation) { 29058 var that = this, 29059 animBackup = {}; 29060 useAnimation = useAnimation !== false; 29061 element = this.element.find(element); 29062 29063 element.each(function (index, item) { 29064 item = $(item); 29065 var groups = item.find(GROUPS).add(item.find(CONTENTS)); 29066 29067 if (!item.hasClass(DISABLEDCLASS) && groups.length > 0) { 29068 29069 if (that.options.expandMode == SINGLE && that._collapseAllExpanded(item)) { 29070 return that; 29071 } 29072 29073 element.find(HIGHLIGHTEDCLASS).removeClass(HIGHLIGHTEDCLASS.substr(1)); 29074 item.addClass(HIGHLIGHTEDCLASS.substr(1)); 29075 29076 if (!useAnimation) { 29077 animBackup = that.options.animation; 29078 that.options.animation = { expand: { show: true, effects: {} }, collapse: { hide:true, effects: {} } }; 29079 } 29080 29081 if (!that._triggerEvent(EXPAND, item)) { 29082 that._toggleItem(item, false, null); 29083 } 29084 29085 if (!useAnimation) { 29086 that.options.animation = animBackup; 29087 } 29088 } 29089 }); 29090 29091 return that; 29092 }, 29093 29094 /** 29095 * 29096 * Collapses the specified item(s) of a <strong>PanelBar</strong>. 29097 * 29098 * @example 29099 * // access an existing PanelBar instance 29100 * var panelBar = $("#panelBar").data("kendoPanelBar"); 29101 * // collapse the element with ID, "item1" 29102 * panelBar.collapse($("#item1")); 29103 * // collapse the element with ID, "item2" without visual animations 29104 * panelBar.collapse($("#item2"), false); 29105 * // collapse all list items that start with ID, "item" 29106 * panelBar.collapse($('[id^="item"]')); 29107 * 29108 * @param {Selector} element 29109 * The <strong>PanelBar</strong> item(s) to be collapsed, expressed as a string containing a selector 29110 * expression or represented by a <a href="http://api.jquery.com/category/selectors/">jQuery selector</a>. 29111 * 29112 * @param {Boolean} [useAnimation] 29113 * Temporarily enables (<strong>true</strong>) or disables (<strong>false</strong>) any visual animation(s) 29114 * when collapsing items. 29115 * 29116 * @returns {PanelBar} 29117 * Returns the PanelBar object to support chaining. 29118 * 29119 */ 29120 collapse: function (element, useAnimation) { 29121 var that = this, 29122 animBackup = {}; 29123 useAnimation = useAnimation !== false; 29124 element = that.element.find(element); 29125 29126 element.each(function (index, item) { 29127 item = $(item); 29128 var groups = item.find(GROUPS).add(item.find(CONTENTS)); 29129 29130 if (!item.hasClass(DISABLEDCLASS) && groups.is(VISIBLE)) { 29131 item.removeClass(HIGHLIGHTEDCLASS.substr(1)); 29132 29133 if (!useAnimation) { 29134 animBackup = that.options.animation; 29135 that.options.animation = { expand: { show: true, effects: {} }, collapse: { hide:true, effects: {} } }; 29136 } 29137 29138 if (!that._triggerEvent(COLLAPSE, item)) { 29139 that._toggleItem(item, true, null); 29140 } 29141 29142 if (!useAnimation) { 29143 that.options.animation = animBackup; 29144 } 29145 } 29146 29147 }); 29148 29149 return that; 29150 }, 29151 29152 _toggleDisabled: function (element, enable) { 29153 element = this.element.find(element); 29154 element 29155 .toggleClass(defaultState, enable) 29156 .toggleClass(DISABLEDCLASS.substr(1), !enable); 29157 }, 29158 29159 /** 29160 * 29161 * Selects the specified item of the <strong>PanelBar</strong>. If this method is invoked without arguments, it 29162 * returns the currently selected item. 29163 * 29164 * @param {String | Selector} element 29165 * The <strong>PanelBar</strong> item to be selected, expressed as a string containing a selector expression or 29166 * represented by a <a href="http://api.jquery.com/category/selectors/">jQuery selector</a>. 29167 * 29168 * @example 29169 * // access an existing PanelBar instance 29170 * var panelBar = $("#panelBar").data("kendoPanelBar"); 29171 * // select the item with ID, "item1" 29172 * panelBar.select("#item1"); 29173 * 29174 */ 29175 select: function (element) { 29176 var that = this; 29177 element = that.element.find(element); 29178 29179 if (arguments.length === 0) { 29180 return that.element.find(".k-item > " + SELECTEDCLASS).parent(); 29181 } 29182 29183 element.each(function (index, item) { 29184 item = $(item); 29185 var link = item.children("." + LINK); 29186 29187 if (item.is(DISABLEDCLASS)) { 29188 return that; 29189 } 29190 29191 $(SELECTEDCLASS, that.element).removeClass(SELECTEDCLASS.substr(1)); 29192 $(HIGHLIGHTEDCLASS, that.element).removeClass(HIGHLIGHTEDCLASS.substr(1)); 29193 29194 link.addClass(SELECTEDCLASS.substr(1)); 29195 link.parentsUntil(that.element, ITEM).filter(":has(.k-header)").addClass(HIGHLIGHTEDCLASS.substr(1)); 29196 }); 29197 29198 return that; 29199 }, 29200 29201 /** 29202 * 29203 * Enables (<strong>true</strong>) or disables (<strong>false</strong>) the specified item(s) of the 29204 * <strong>PanelBar</strong>. 29205 * 29206 * @example 29207 * // access an existing PanelBar instance 29208 * var panelBar = $("#panelBar").data("kendoPanelBar"); 29209 * // enable the item of the PanelBar with ID, "item1" 29210 * panelBar.enable($("#item1"), true); 29211 * // disable the currently selected item of the PanelBar 29212 * var item = panelBar.select(); 29213 * panelBar.enable(item, false); 29214 * // disable all list items that start with ID, "item" 29215 * panelBar.enable($('[id^="item"]'), false); 29216 * 29217 * @param {String | Selector} element 29218 * The <strong>PanelBar</strong> item(s) to be enabled (<b>true</b>) or disabled (<b>false</b>), expressed as a 29219 * string containing a selector expression or represented by a 29220 * <a href="http://api.jquery.com/category/selectors/">jQuery selector</a>. 29221 * 29222 * @param {Boolean} enable 29223 * The desired state - enabled (<strong>true</strong>) or disabled (<strong>false</strong>) - of the target 29224 * element(s). 29225 * 29226 */ 29227 enable: function (element, state) { 29228 this._toggleDisabled(element, state !== false); 29229 29230 return this; 29231 }, 29232 29233 disable: function (element) { 29234 this._toggleDisabled(element, false); 29235 29236 return this; 29237 }, 29238 29239 /** 29240 * 29241 * Appends an item to the PanelBar. 29242 * 29243 * @param {Selector} item 29244 * Target item, specified as the JSON representation of an object. You can pass item text, content or 29245 * contentUrl here. Can handle an HTML string or array of such strings or JSON. 29246 * 29247 * @param {Item} referenceItem 29248 * A reference item to append the new item in 29249 * 29250 * @returns {PanelBar}
29251 * Returns the PanelBar object to support chaining. 29252 * 29253 * @example 29254 * var panelBar = $("#panelBar").data("kendoPanelBar"); 29255 * panelBar.append( 29256 * [ 29257 * { 29258 * text: "Item 1", 29259 * url: "http://www.kendoui.com/" // link URL if navigation is needed (optional) 29260 * }, 29261 * { 29262 * text: "<b>Item 2</b>", 29263 * encoded: false, // Allows use of HTML for item text 29264 * content: "text" // content within an item 29265 * }, 29266 * { 29267 * text: "Item 3", 29268 * contentUrl: "partialContent.html" // content URL to load within an item 29269 * }, 29270 * { 29271 * text: "Item 4", 29272 * imageUrl: "http://www.kendoui.com/test.jpg", // item image URL, optional 29273 * expanded: true, // item is rendered expanded 29274 * items: [{ // Sub item collection. 29275 * text: "Sub Item 1" 29276 * }, 29277 * { 29278 * text: "Sub Item 2" 29279 * }] 29280 * }, 29281 * { 29282 * text: "Item 5", 29283 * // item image sprite CSS class, optional 29284 * spriteCssClass: "imageClass3" 29285 * } 29286 * ], 29287 * referenceItem 29288 * ); 29289 * 29290 */ 29291 append: function (item, referenceItem) { 29292 referenceItem = this.element.find(referenceItem); 29293 29294 var inserted = this._insert(item, referenceItem, referenceItem.length ? referenceItem.find(GROUPS) : null); 29295 29296 each(inserted.items, function (idx) { 29297 inserted.group.append(this); 29298 29299 var contents = inserted.contents[idx]; 29300 if (contents) { 29301 $(this).append(contents); 29302 } 29303 29304 updateFirstLast(this); 29305 }); 29306 29307 updateArrow(referenceItem); 29308 updateFirstLast(inserted.group.find(".k-first, .k-last")); 29309 inserted.group.height("auto"); 29310 29311 return this; 29312 }, 29313 29314 /** 29315 * 29316 * Inserts a PanelBar item before the specified referenceItem 29317 * 29318 * @param {Selector} item 29319 * Target item, specified as a JSON object. You can pass item text, content or contentUrl here. Can handle an 29320 * TML string or array of such strings or JSON. 29321 * 29322 * @param {Item} referenceItem 29323 * A reference item to insert the new item before. 29324 * 29325 * @returns {PanelBar} 29326 * Returns the PanelBar object to support chaining. 29327 * 29328 * @example 29329 * panelBar.insertBefore( 29330 * [{ 29331 * text: "Item 1", 29332 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 29333 * }, 29334 * { 29335 * text: "<b>Item 2</b>", 29336 * encoded: false, // Allows use of HTML for item text 29337 * content: "text" // Content for the content element 29338 * }, 29339 * { 29340 * text: "Item 3", 29341 * contentUrl: "partialContent.html" // From where to load the item content 29342 * }, 29343 * { 29344 * text: "Item 4", 29345 * imageUrl: "http://www.kendoui.com/test.jpg", // Item image URL, optional. 29346 * expanded: true, // item is rendered expanded 29347 * items: [{ // Sub item collection. 29348 * text: "Sub Item 1" 29349 * }, 29350 * { 29351 * text: "Sub Item 2" 29352 * }] 29353 * }, 29354 * { 29355 * text: "Item 5", 29356 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 29357 * }], 29358 * referenceItem 29359 * ); 29360 * 29361 */ 29362 insertBefore: function (item, referenceItem) { 29363 referenceItem = this.element.find(referenceItem); 29364 29365 var inserted = this._insert(item, referenceItem, referenceItem.parent()); 29366 29367 each(inserted.items, function (idx) { 29368 referenceItem.before(this); 29369 29370 var contents = inserted.contents[idx]; 29371 if (contents) { 29372 $(this).append(contents); 29373 } 29374 29375 updateFirstLast(this); 29376 }); 29377 29378 updateFirstLast(referenceItem); 29379 inserted.group.height("auto"); 29380 29381 return this; 29382 }, 29383 29384 /** 29385 * Inserts a PanelBar item after the specified referenceItem 29386 * @param {Selector} item Target item, specified as a JSON object. You can pass item text, content or contentUrl here. Can handle an HTML string or array of such strings or JSON. 29387 * @param {Item} referenceItem A reference item to insert the new item after 29388 * @example 29389 * panelBar.insertAfter( 29390 * [{ 29391 * text: "Item 1", 29392 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 29393 * }, 29394 * { 29395 * text: "<b>Item 2</b>", 29396 * encoded: false, // Allows use of HTML for item text 29397 * content: "text" // Content for the content element 29398 * }, 29399 * { 29400 * text: "Item 3", 29401 * contentUrl: "partialContent.html" // From where to load the item content 29402 * }, 29403 * { 29404 * text: "Item 4", 29405 * imageUrl: "http://www.kendoui.com/test.jpg", // Item image URL, optional. 29406 * expanded: true, // item is rendered expanded 29407 * items: [{ // Sub item collection. 29408 * text: "Sub Item 1" 29409 * }, 29410 * { 29411 * text: "Sub Item 2" 29412 * }] 29413 * }, 29414 * { 29415 * text: "Item 5", 29416 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 29417 * }], 29418 * referenceItem 29419 * ); 29420 */ 29421 insertAfter: function (item, referenceItem) { 29422 referenceItem = this.element.find(referenceItem); 29423 29424 var inserted = this._insert(item, referenceItem, referenceItem.parent()); 29425 29426 each(inserted.items, function (idx) { 29427 referenceItem.after(this); 29428 29429 var contents = inserted.contents[idx]; 29430 if (contents) { 29431 $(this).append(contents); 29432 } 29433 29434 updateFirstLast(this); 29435 }); 29436 29437 updateFirstLast(referenceItem); 29438 inserted.group.height("auto"); 29439 29440 return this; 29441 }, 29442 29443 /** 29444 * 29445 * Removes the specified PanelBar item(s). 29446 * 29447 * @param {Selector} element Target item selector. 29448 * 29449 * @example 29450 * // get a reference to the panel bar 29451 * var panelBar = $("#panelBar").data("kendoPanelBar"); 29452 * // remove Item 1 29453 * panelBar.remove("#Item1"); 29454 * 29455 */ 29456 remove: function (element) { 29457 element = this.element.find(element); 29458 29459 var that = this, 29460 parent = element.parentsUntil(that.element, ITEM), 29461 group = element.parent("ul"); 29462 29463 element.remove(); 29464 29465 if (group && !group.hasClass("k-panelbar") && !group.children(ITEM).length) { 29466 group.remove(); 29467 } 29468 29469 if (parent.length) { 29470 parent = parent.eq(0); 29471 29472 updateArrow(parent); 29473 updateFirstLast(parent); 29474 } 29475 29476 return that; 29477 }, 29478 29479 /** 29480 * Reloads the content of a <strong>PanelBar</strong> from an AJAX request. 29481 * @param {Selector} element Target element 29482 * @example 29483 * // get a reference to the panel bar 29484 * var panelBar = $("#panelBar").data("kendoPanelBar"); 29485 * // reload the panel basr 29486 * panelBar.reload(); 29487 */ 29488 reload: function (element) { 29489 var that = this; 29490 element = that.element.find(element); 29491 29492 element.each(function () { 29493 var item = $(this); 29494 29495 that._ajaxRequest(item, item.children("." + CONTENT), !item.is(VISIBLE)); 29496 }); 29497 }, 29498 29499 _insert: function (item, referenceItem, parent) { 29500 var that = this, 29501 items, contents = []; 29502 29503 if (!referenceItem || !referenceItem.length) { 29504 parent = that.element; 29505 } 29506 29507 var plain = $.isPlainObject(item), 29508 groupData = { 29509 firstLevel: parent.hasClass("k-panelbar"), 29510 expanded: parent.parent().hasClass("k-state-active"), 29511 length: parent.children().length 29512 }; 29513 29514 if (referenceItem && !parent.length) { 29515 parent = $(PanelBar.renderGroup({ group: groupData })).appendTo(referenceItem); 29516 } 29517 29518 if (plain || $.isArray(item)) { // is JSON 29519 items = $.map(plain ? [ item ] : item, function (value, idx) { 29520 if (typeof value === "string") { 29521 return $(value); 29522 } else { 29523 return $(PanelBar.renderItem({ 29524 group: groupData, 29525 item: extend(value, { index: i
29525dx }) 29526 })); 29527 } 29528 }); 29529 contents = $.map(plain ? [ item ] : item, function (value, idx) { 29530 if (value.content || value.contentUrl) { 29531 return $(PanelBar.renderContent({ 29532 item: extend(value, { index: idx }) 29533 })); 29534 } else { 29535 return false; 29536 } 29537 }); 29538 } else { 29539 items = $(item); 29540 29541 updateItemClasses(items, that.element); 29542 } 29543 29544 return { items: items, group: parent, contents: contents }; 29545 }, 29546 29547 _toggleHover: function(e) { 29548 var target = $(e.currentTarget); 29549 29550 if (!target.parents("li" + DISABLEDCLASS).length) { 29551 target.toggleClass("k-state-hover", e.type == MOUSEENTER); 29552 } 29553 }, 29554 29555 _updateClasses: function() { 29556 var that = this; 29557 29558 that.element.addClass("k-widget k-reset k-header k-panelbar"); 29559 29560 var panels = that.element 29561 .find("li > ul") 29562 .not(function () { 29563 return $(this).parentsUntil(".k-panelbar", "div").length; 29564 }) 29565 .addClass("k-group k-panel") 29566 .add(that.element); 29567 29568 var items = panels 29569 .find("> li:not(" + ACTIVECLASS + ") > ul") 29570 .css({ display: "none" }) 29571 .end() 29572 .find("> li"); 29573 29574 items.each(function () { 29575 updateItemClasses(this, that.element); 29576 }); 29577 29578 updateArrow(items); 29579 updateFirstLast(items); 29580 }, 29581 29582 _click: function (e) { 29583 var that = this, 29584 target = $(e.currentTarget), 29585 element = that.element; 29586 29587 if (target.parents("li" + DISABLEDCLASS).length) { 29588 return; 29589 } 29590 29591 if (target.closest(".k-widget")[0] != element[0]) { 29592 return; 29593 } 29594 29595 var link = target.closest("." + LINK), 29596 item = link.closest(ITEM); 29597 29598 $(SELECTEDCLASS, element).removeClass(SELECTEDCLASS.substr(1)); 29599 $(HIGHLIGHTEDCLASS, element).removeClass(HIGHLIGHTEDCLASS.substr(1)); 29600 29601 link.addClass(SELECTEDCLASS.substr(1)); 29602 link.parentsUntil(that.element, ITEM).filter(":has(.k-header)").addClass(HIGHLIGHTEDCLASS.substr(1)); 29603 29604 var contents = item.find(GROUPS).add(item.find(CONTENTS)), 29605 href = link.attr(HREF), 29606 isAnchor = link.data(CONTENTURL) || (href && (href.charAt(href.length - 1) == "#" || href.indexOf("#" + that.element[0].id + "-") != -1)); 29607 29608 if (contents.data("animating")) { 29609 return; 29610 } 29611 29612 if (that._triggerEvent(SELECT, item)) { 29613 e.preventDefault(); 29614 } 29615 29616 if (isAnchor || contents.length) { 29617 e.preventDefault(); 29618 } else { 29619 return; 29620 } 29621 29622 if (that.options.expandMode == SINGLE) { 29623 if (that._collapseAllExpanded(item)) { 29624 return; 29625 } 29626 } 29627 29628 if (contents.length) { 29629 var visibility = contents.is(VISIBLE); 29630 29631 if (!that._triggerEvent(!visibility ? EXPAND : COLLAPSE, item)) { 29632 that._toggleItem(item, visibility, e); 29633 } 29634 } 29635 }, 29636 29637 _toggleItem: function (element, isVisible, e) { 29638 var that = this, 29639 childGroup = element.find(GROUPS); 29640 29641 if (childGroup.length) { 29642 29643 this._toggleGroup(childGroup, isVisible); 29644 29645 if (e) { 29646 e.preventDefault(); 29647 } 29648 } else { 29649 29650 var content = element.find("> ." + CONTENT); 29651 29652 if (content.length) { 29653 if (e) { 29654 e.preventDefault(); 29655 } 29656 29657 if (!content.is(EMPTY)) { 29658 that._toggleGroup(content, isVisible); 29659 } else { 29660 that._ajaxRequest(element, content, isVisible); 29661 } 29662 } 29663 } 29664 }, 29665 29666 _toggleGroup: function (element, visibility) { 29667 var that = this, 29668 animationSettings = that.options.animation, 29669 animation = animationSettings.expand, 29670 collapse = extend({}, animationSettings.collapse), 29671 hasCollapseAnimation = collapse && "effects" in collapse; 29672 29673 if (element.is(VISIBLE) != visibility) { 29674 return; 29675 } 29676 29677 if (visibility) { // Set initial height on visible items (due to a Chrome bug/feature). 29678 element.css("height", element.height()); 29679 } 29680 element.css("height"); 29681 29682 element 29683 .parent() 29684 .toggleClass(defaultState, visibility) 29685 .toggleClass(ACTIVECLASS.substr(1), !visibility) 29686 .find("> .k-link > .k-icon") 29687 .toggleClass("k-arrow-up", !visibility) 29688 .toggleClass("k-panelbar-collapse", !visibility) 29689 .toggleClass("k-arrow-down", visibility) 29690 .toggleClass("k-panelbar-expand", visibility); 29691 29692 if (visibility) {
29693 animation = extend( hasCollapseAnimation ? collapse 29694 : extend({ reverse: true }, animation), { show: false, hide: true }); 29695 } else { 29696 animation = extend( { complete: function (element) { 29697 that._triggerEvent(ACTIVATE, element.closest(ITEM)); 29698 } }, animation ); 29699 } 29700 29701 element 29702 .kendoStop(true, true) 29703 .kendoAnimate( animation ); 29704 }, 29705 29706 _collapseAllExpanded: function (item) { 29707 var that = this, children, stopExpand = false; 29708 29709 if (item.find("> ." + LINK).hasClass("k-header")) { 29710 var groups = item.find(GROUPS).add(item.find(CONTENTS)); 29711 29712 if (groups.is(VISIBLE)) { 29713 stopExpand = true; 29714 } 29715 29716 if (!(groups.is(VISIBLE) || groups.length === 0)) { 29717 children = $(that.element).children(); 29718 children.find(GROUPS).add(children.find(CONTENTS)) 29719 .filter(function () { return $(this).is(VISIBLE); }) 29720 .each(function (index, content) { 29721 content = $(content); 29722 29723 stopExpand = that._triggerEvent(COLLAPSE, content.closest(ITEM)); 29724 if (!stopExpand) { 29725 that._toggleGroup(content, true); 29726 } 29727 }); 29728 } 29729 29730 return stopExpand; 29731 } 29732 }, 29733 29734 _ajaxRequest: function (element, contentElement, isVisible) { 29735 29736 var that = this, 29737 statusIcon = element.find(".k-panelbar-collapse, .k-panelbar-expand"), 29738 link = element.find("." + LINK), 29739 loadingIconTimeout = setTimeout(function () { 29740 statusIcon.addClass("k-loading"); 29741 }, 100), 29742 data = {}; 29743 29744 $.ajax({
29745 type: "GET", 29746 cache: false, 29747 url: link.data(CONTENTURL) || link.attr(HREF), 29748 dataType: "html", 29749 data: data, 29750 29751 error: function (xhr, status) { 29752 statusIcon.removeClass("k-loading"); 29753 if (that.trigger(ERROR, { xhr: xhr, status: status })) { 29754 this.complete(); 29755 } 29756 }, 29757 29758 complete: function () { 29759 clearTimeout(loadingIconTimeout); 29760 statusIcon.removeClass("k-loading"); 29761 }, 29762 29763 success: function (data, textStatus) { 29764 contentElement.html(data); 29765 that._toggleGroup(contentElement, isVisible); 29766 29767 that.trigger(CONTENTLOAD, { item: element[0], contentElement: contentElement[0] }); 29768 } 29769 }); 29770 }, 29771 29772 _triggerEvent: function (eventName, element) { 29773 var that = this; 29774 29775 return that.trigger(eventName, { item: element[0] }); 29776 } 29777 }); 29778 29779 // client-side rendering 29780 extend(PanelBar, { 29781 renderItem: function (options) { 29782 options = extend({ panelBar: {}, group: {} }, options); 29783 29784 var empty = templates.empty, 29785 item = options.item; 29786 29787 return templates.item(extend(options, { 29788 image: item.imageUrl ? templates.image : empty, 29789 sprite: item.spriteCssClass ? templates.sprite : empty, 29790 itemWrapper: templates.itemWrapper, 29791 arrow: item.items || item.content || item.contentUrl ? templates.arrow : empty, 29792 subGroup: PanelBar.renderGroup 29793 }, rendering)); 29794 }, 29795 29796 renderGroup: function (options) { 29797 return templates.group(extend({ 29798 renderItems: function(options) { 29799 var html = "", 29800 i = 0, 29801 items = options.items, 29802 len = items ? items.length : 0, 29803 group = extend({ length: len }, options.group); 29804 29805 for (; i < len; i++) { 29806 html += PanelBar.renderItem(extend(options, { 29807 group: group, 29808 item: extend({ index: i }, items[i]) 29809 })); 29810 } 29811 29812 return html; 29813 } 29814 }, options, rendering)); 29815 }, 29816 29817 renderContent: function (options) { 29818 return templates.content(extend(options, rendering)); 29819 } 29820 }); 29821 29822 kendo.ui.plugin(PanelBar); 29823 29824})(jQuery); 29825/** 29826 * @fileOverview Provides a TabStrip implementation which can be used to display a collection of tabs with associated 29827 * content 29828 */ 29829 29830(function ($, undefined) { 29831 /** 29832 * 29833 * @name kendo.ui.TabStrip.Description 29834 * 29835 * @section 29836 * <p> 29837 * A <strong>TabStrip</strong> displays a collection of tabs with associated content. It is composed of an 29838 * unordered list of items - representing tabs - and a collection of div elements, which contain the content for 29839 * each tab. 29840 * </p> 29841 * <h3>Getting Started</h3> 29842 * 29843 * @exampleTitle Create an unordered list for tabs with associated div elements for content 29844 * @example 29845 * <div id="tabStrip"> 29846 * <ul> 29847 * <li>First tab</li> 29848 * <li>Second tab</li> 29849 * </ul> 29850 * <div>First tab content</div> 29851 * <div>Second tab content</div> 29852 * </div> 29853 * 29854 * @section 29855 * <p> 29856 * Initialization of a <strong>TabStrip</strong> should occur after the DOM is fully loaded. It is recommended 29857 * that initialization the <strong>TabStrip</strong> occur within a handler is provided to $(document).ready(). 29858 * </p> 29859 * 29860 * @exampleTitle Initialize a TabStrip using a selector within $(document).ready() 29861 * @example 29862 * $(document).ready(function() { 29863 * $("#tabStrip").kendoTabStrip(); 29864 * }); 29865 * 29866 * @exampleTitle Initialize the TabStrip using JSON data object 29867 * @example 29868 * $(document).ready(function() {
29869 * $("#tabstrip").kendoTabStrip({ 29870 * dataTextField: "text", 29871 * dataContentField: "content", 29872 * dataUrlField: "url", 29873 * dataImageUrlField: "imageUrl", 29874 * dataSpriteCssClass: "spriteCssClass", 29875 * dataContentUrlField: "contentUrl", 29876 * dataSource: 29877 * [{ 29878 * text: "Item 1", 29879 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 29880 * }, 29881 * { 29882 * text: "Item 2", 29883 * content: "text" // Content for the content element 29884 * }, 29885 * { 29886 * text: "Item 3", 29887 * contentUrl: "partialContent.html" // From where to load the item content 29888 * }, 29889 * { 29890 * text: "Item 4", 29891 * imageUrl: "http://www.kendoui.com/test.jpg" // Item image URL, optional. 29892 * }, 29893 * { 29894 * text: "Item 5", 29895 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 29896 * }] 29897 * }); 29898 * }); 29899 * 29900 * @section 29901 * <p> 29902 * The tabs of a <strong>TabStrip</strong> are not required to have content. Should a tab have no content, it is 29903 * safe to omit its associated div. 29904 * </p> 29905 * <h3>Loading TabStrip content with AJAX</h3> 29906 * <p> 29907 * While any valid technique for loading AJAX content can be used, a <strong>TabStrip</strong> supports loading 29908 * content from URLs in an asynchronous manner. These URLs should return HTML fragments that can be loaded in a 29909 * TabStrip content area. 29910 * </p> 29911 * 29912 * @exampleTitle Loading Tab content asynchronously 29913 * @example 29914 * <div id="tabstrip"> 29915 * <ul> 29916 * <li>First Tab</li> 29917 * <li>Second Tab</li> 29918 * </ul> 29919 * <div></div> 29920 * <div></div> 29921 * </div> 29922 * 29923 * @exampleTitle Initialize TabStrip and configure one tab with async content loading 29924 * @example 29925 * $(document).ready(function(){ 29926 * $("#tabstrip").kendoTabStrip({ 29927 * contentUrls: [null, "html-content-snippet.html"] 29928 * }); 29929 * }); 29930 * 29931 * @section 29932 * <h3>Dynamically Configure TabStrip Tabs</h3> 29933 * <p> 29934 * The <strong>TabStrip</strong> API provides several methods for dynamically adding or removing tabs. To add 29935 * tabs, provide the new item as a JSON object along with a reference item that will be used to determine the 29936 * placement in the <strong>TabStrip</strong>. Note: append() does not require a reference item. 29937 * <p> 29938 * <p> 29939 * A reference item is simply a target DOM element of a tab that already exists in the TabStrip. Any valid 29940 * selector may be used to obtain a reference to the target item. 29941 * </p> 29942 * <p>Removing an item requires a reference to the target element.</p> 29943 * 29944 * @exampleTitle Dynamically add a new tab 29945 * @example 29946 * var tabStrip = $("#tabStrip").data("kendoTabStrip"); 29947 * tabStrip.insertAfter( 29948 * { text: "New Tab" }, 29949 * tabstrip.tabGroup.children("li:last") 29950 * ); 29951 * 29952 * @section 29953 * <h3>Selecting a Tab on Initial Load</h3> 29954 * <p> 29955 * It is possible to select a tab and display its associated content upon its initial load. There are two (2) ways 29956 * to accomplish this task: 29957 * </p> 29958 * <ol> 29959 * <li>Add a "k-state-active" class to the DOM element of the tab</li> 29960 * <li>Use select() to target and select a tab either by selector or index</li> 29961 * </ol> 29962 * <p>Both approaches produce the same result.</p> 29963 * 29964 * @exampleTitle Selecting a default tab manually using HTML 29965 * @example 29966 * <div id="tabstrip"> 29967 * <ul> 29968 * <li class="k-state-active">First Tab</li> 29969 * <li>Second Tab</li> 29970 * </ul> 29971 * <div></div> 29972 * <div></div> 29973 * </div> 29974 * 29975 * @exampleTitle Initialize a TabStrip and select first tab via select(element) 29976 * @example 29977 * $(document).ready(function(){
29978 * var tabstrip = $("#tabstrip").kendoTabStrip().data("kendoTabStrip"); 29979 * tabstrip.select(tabstrip.tabGroup.children("li:first")); 29980 * }); 29981 * 29982 * @exampleTitle Initialize a TabStrip and select first tab via select(index) 29983 * @example 29984 * $(document).ready(function(){ 29985 * var tabstrip = $("#tabstrip").kendoTabStrip().data("kendoTabStrip"); 29986 * tabstrip.select(1); 29987 * }); 29988 * 29989 * @section 29990 * <h3>Accessing an Existing TabStrip</h3> 29991 * <p> 29992 * You can reference an existing <b>TabStrip</b> instance via 29993 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Once a reference has been established, you can 29994 * use the API to control its behavior. 29995 * </p> 29996 * 29997 * @exampleTitle Accessing an existing TabStrip instance 29998 * @example 29999 * var tabStrip = $("#tabStrip").data("kendoTabStrip"); 30000 * 30001 */ 30002 var kendo = window.kendo, 30003 ui = kendo.ui, 30004 map = $.map, 30005 each = $.each, 30006 trim = $.trim, 30007 extend = $.extend, 30008 template = kendo.template, 30009 Widget = ui.Widget, 30010 excludedNodesRegExp = /^(a|div)$/i, 30011 IMG = "img", 30012 HREF = "href", 30013 LINK = "k-link", 30014 LAST = "k-last", 30015 CLICK = "click", 30016 ERROR = "error", 30017 EMPTY = ":empty", 30018 IMAGE = "k-image", 30019 FIRST = "k-first", 30020 SELECT = "select", 30021 ACTIVATE = "activate", 30022 CONTENT = "k-content", 30023 CONTENTURL = "contentUrl", 30024 MOUSEENTER = "mouseenter", 30025 MOUSELEAVE = "mouseleave", 30026 CONTENTLOAD = "contentLoad", 30027 CLICKABLEITEMS = ".k-tabstrip-items > .k-item:not(.k-state-disabled)", 30028 HOVERABLEITEMS = ".k-tabstrip-items > .k-item:not(.k-state-disabled):not(.k-state-active)", 30029 DISABLEDLINKS = ".k-tabstrip-items > .k-state-disabled .k-link", 30030 DISABLEDSTATE = "k-state-disabled", 30031 DEFAULTSTATE = "k-state-default", 30032 ACTIVESTATE = "k-state-active", 30033 HOVERSTATE = "k-state-hover", 30034 TABONTOP = "k-tab-on-top", 30035 30036 templates = { 30037 content: template( 30038 "<div class='k-content'#= contentAttributes(data) #>#= content(item) #</div>" 30039 ), 30040 itemWrapper: template( 30041 "<#= tag(item) # class='k-link'#= contentUrl(item) ##= textAttributes(item) #>" + 30042 "#= image(item) ##= sprite(item) ##= text(item) #" + 30043 "</#= tag(item) #>" 30044 ), 30045 item: template( 30046 "<li class='#= wrapperCssClass(group, item) #'>" + 30047 "#= itemWrapper(data) #" + 30048 "</li>" 30049 ), 30050 image: template("<img class='k-image' alt='' src='#= imageUrl #' />"), 30051 sprite: template("<span class='k-sprite #= spriteCssClass #'></span>"), 30052 empty: template("") 30053 }, 30054 30055 rendering = { 30056 wrapperCssClass: function (group, item) { 30057 var result = "k-item", 30058 index = item.index; 30059 30060 if (item.enabled === false) { 30061 result += " k-state-disabled"; 30062 } else { 30063 result += " k-state-default"; 30064 } 30065 30066 if (index === 0) { 30067 result += " k-first"; 30068 } 30069 30070 if (index == group.length-1) { 30071 result += " k-last"; 30072 } 30073 30074 return result; 30075 }, 30076 textAttributes: function(item) { 30077 return item.url ? " href='" + item.url + "'" : ""; 30078 }, 30079 text: function(item) { 30080 return item.encoded === false ? item.text : kendo.htmlEncode(item.text); 30081 }, 30082 tag: function(item) { 30083 return item.url ? "a" : "span"; 30084 }, 30085 contentAttributes: function(content) { 30086 return content.active !== true ? " style='display:none'" : ""; 30087 }, 30088 content: function(item) { 30089 return item.content ? item.content : item.contentUrl ? "" : " "; 30090 }, 30091 contentUrl: function(item) { 30092 return item.contentUrl ? kendo.attr("content-url") + '="' + item.contentUrl + '"' : ""; 30093 } 30094 }; 30095 30096 function updateTabClasses (tabs) { 30097 tabs.children(IMG) 30098 .addClass(IMAGE); 30099 30100 tabs.children("a") 30101 .addClass(LINK) 30102 .children(IMG) 30103 .addClass(IMAGE); 30104 30105 tabs.filter(":not([disabled]):not([class*=k-state-disabled])") 30106 .addClass(DEFAULTSTATE); 30107 30108 tabs.filter("li[disabled]") 30109 .addClass(DISABLEDSTATE) 30110 .removeAttr("disabled"); 30111 30112 tabs.filter(":not([class*=k-state])") 30113 .children("a:focus") 30114 .parent() 30115 .addClass(ACTIVESTATE + " " + TABONTOP); 30116 30117 tabs.each(function() { 30118 var item = $(this); 30119 30120 if (!item.children("." + LINK).length) { 30121 item 30122 .contents() // exclude groups, real links, templates and empty text nodes 30123 .filter(function() { return (!this.nodeName.match(excludedNodesRegExp) && !(this.nodeType == 3 && !trim(this.nodeValue))); }) 30124 .wrapAll("<a class='" + LINK + "'/>"); 30125 } 30126 }); 30127 30128 } 30129 30130 function updateFirstLast (tabGroup) { 30131 var tabs = tabGroup.children(".k-item"); 30132 30133 tabs.filter(".k-first:not(:first-child)").removeClass(FIRST); 30134 tabs.filter(".k-last:not(:last-child)").removeClass(LAST); 30135 tabs.filter(":first-child").addClass(FIRST); 30136 tabs.filter(":last-child").addClass(LAST); 30137 } 30138 30139 var TabStrip = Widget.extend({/** @lends kendo.ui.TabStrip.prototype */ 30140 /** 30141 * 30142 * Creates a TabStrip instance. 30143 * 30144 * @constructs 30145 * @extends kendo.ui.Widget 30146 * @class TabStrip UI widget 30147 * 30148 * @param {Selector} element DOM element 30149 * @param {Object} options Configuration options. 30150 * 30151 * @option {Object} [animation] 30152 * A collection of visual animations used when <strong>TabStrip</strong> tab are selected through 30153 * user interactions. Setting this option to <strong>false</strong> will disable all animations. 30154 * 30155 * _exampleTitle Defining custom animations when selecting tabs 30156 * _example
30157 * $("#tabstrip").kendoTabStrip({ 30158 * animation: { 30159 * // fade-out current tab over 1000 milliseconds 30160 * close: { 30161 * duration: 1000, 30162 * effects: "fadeOut" 30163 * }, 30164 * // fade-in new tab over 500 milliseconds 30165 * open: { 30166 * duration: 500, 30167 * effects: "fadeIn" 30168 * } 30169 * } 30170 * }); 30171 * 30172 * @option {Object} [animation.open] 30173 * The visual animation(s) that will be used when the new tab is shown. 30174 * 30175 * _exampleTitle Defining a custom animation when new tab is shown that executes over 200 milliseconds 30176 * _example 30177 * $("#tabstrip").kendoTabStrip({ 30178 * animation: { 30179 * open: { 30180 * duration: 200, 30181 * effects: "expand:vertical" 30182 * } 30183 * } 30184 * }); 30185 * 30186 * @option {Number} [animation.open.duration] <200> 30187 * The number of milliseconds used for the visual animation when a new tab is shown. 30188 * 30189 * _exampleTitle Defining animation when a new tab is shown that executes over 1000 milliseconds 30190 * _example 30191 * $("#tabstrip").kendoTabStrip({ 30192 * animation: { 30193 * open: { 30194 * duration: 1000 30195 * } 30196 * } 30197 * }); 30198 * 30199 * @option {String} [animation.open.effects] <"expand:vertical fadeIn"> 30200 * A whitespace-separated string of animation effects that are used when a new tab is shown. Options include 30201 * <strong>"expand:vertical"</strong> and <strong>"fadeIn"</strong>. 30202 * 30203 * @option {Boolean} [animation.open.show] <true> 30204 * 30205 * @option {Object} [animation.close] 30206 * The visual animation(s) that will be used when the current tab is closed. 30207 * 30208 * _exampleTitle Defining a custom animation for the current tab that 30209 * executes over 200 milliseconds 30210 * _example 30211 * $("#tabstrip").kendoTabStrip({ 30212 * animation: { 30213 * close: { 30214 * duration: 200, 30215 * effects: "fadeOut" 30216 * } 30217 * } 30218 * }); 30219 * 30220 * @option {Number} [animation.close.duration] <200> 30221 * The number of milliseconds used for the visual animation when the current tab is closed. 30222 * 30223 * _exampleTitle Animating the current tab for 1000 milliseconds 30224 * _example 30225 * $("#tabstrip").kendoTabStrip({ 30226 * animation: { 30227 * close: { 30228 duration: 1000 30229 } 30230 * } 30231 * }); 30232 * 30233 * @option {String} [animation.close.effects] 30234 * A whitespace-delimited string of animation effects that are utilized when the current tab 30235 * is closed. By default not specified - uses the opening animation with reverse. 30236 * 30237 * _exampleTitle Fading-out the current tab for 1000 milliseconds 30238 * _example 30239 * $("#tabstrip").kendoTabStrip({ 30240 * animation: { 30241 * close: { 30242 * duration: 1000, 30243 * effects: "fadeOut" 30244 * } 30245 * } 30246 * }); 30247 * 30248 * @option {String} [dataTextField] <""> Sets the field of the data item that provides the text name of the tab. 30249 * _example 30250 * $("#tabstrip").kendoTabStrip({ 30251 * dataTextField: "Name", 30252 * dataSource: data 30253 * }); 30254 * 30255 * @option {String} [dataContentField] <""> Sets the field of the data item that provides the text content of 30256 * the tab content element. 30257 * _example 30258 * $("#tabstrip").kendoTabStrip({ 30259 * dataTextField: "Name", 30260 * dataContentField: "Content", 30261 * dataSource: data 30262 * }); 30263 * 30264 * @option {String} [dataImageUrlField] <""> Sets the field of the data item that provides the image URL of 30265 * the tab. 30266 * _example
30267 * $("#tabstrip").kendoTabStrip({ 30268 * dataTextField: "Name", 30269 * dataImageUrlField: "ImageUrl", 30270 * dataSource: data 30271 * }); 30272 * 30273 * @option {String} [dataUrlField] <""> Sets the field of the data item that provides the link URL for the 30274 * tab. 30275 * _example 30276 * $("#tabstrip").kendoTabStrip({ 30277 * dataTextField: "Name", 30278 * dataUrlField: "Url", 30279 * dataSource: data 30280 * }); 30281 * 30282 * @option {String} [dataSpriteCssClass] <""> Sets the field of the data item that provides the CSS class of 30283 * the tab. 30284 * _example 30285 * $("#tabstrip").kendoTabStrip({ 30286 * dataTextField: "Name", 30287 * dataSpriteCssClass: "CssClass", 30288 * dataSource: data 30289 * }); 30290 * 30291 * @option {String} [dataContentUrlField] <""> Sets the field of the data item that provides the URL for 30292 * the ajax loaded tab content. 30293 * _example 30294 * $("#tabstrip").kendoTabStrip({ 30295 * dataTextField: "Name", 30296 * dataContentUrlField: "ContentUrl", 30297 * dataSource: data 30298 * }); 30299 * 30300 * @option {Boolean} [collapsible] <false> 30301 * Specifies whether the TabStrip should be able to collapse completely when clicking an expanded tab. 30302 * 30303 * _exampleTitle Turning on tab collapsing 30304 * _example 30305 * $("#tabstrip").kendoTabStrip({ 30306 * collapsible: true 30307 * }); 30308 * 30309 */ 30310 init: function(element, options) { 30311 var that = this; 30312 30313 that._animations(options); 30314 30315 Widget.fn.init.call(that, element, options); 30316 30317 if (that.element.is("ul")) { 30318 that.wrapper = that.element.wrapAll("<div />").parent(); 30319 } else { 30320 that.wrapper = that.element; 30321 } 30322 30323 options = that.options; 30324 30325 that.wrapper 30326 .delegate(CLICKABLEITEMS, CLICK, $.proxy(that._click, that)) 30327 .delegate(HOVERABLEITEMS, MOUSEENTER + " " + MOUSELEAVE, that._toggleHover) 30328 .delegate(DISABLEDLINKS, CLICK, false); 30329 30330 that._updateClasses(); 30331 30332 that._dataSource(); 30333 30334 if (options.dataSource) { 30335 that.dataSource.fetch(); 30336 } 30337 30338 if (that.options.contentUrls) { 30339 that.wrapper.find(".k-tabstrip-items > .k-item") 30340 .each(function(index, item) { 30341 $(item).find(">." + LINK).data(CONTENTURL, that.options.contentUrls[index]); 30342 }); 30343 } 30344 30345 var selectedItems = that.wrapper.find("li." + ACTIVESTATE), 30346 content = $(that.contentElement(selectedItems.parent().children().index(selectedItems))); 30347 30348 if (content.length > 0 && content[0].childNodes.length === 0) { 30349 that.activateTab(selectedItems.eq(0)); 30350 } 30351 30352 kendo.notify(that); 30353 }, 30354 30355 _dataSource: function() { 30356 var that = this; 30357 30358 if (that.dataSource && that._refreshHandler) { 30359 that.dataSource.unbind("change", that._refreshHandler); 30360 } else { 30361 that._refreshHandler = $.proxy(that.refresh, that); 30362 } 30363 30364 that.dataSource = kendo.data.DataSource.create(that.options.dataSource) 30365 .bind("change", that._refreshHandler); 30366 }, 30367 30368 setDataSource: function(dataSource) { 30369 this.options.dataSource = dataSource; 30370 this._dataSource(); 30371 dataSource.fetch(); 30372 }, 30373 30374 _animations: function(options) { 30375 if (options && ("animation" in options) && !options.animation) { 30376 options.animation = { open: { effects: {}, show: true }, close: { effects: {} } }; // No animation 30377 } 30378 }, 30379 30380 refresh: function(e) { 30381 var that = this, 30382 options = that.options, 30383 text = kendo.getter(options.dataTextField), 30384 content = kendo.getter(options.dataContentField), 30385 contentUrl = kendo.getter(options.dataContentUrlField), 30386 image = kendo.getter(options.dataImageUrlField), 30387 url = kendo.getter(options.dataUrlField), 30388 sprite = kendo.getter(options.dataSpriteCssClass), 30389 idx, 30390 tabs = [], 30391 tab, 30392 action, 30393 view = that.dataSource.view(), 30394 length; 30395 30396 30397 e = e || {}; 30398 action = e.action; 30399 30400 if (action) { 30401 view = e.items; 30402 } 30403 30404 for (idx = 0, length = view.length; idx < length; idx ++) { 30405 tab = { 30406 text: text(view[idx]) 30407 }; 30408 30409 if (options.dataContentField) { 30410 tab.content = content(view[idx]); 30411 } 30412 30413 if (options.dataContentUrlField) { 30414 tab.contentUrl = contentUrl(view[idx]); 30415 } 30416 30417 if (options.dataUrlField) { 30418 tab.url = url(view[idx]); 30419 } 30420 30421 if (options.dataImageUrlField) {
30422 tab.imageUrl = image(view[idx]); 30423 } 30424 30425 if (options.dataSpriteCssClass) { 30426 tab.spriteCssClass = sprite(view[idx]); 30427 } 30428 30429 tabs[idx] = tab; 30430 } 30431 30432 if (e.action == "add") { 30433 if (e.index < that.tabGroup.children().length) { 30434 that.insertBefore(tabs, that.tabGroup.children().eq(e.index)); 30435 } else { 30436 that.append(tabs); 30437 } 30438 } else if (e.action == "remove") { 30439 for (idx = 0; idx < view.length; idx++) { 30440 that.remove(e.index); 30441 } 30442 } else if (e.action == "itemchange") { 30443 idx = that.dataSource.view().indexOf(view[0]); 30444 if (e.field === options.dataTextField) { 30445 that.tabGroup.children().eq(idx).find(".k-link").text(view[0].get(e.field)); 30446 } 30447 } else { 30448 that.trigger("dataBinding"); 30449 that.append(tabs); 30450 that.trigger("dataBound"); 30451 } 30452 }, 30453 30454 value: function(value) { 30455 var that = this; 30456 30457 if (value !== undefined) { 30458 if (value != that.value()) { 30459 that.tabGroup.children().each(function() { 30460 if ($.trim($(this).text()) == value) { 30461 that.select(this); 30462 } 30463 }); 30464 } 30465 } else { 30466 return that.select().text(); 30467 } 30468 }, 30469 30470 items: function() { 30471 return this.tabGroup[0].children; 30472 }, 30473 30474 setOptions: function(options) { 30475 var animation = this.options.animation; 30476 30477 this._animations(options); 30478 30479 options.animation = extend(true, animation, options.animation); 30480 30481 Widget.fn.setOptions.call(this, options); 30482 }, 30483 30484 events: [ 30485 /** 30486 * 30487 * Triggered before a tab is selected. 30488 * 30489 * @name kendo.ui.TabStrip#select 30490 * @event 30491 * 30492 * @param {Event} e 30493 * 30494 * @param {HTMLElement} e.item 30495 * The selected item chosen by a user. 30496 * 30497 * @param {Element} e.contentElement 30498 * The content element of the tab going to be selected. 30499 * 30500 * @exampleTitle Attach select event handler during initialization; detach via unbind() 30501 * @example 30502 * // event handler for select 30503 * var onSelect = function(e) { 30504 * // access the selected item via e.item (HTMLElement) 30505 * }; 30506 * 30507 * // attach select event handler during initialization 30508 * var tabStrip = $("#tabStrip").kendoTabStrip({ 30509 * select: onSelect 30510 * }); 30511 * 30512 * // detach select event handler via unbind() 30513 * tabStrip.data("kendoTabStrip").unbind("select", onSelect); 30514 * 30515 * @exampleTitle Attach select event handler via bind(); detach via unbind() 30516 * @example 30517 * // event handler for select 30518 * var onSelect = function(e) { 30519 * // access the selected item via e.item (HTMLElement) 30520 * }; 30521 * 30522 * // attach select event handler via bind() 30523 * $("#tabStrip").data("kendoTabStrip").bind("select", onSelect); 30524 * 30525 * // detach select event handler via unbind() 30526 * $("#tabStrip").data("kendoTabStrip").unbind("select", onSelect); 30527 * 30528 */ 30529 SELECT, 30530 /** 30531 * Triggered just after a tab is being made visible, but before the end of the animation 30532 * 30533 * @name kendo.ui.TabStrip#activate 30534 * @event 30535 * 30536 * @param {Event} e 30537 * 30538 * @param {HTMLElement} e.item 30539 * The activated tab. 30540 * 30541 * @param {Element} e.contentElement 30542 * The content element of the activated tab. 30543 * 30544 * @exampleTitle Attach activate event handler during initialization; detach via unbind() 30545 * @example 30546 * // event handler for activate 30547 * var onActivate = function(e) { 30548 * // access the activated item via e.item (HTMLElement) 30549 * }; 30550 * 30551 * // attach activate event handler during initialization 30552 * var tabStrip = $("#tabStrip").kendoTabStrip({ 30553 * activate: onActivate 30554 * }); 30555 * 30556 * // detach activate event handler via unbind() 30557 * tabStrip.data("kendoTabStrip").unbind("activate", onActivate); 30558 * 30559 * @exampleTitle Attach activate event handler via bind(); detach via unbind() 30560 * @example 30561 * // event handler for activate 30562 * var onActivate = function(e) { 30563 * // access the activated item via e.item (HTMLElement) 30564 * }; 30565 * 30566 * // attach activate event handler via bind() 30567 * $("#tabStrip").data("kendoTabStrip").bind("activate", onActivate); 30568 * 30569 * // detach activate event handler via unbind() 30570 * $("#tabStrip").data("kendoTabStrip").unbind("activate", onActivate); 30571 * 30572 */ 30573 ACTIVATE, 30574 /** 30575 * 30576 * Triggered when an AJAX request results in an error. 30577 * 30578 * @name kendo.ui.TabStrip#error 30579 * @event 30580 * 30581 * @param {Event} e 30582 * 30583 * @param {jqXHR} e.xhr 30584 * The jqXHR object used to load the content 30585 * 30586 * @param {String} e.status 30587 * The returned status. 30588 * 30589 */ 30590 ERROR, 30591 /** 30592 * 30593 * Triggered when content is fetched from an AJAX request. 30594 * 30595 * @name kendo.ui.TabStrip#contentLoad 30596 * @event 30597 * 30598 * @param {Event} e 30599 * 30600 * @param {Element} e.item 30601 * The selected item 30602 * 30603 * @param {Element} e.contentElement 30604 * The loaded content element that is retrieved via AJAX. 30605 * 30606 */ 30607 CONTENTLOAD, 30608 "change", 30609 "dataBinding", 30610 "dataBound" 30611 ], 30612 30613 options: { 30614 name: "TabStrip", 30615 dataTextField: "", 30616 dataContentField: "", 30617 dataImageUrlField: "", 30618 dataUrlField: "", 30619 dataSpriteCssClass: "", 30620 dataContentUrlField: "", 30621 animation: { 30622 open: { 30623 effects: "expand:vertical fadeIn", 30624 duration: 200, 30625 show: true 30626 }, 30627 close: { // if close animation effects are defined, they will be used instead of open.reverse 30628 duration: 200 30629 } 30630 }, 30631 collapsible: false 30632 }, 30633 30634 /** 30635 * 30636 * Selects the specified tab(s) within a <strong>TabStrip</strong>. If called without arguments, it returns the 30637 * currently selected tab. 30638 * 30639 * @param {Selector/Index} element or index 30640 * The target tab(s), specified as a selector or index in the tab group. 30641 * 30642 * @example 30643 * tabStrip.select("#tab1"); 30644 * 30645 * @example 30646 * tabStrip.select(1); 30647 * 30648 * @returns {TabStrip}
30649 * Returns the TabStrip object to support chaining. 30650 * 30651 */ 30652 select: function (element) { 30653 var that = this; 30654 30655 if (arguments.length === 0) { 30656 return that.wrapper.find("li." + ACTIVESTATE); 30657 } 30658 30659 if (!isNaN(element)) { 30660 element = that.tabGroup.children().get(element); 30661 } 30662 30663 element = that.element.find(element); 30664 $(element).each(function (index, item) { 30665 item = $(item); 30666 if (!item.hasClass(ACTIVESTATE) && !that.trigger(SELECT, { item: item[0], contentElement: that.contentElement(item.index()) })) { 30667 that.activateTab(item); 30668 } 30669 }); 30670 30671 return that; 30672 }, 30673 30674 /** 30675 * 30676 * Disables (<strong>false</strong>) or enables (<strong>true</strong>) a tab(s) of a <strong>TabStrip</strong>. 30677 * 30678 * @param {Selector} element 30679 * The target tab(s), specified as a selector, to be enabled (<strong>true</strong>) or disabled 30680 * (<strong>false</strong>). 30681 * 30682 * @param {Boolean} enable 30683 * Desired state of the tab(s) specified by the selector; enabled (<strong>true</strong>) or disabled 30684 * (<strong>false</strong>). 30685 * 30686 * @returns {TabStrip} 30687 * Returns the TabStrip object to support chaining. 30688 * 30689 */ 30690 enable: function (element, state) { 30691 this._toggleDisabled(element, state !== false); 30692 30693 return this; 30694 }, 30695 30696 /** 30697 * 30698 * Disables a tab(s) of a <strong>TabStrip</strong>. 30699 * 30700 * @param {Selector} element 30701 * The target tab(s), specified as a selector, to be disabled. 30702 * 30703 * @returns {TabStrip} 30704 * Returns the TabStrip object to support chaining. 30705 * 30706 */ 30707 disable: function (element) { 30708 this._toggleDisabled(element, false); 30709 30710 return this; 30711 }, 30712 30713 /** 30714 * 30715 * Reloads TabStrip tab(s) via AJAX. 30716 * 30717 * @param {Selector} element 30718 * The target tab(s), specified as a selector, to be reloaded via AJAX. 30719 * 30720 * @returns {TabStrip} 30721 * Returns the TabStrip object to support chaining. 30722 * 30723 */ 30724 reload: function (element) { 30725 element = this.tabGroup.find(element); 30726 var that = this; 30727 30728 element.each(function () { 30729 var item = $(this), 30730 contentUrl = item.find("." + LINK).data(CONTENTURL); 30731 30732 if (contentUrl) { 30733 that.ajaxRequest(item, $(that.contentElement(item.index())), null, contentUrl); 30734 } 30735 }); 30736 30737 return that; 30738 }, 30739 30740 /** 30741 * 30742 * Appends a tab to the collection of tabs in a <strong>TabStrip</strong>. 30743 * 30744 * @param {Selector} tab 30745 * Target tab, specified as a JSON object. You can pass tab text, content or contentUrl here. Can handle an 30746 * HTML string or array of such strings or JSON. 30747 * 30748 * @returns {TabStrip} 30749 * Returns the TabStrip object to support chaining. 30750 * 30751 * @example 30752 * tabStrip.append( 30753 * [{ 30754 * text: "Item 1", 30755 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 30756 * }, 30757 * { 30758 * text: "<b>Item 2</b>", 30759 * encoded: false, // Allows use of HTML for item text 30760 * content: "text" // Content for the content element 30761 * }, 30762 * { 30763 * text: "Item 3", 30764 * contentUrl: "partialContent.html" // From where to load the item content 30765 * }, 30766 * { 30767 * text: "Item 4", 30768 * imageUrl: "http://www.kendoui.com/test.jpg" // Item image URL, optional. 30769 * }, 30770 * { 30771 * text: "Item 5", 30772 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 30773 * }] 30774 * ); 30775 * 30776 */ 30777 append: function (tab) { 30778 var that = this, 30779 inserted = that._create(tab); 30780 30781 each(inserted.tabs, function (idx) { 30782 that.tabGroup.append(this); 30783 that.wrapper.append(inserted.contents[idx]); 30784 }); 30785 30786 updateFirstLast(that.tabGroup); 30787 that._updateContentElements(); 30788 30789 return that; 30790 }, 30791 30792 /** 30793 * 30794 * Inserts a newly-created tab before a specified tab. 30795 * 30796 * @param {Selector} item 30797 * Target tab, specified as a JSON object. You can pass tab text, content or contentUrl here. Can handle an 30798 * HTML string or array of such strings or JSON. 30799 * 30800 * @param {Item} referenceTab 30801 * A reference tab to insert the new item before 30802 * 30803 * @returns {TabStrip}
30804 * Returns the TabStrip object to support chaining. 30805 * 30806 * @example 30807 * tabStrip.insertBefore( 30808 * [{ 30809 * text: "Item 1", 30810 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 30811 * }, 30812 * { 30813 * text: "<b>Item 2</b>", 30814 * encoded: false, // Allows use of HTML for item text 30815 * content: "text" // Content for the content element 30816 * }, 30817 * { 30818 * text: "Item 3", 30819 * contentUrl: "partialContent.html" // From where to load the item content 30820 * }, 30821 * { 30822 * text: "Item 4", 30823 * imageUrl: "http://www.kendoui.com/test.jpg" // Item image URL, optional. 30824 * }, 30825 * { 30826 * text: "Item 5", 30827 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 30828 * }], 30829 * referenceItem 30830 * ); 30831 */ 30832 insertBefore: function (tab, referenceTab) { 30833 var that = this, 30834 inserted = that._create(tab), 30835 referenceContent = $(that.contentElement(referenceTab.index())); 30836 30837 each(inserted.tabs, function (idx) { 30838 referenceTab.before(this); 30839 referenceContent.before(inserted.contents[idx]); 30840 }); 30841 30842 updateFirstLast(that.tabGroup); 30843 that._updateContentElements(); 30844 30845 return that; 30846 }, 30847 30848 /** 30849 * 30850 * Inserts a newly-created tab after a specified tab. 30851 * 30852 * @param {Selector} item 30853 * Target tab, specified as a JSON object. You can pass tab text, content or contentUrl here. Can handle an 30854 * HTML string or array of such strings or JSON. 30855 * 30856 * @param {Item} referenceTab 30857 * A reference tab to insert the new item after. 30858 * 30859 * @returns {TabStrip} 30860 * Returns the TabStrip object to support chaining. 30861 * 30862 * @example 30863 * tabStrip.insertAfter( 30864 * [{ 30865 * text: "Item 1", 30866 * url: "http://www.kendoui.com" // Link URL if navigation is needed, optional. 30867 * }, 30868 * { 30869 * text: "<b>Item 2</b>", 30870 * encoded: false, // Allows use of HTML for item text 30871 * content: "text" // Content for the content element 30872 * }, 30873 * { 30874 * text: "Item 3", 30875 * contentUrl: "partialContent.html" // From where to load the item content 30876 * }, 30877 * { 30878 * text: "Item 4", 30879 * imageUrl: "http://www.kendoui.com/test.jpg" // Item image URL, optional. 30880 * }, 30881 * { 30882 * text: "Item 5", 30883 * spriteCssClass: "imageClass3" // Item image sprite CSS class, optional. 30884 * }], 30885 * referenceItem 30886 * ); 30887 * 30888 */ 30889 insertAfter: function (tab, referenceTab) { 30890 var that = this, 30891 inserted = that._create(tab), 30892 referenceContent = $(that.contentElement(referenceTab.index())); 30893 30894 each(inserted.tabs, function (idx) { 30895 referenceTab.after(this); 30896 referenceContent.after(inserted.contents[idx]); 30897 }); 30898 30899 updateFirstLast(that.tabGroup); 30900 that._updateContentElements(); 30901 30902 return that; 30903 }, 30904 30905 /** 30906 * 30907 * Removes a specified tab from a TabStrip. 30908 * 30909 * @param {Selector} element 30910 * The target tab(s), specified as a selector, to be removed. 30911 * 30912 * @returns {TabStrip}
30913 * Returns the TabStrip object to support chaining. 30914 * 30915 * @exampleTitle Remove a tab with ID, tab1 from a TabStrip 30916 * @example 30917 * tabStrip.remove("#tab1"); 30918 * 30919 */ 30920 remove: function (element) { 30921 var that = this, 30922 type = typeof element, 30923 content; 30924 30925 if (type === "string") { 30926 element = that.tabGroup.find(element); 30927 } else if (type === "number") { 30928 element = that.tabGroup.children().eq(element); 30929 } 30930 30931 content = $(that.contentElement(element.index())); 30932 30933 content.remove(); 30934 element.remove(); 30935 30936 that._updateContentElements(); 30937 30938 return that; 30939 }, 30940 30941 _create: function (tab) { 30942 var plain = $.isPlainObject(tab), 30943 that = this, tabs, contents; 30944 30945 if (plain || $.isArray(tab)) { 30946 tab = $.isArray(tab) ? tab : [tab]; 30947 30948 tabs = map(tab, function (value, idx) { 30949 return $(TabStrip.renderItem({ 30950 group: that.tabGroup, 30951 item: extend(value, { index: idx }) 30952 })); 30953 }); 30954 30955 contents = map( tab, function (value, idx) { 30956 if (value.content || value.contentUrl) { 30957 return $(TabStrip.renderContent({ 30958 item: extend(value, { index: idx }) 30959 })); 30960 } 30961 }); 30962 } else { 30963 tabs = $(tab); 30964 contents = $("<div class='" + CONTENT + "'/>"); 30965 30966 updateTabClasses(tabs); 30967 } 30968 30969 return { tabs: tabs, contents: contents }; 30970 }, 30971 30972 _toggleDisabled: function(element, enable) { 30973 element = this.tabGroup.find(element); 30974 element.each(function () { 30975 $(this) 30976 .toggleClass(DEFAULTSTATE, enable) 30977 .toggleClass(DISABLEDSTATE, !enable); 30978 }); 30979 }, 30980 30981 _updateClasses: function() { 30982 var that = this, 30983 tabs, activeItem, activeTab; 30984 30985 that.wrapper.addClass("k-widget k-header k-tabstrip"); 30986 30987 that.tabGroup = that.wrapper.children("ul").addClass("k-tabstrip-items k-reset"); 30988 30989 if (!that.tabGroup[0]) { 30990 that.tabGroup = $("<ul class='k-tabstrip-items k-reset'/>").appendTo(that.wrapper); 30991 } 30992 30993 tabs = that.tabGroup.find("li").addClass("k-item"); 30994 30995 if (tabs.length) { 30996 activeItem = tabs.filter("." + ACTIVESTATE).index(); 30997 activeTab = activeItem >= 0 ? activeItem : undefined; 30998 30999 that.tabGroup // Remove empty text nodes 31000 .contents() 31001 .filter(function () { return (this.nodeType == 3 && !trim(this.nodeValue)); }) 31002 .remove(); 31003 } 31004 31005 tabs.eq(activeItem).addClass(TABONTOP); 31006 31007 that.contentElements = that.wrapper.children("div"); 31008 31009 that.contentElements 31010 .addClass(CONTENT) 31011 .eq(activeTab) 31012 .addClass(ACTIVESTATE) 31013 .css({ display: "block" }); 31014 31015 if (tabs.length) { 31016 updateTabClasses(tabs); 31017 31018 updateFirstLast(that.tabGroup); 31019 that._updateContentElements(); 31020 } 31021 }, 31022 31023 _updateContentElements: function() { 31024 var that = this, 31025 contentUrls = that.options.contentUrls || [], 31026 tabStripID = that.element.attr("id"), 31027 contentElements = that.wrapper.children("div"); 31028 31029 that.tabGroup.find(".k-item").each(function(idx) { 31030 var currentContent = contentElements.eq(idx), 31031 id = tabStripID + "-" + (idx+1); 31032 31033 if (!currentContent.length && contentUrls[idx]) { 31034 $("<div id='"+ id +"' class='" + CONTENT + "'/>").appendTo(that.wrapper); 31035 } else { 31036 currentContent.attr("id", id); 31037 } 31038 }); 31039 31040 that.contentElements = that.contentAnimators = that.wrapper.children("div"); // refresh the contents 31041 31042 if (kendo.support.touch && kendo.mobile.ui.Scroller) { 31043 kendo.touchScroller(that.contentElements); 31044 that.contentElements = that.contentElements.children(".km-scroll-container"); 31045 } 31046 }, 31047 31048 _toggleHover: function(e) { 31049 $(e.currentTarget).toggleClass(HOVERSTATE, e.type == MOUSEENTER); 31050 }, 31051 31052 _click: function (e) { 31053 var that = this, 31054 item = $(e.currentTarget), 31055 link = item.find("." + LINK), 31056 href = link.attr(HREF), 31057 collapse = that.options.collapsible, 31058 content = $(that.contentElement(item.index())); 31059 31060 if (item.closest(".k-widget")[0] != that.wrapper[0]) { 31061 return; 31062 } 31063 31064 if (item.is("." + DISABLEDSTATE + (!collapse ? ",." + ACTIVESTATE : ""))) { 31065 e.preventDefault(); 31066 return; 31067 } 31068 31069 if (that.tabGroup.children("[data-animating], [data-in-request]").length) { 31070 return; 31071 } 31072 31073 if (that.trigger(SELECT, { item: item[0], contentElement: content[0] })) { 31074 e.preventDefault(); 31075 } else { 31076 var isAnchor = link.data(CONTENTURL) || (href && (href.charAt(href.length - 1) == "#" || href.indexOf("#" + that.element[0].id + "-") != -1)); 31077 31078 if (!href || isAnchor) { 31079 e.preventDefault(); 31080 } else { 31081 return; 31082 } 31083 31084 if (collapse && item.is("." + ACTIVESTATE)) { 31085 that.deactivateTab(item); 31086 e.preventDefault(); 31087 31088 return; 31089 } 31090 31091 if (that.activateTab(item)) { 31092 e.preventDefault(); 31093 } 31094 } 31095 }, 31096 31097 /** 31098 * 31099 * Deactivates a tab specified as a selector. Note: Invoking this method will not trigger any events. 31100 * 31101 * @param {Selector} item 31102 * The target tab, specified as a selector, to be deactivated. 31103 * 31104 * @example 31105 * var tabToDeactivate = $("#tab1"); 31106 * $("#tabStrip").data("kendoTabStrip").deactivateTab(tabToActivate); 31107 * 31108 */ 31109 deactivateTab: function (item) { 31110 var that = this, 31111 animationSettings = that.options.animation, 31112 animation = animationSettings.open, 31113 close = extend({}, animationSettings.close), 31114 hasCloseAnimation = close && "effects" in close; 31115 item = that.tabGroup.find(item); 31116 31117 close = extend( hasCloseAnimation ? close : extend({ reverse: true }, animation), { show: false, hide: true });
31118 31119 if (kendo.size(animation.effects)) { 31120 item.kendoAddClass(DEFAULTSTATE, { duration: animation.duration }); 31121 item.kendoRemoveClass(ACTIVESTATE, { duration: animation.duration }); 31122 } else { 31123 item.addClass(DEFAULTSTATE); 31124 item.removeClass(ACTIVESTATE); 31125 } 31126 31127 that.contentAnimators 31128 .filter("." + ACTIVESTATE) 31129 .kendoStop(true, true) 31130 .kendoAnimate( close ) 31131 .removeClass(ACTIVESTATE); 31132 }, 31133 31134 /** 31135 * 31136 * Activates a tab specified as a selector. Note: Invoking this method will not trigger any events. 31137 * 31138 * @param {Selector} item 31139 * The target tab, specified as a selector, to be activated. 31140 * 31141 * @returns {Boolean} 31142 * Returns <strong>true</strong> if successful; otherwise, <strong>false</strong>. 31143 * 31144 * @exampleTitle Activate a tab with ID, tab1 in a TabStrip 31145 * @example 31146 * var tabToActivate = $("#tab1"); 31147 * $("#tabStrip").data("kendoTabStrip").activateTab(tabToActivate); 31148 * 31149 */ 31150 activateTab: function (item) { 31151 item = this.tabGroup.find(item); 31152 31153 var that = this, 31154 animationSettings = that.options.animation, 31155 animation = animationSettings.open, 31156 close = extend({}, animationSettings.close), 31157 hasCloseAnimation = close && "effects" in close, 31158 neighbours = item.parent().children(), 31159 oldTab = neighbours.filter("." + ACTIVESTATE), 31160 itemIndex = neighbours.index(item); 31161 31162 close = extend( hasCloseAnimation ? close : extend({ reverse: true }, animation), { show: false, hide: true }); 31163 // deactivate previously active tab 31164 if (kendo.size(animation.effects)) { 31165 oldTab.kendoRemoveClass(ACTIVESTATE, { duration: close.duration }); 31166 item.kendoRemoveClass(HOVERSTATE, { duration: close.duration }); 31167 } else { 31168 oldTab.removeClass(ACTIVESTATE); 31169 item.removeClass(HOVERSTATE); 31170 } 31171 31172 // handle content elements 31173 var contentAnimators = that.contentAnimators; 31174 31175 if (contentAnimators.length === 0) { 31176 oldTab.removeClass(TABONTOP); 31177 item.addClass(TABONTOP) // change these directly to bring the tab on top. 31178 .css("z-index"); 31179 31180 item.addClass(ACTIVESTATE); 31181 31182 that.trigger("change"); 31183 31184 return false; 31185 } 31186 31187 var visibleContents = contentAnimators.filter("." + ACTIVESTATE); 31188 31189 // find associated content element 31190 var content = $(that.contentElement(itemIndex)); 31191 31192 if (content.length === 0) { 31193 visibleContents 31194 .removeClass( ACTIVESTATE ) 31195 .kendoStop(true, true) 31196 .kendoAnimate( close ); 31197 return false; 31198 } 31199 31200 item.attr("data-animating", true); 31201 31202 var isAjaxContent = (item.children("." + LINK).data(CONTENTURL) || false) && content.is(EMPTY), 31203 showContentElement = function () { 31204 oldTab.removeClass(TABONTOP); 31205 item.addClass(TABONTOP) // change these directly to bring the tab on top. 31206 .css("z-index"); 31207 31208 if (kendo.size(animation.effects)) { 31209 oldTab.kendoAddClass(DEFAULTSTATE, { duration: animation.duration }); 31210 item.kendoAddClass(ACTIVESTATE, { duration: animation.duration }); 31211 } else { 31212 oldTab.addClass(DEFAULTSTATE); 31213 item.addClass(ACTIVESTATE); 31214 } 31215 31216 content 31217 .closest(".k-content") 31218 .addClass(ACTIVESTATE) 31219 .kendoStop(true, true) 31220 .kendoAnimate( extend({ init: function () { 31221 that.trigger(ACTIVATE, { item: item[0], contentElement: content[0] }); 31222 } }, animation, { complete: function () { 31223 item.removeAttr("data-animating"); 31224 } } ) ); 31225 }, 31226 showContent = function() { 31227 if (!isAjaxContent) { 31228 showContentElement(); 31229 that.trigger("change"); 31230 } else { 31231 that.ajaxRequest(item, content, function () { 31232 showContentElement(); 31233 that.trigger("change"); 31234 }); 31235 } 31236 }; 31237 31238 visibleContents 31239 .removeClass(ACTIVESTATE); 31240 31241 if (visibleContents.length) { 31242 visibleContents 31243 .kendoStop(true, true) 31244 .kendoAnimate(extend( { 31245 complete: showContent 31246 }, close )); 31247 } else { 31248 showContent(); 31249 } 31250 31251 return true; 31252 }, 31253 31254 /** 31255 * 31256 * Obtains the DOM element representing a tab by its index in the <strong>TabStrip</strong>. 31257 * 31258 * @param {int} itemIndex 31259 * The index of the tab in the TabStrip. 31260 * 31261 * @returns {HTMLElement} 31262 * The DOM element representing a tab by its index in the <strong>TabStrip</strong>. 31263 * 31264 * @exampleTitle Obtain the DOM element representing the first tab in a TabStrip 31265 * @example 31266 * var tabContent = $("#tabStrip").data("kendoTabStrip").contentElement(0); 31267 * 31268 */ 31269 contentElement: function (itemIndex) { 31270 if (isNaN(itemIndex - 0)) { 31271 return; 31272 } 31273 31274 var contentElements = this.contentElements, 31275 idTest = new RegExp("-" + (itemIndex + 1) + "$"); 31276 31277 for (var i = 0, len = contentElements.length; i < len; i++) { 31278 if (idTest.test(contentElements.closest(".k-content")[i].id)) { 31279 return contentElements[i]; 31280 } 31281 } 31282 }, 31283 31284 ajaxRequest: function (element, content, complete, url) { 31285 element = this.tabGroup.find(element); 31286 if (element.find(".k-loading").length) { 31287 return; 31288 } 31289 31290 var that = this, 31291 link = element.find("." + LINK), 31292 data = {}, 31293 statusIcon = null, 31294 loadingIconTimeout = setTimeout(function () { 31295 statusIcon = $("<span class='k-icon k-loading'/>").prependTo(link); 31296 }, 100); 31297 31298 element.attr("data-in-request", true); 31299 31300 $.ajax({
31301 type: "GET", 31302 cache: false, 31303 url: url || link.data(CONTENTURL) || link.attr(HREF), 31304 dataType: "html", 31305 data: data, 31306 31307 error: function (xhr, status) { 31308 element.removeAttr("data-animating"); 31309 if (that.trigger("error", { xhr: xhr, status: status })) { 31310 this.complete(); 31311 } 31312 }, 31313 31314 complete: function () { 31315 element.removeAttr("data-in-request"); 31316 31317 clearTimeout(loadingIconTimeout); 31318 if (statusIcon !== null) { 31319 statusIcon.remove(); 31320 } 31321 }, 31322 31323 success: function (data, textStatus) { 31324 content.html(data); 31325 31326 if (complete) { 31327 complete.call(that, content); 31328 } 31329 31330 that.trigger(CONTENTLOAD, { item: element[0], contentElement: content[0] }); 31331 } 31332 }); 31333 } 31334 }); 31335 31336 // client-side rendering 31337 extend(TabStrip, { 31338 renderItem: function (options) { 31339 options = extend({ tabStrip: {}, group: {} }, options); 31340 31341 var empty = templates.empty, 31342 item = options.item; 31343 31344 return templates.item(extend(options, { 31345 image: item.imageUrl ? templates.image : empty, 31346 sprite: item.spriteCssClass ? templates.sprite : empty, 31347 itemWrapper: templates.itemWrapper 31348 }, rendering)); 31349 }, 31350 31351 renderContent: function (options) { 31352 return templates.content(extend(options, rendering)); 31353 } 31354 }); 31355 31356 kendo.ui.plugin(TabStrip); 31357 31358})(jQuery); 31359/** 31360 * @fileOverview Provides a TimePicker implementation which allows the end user to select a time value from a list of 31361 * predefined values or to type a new value. 31362 */ 31363 31364(function($, undefined) { 31365 /** 31366 * @name kendo.ui.TimePicker.Description 31367 * 31368 * @section 31369 * <p> 31370 * The <strong>TimePicker</strong> allows the end user to select a time value from a list of predefined values or 31371 * to type a new value. It supports configurable options for the format, minimum and maximum time, and the 31372 * interval between predefined values in the list. 31373 * </p> 31374 * <h3>Getting Started</h3> 31375 * 31376 * @exampleTitle Creating a TimePicker from existing input element 31377 * @example 31378 * <input id="timePicker" /> 31379 * 31380 * @exampleTitle Initialize the PanelBar via an ID selector 31381 * @example 31382 * $(document).ready(function(){ 31383 * $("#timePicker").kendoTimePicker(); 31384 * }); 31385 * 31386 * @section 31387 * <p> 31388 * When a <strong>TimePicker</strong> is initialized, it will automatically be displayed near the location of the 31389 * used HTML element. 31390 * </p> 31391 * <h3>Configuring TimePicker Behaviors</h3> 31392 * <p> 31393 * A <strong>TimePicker</strong> provides configuration options that can be easily set during initialization. 31394 * Among the properties that can be controlled: 31395 * </p> 31396 * <ul> 31397 * <li>Selected time</li> 31398 * <li>Minimum/Maximum time</li> 31399 * <li>Define format</li> 31400 * <li>Define interval between predefined values in the list</li> 31401 * </ul> 31402 * 31403 * @exampleTitle Create TimePicker with selected time and defined min and max time 31404 * @example 31405 * $("#timePicker").kendoTimePicker({ 31406 * value: new Date(2000, 10, 10, 10, 0, 0), 31407 * min: new Date(1950, 0, 1, 8, 0, 0), 31408 * max: new Date(2049, 11, 31, 18, 0, 0) 31409 * }); 31410 * 31411 * @section 31412 * <p> 31413 * A <strong>TimePicker</strong> will set the value only if the entered time is valid and if it is in the defined 31414 * range. 31415 * </p> 31416 * 31417 * @exampleTitle Define time format 31418 * @example 31419 * $("#timePicker").kendoTimePicker({ 31420 * format: "hh:mm:ss tt" 31421 * }); 31422 * 31423 * @exampleTitle Define the interval (in minutes) between values in the list 31424 * @example 31425 * $("#timePicker").kendoTimePicker({ 31426 * interval: 15 31427 * }); 31428 * 31429 * @section 31430 * <h3>Accessing an Existing TimePicker</h3> 31431 * <p> 31432 * You can reference an existing <strong>TimePicker</strong> instance via 31433 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Once a reference has been established, you can 31434 * use the API to control its behavior. 31435 * </p> 31436 * 31437 * @exampleTitle Accessing an existing TimePicker instance 31438 * @example 31439 * var timePicker = $("#timePicker").data("kendoTimePicker"); 31440 * 31441 */ 31442 var kendo = window.kendo, 31443 touch = kendo.support.touch, 31444 keys = kendo.keys, 31445 ui = kendo.ui, 31446 Widget = ui.Widget, 31447 OPEN = "open", 31448 CLOSE = "close", 31449 CHANGE = "change", 31450 CLICK = (touch ? "touchend" : "click"), 31451 DEFAULT = "k-state-default", 31452 DISABLED = "disabled", 31453 LI = "li", 31454 SPAN = "<span/>", 31455 FOCUSED = "k-state-focused", 31456 HOVER = "k-state-hover", 31457 HOVEREVENTS = "mouseenter mouseleave", 31458 MOUSEDOWN = "mousedown", 31459 MS_PER_MINUTE = 60000, 31460 MS_PER_DAY = 86400000, 31461 SELECTED = "k-state-selected", 31462 STATEDISABLED = "k-state-disabled", 31463 extend = $.extend, 31464 proxy = $.proxy, 31465 DATE = Date, 31466 TODAY = new DATE(); 31467 31468 TODAY = new DATE(TODAY.getFullYear(), TODAY.getMonth(), TODAY.getDate(), 0, 0, 0); 31469 31470 var TimeView = function(options) { 31471 var that = this; 31472 31473 that.options = options; 31474 31475 that.ul = $('<ul unselectable="on" class="k-list k-reset"/>') 31476 .css({ overflow: kendo.support.touch ? "": "auto" }) 31477 .delegate(LI, CLICK, proxy(that._click, that)) 31478 .delegate(LI, "mouseenter", function() { $(this).addClass(HOVER); }) 31479 .delegate(LI, "mouseleave", function() { $(this).removeClass(HOVER); }); 31480 31481 that.list = $("<div class='k-list-container'/>") 31482 .append(that.ul) 31483 .mousedown(function(e) { 31484 e.preventDefault(); 31485 }); 31486 31487 that._popup(); 31488 31489 that.template = kendo.template('<li class="k-item" unselectable="on">#=data#</li>', { useWithBlock: false }); 31490 }; 31491 31492 TimeView.prototype = { 31493 current: function(candidate) { 31494 var that = this; 31495 31496 if (candidate !== undefined) { 31497 if (that._current) { 31498 that._current.removeClass(SELECTED); 31499 } 31500 31501 if (candidate) { 31502 candidate = $(candidate); 31503 candidate.addClass(SELECTED); 31504 that.scroll(candidate[0]); 31505 } 31506 31507 that._current = candidate; 31508 } else { 31509 return that._current; 31510 } 31511 }, 31512 31513 close: function() { 31514 this.popup.close(); 31515 }, 31516 31517 open: function() { 31518 var that = this; 31519 31520 if (!that.ul[0].firstChild) { 31521 that.refresh();
31522 } 31523 31524 that.popup.open(); 31525 if (that._current) { 31526 that.scroll(that._current[0]); 31527 } 31528 }, 31529 31530 refresh: function() { 31531 var that = this, 31532 options = that.options, 31533 format = options.format, 31534 offset = dst(), 31535 ignoreDST = offset < 0, 31536 min = options.min, 31537 max = options.max, 31538 msMin = getMilliseconds(min), 31539 msMax = getMilliseconds(max), 31540 msInterval = options.interval * MS_PER_MINUTE, 31541 toString = kendo.toString, 31542 template = that.template, 31543 start = new DATE(min), 31544 idx = 0, length, 31545 html = ""; 31546 31547 if (ignoreDST) { 31548 length = (MS_PER_DAY + (offset * MS_PER_MINUTE)) / msInterval; 31549 } else { 31550 length = MS_PER_DAY / msInterval; 31551 } 31552 31553 31554 if (msMin != msMax) { 31555 if (msMin > msMax) { 31556 msMax += MS_PER_DAY; 31557 } 31558 length = (msMax - msMin) / msInterval + 1; 31559 } 31560 31561 for (; idx < length; idx++) { 31562 if (idx) { 31563 setTime(start, msInterval, ignoreDST); 31564 } 31565 31566 if (msMax && getMilliseconds(start) > msMax) { 31567 start = new DATE(max); 31568 } 31569 31570 html += template(toString(start, format)); 31571 } 31572 31573 that.ul[0].innerHTML = html; 31574 31575 that._height(length); 31576 31577 that.select(that._value); 31578 }, 31579 31580 scroll: function(item) { 31581 if (!item) { 31582 return; 31583 } 31584 31585 var ul = this.ul[0], 31586 itemOffsetTop = item.offsetTop, 31587 itemOffsetHeight = item.offsetHeight, 31588 ulScrollTop = ul.scrollTop, 31589 ulOffsetHeight = ul.clientHeight, 31590 bottomDistance = itemOffsetTop + itemOffsetHeight; 31591 31592 ul.scrollTop = ulScrollTop > itemOffsetTop ? 31593 itemOffsetTop : bottomDistance > (ulScrollTop + ulOffsetHeight) ? 31594 bottomDistance - ulOffsetHeight : ulScrollTop; 31595 }, 31596 31597 select: function(li) { 31598 var that = this, 31599 current = that._current; 31600 31601 if (typeof li === "string") { 31602 if (!current || current.text() !== li) { 31603 li = $.grep(that.ul[0].childNodes, function(node) { 31604 return (node.textContent || node.innerText) == li; 31605 }); 31606 31607 li = li[0] ? li : null; 31608 } else { 31609 li = current; 31610 } 31611 } 31612 31613 that.current(li); 31614 }, 31615 31616 toggle: function() { 31617 var that = this; 31618 31619 if (that.popup.visible()) { 31620 that.close(); 31621 } else { 31622 that.open(); 31623 } 31624 }, 31625 31626 value: function(value) { 31627 var that = this; 31628 31629 that._value = value; 31630 if (that.ul[0].firstChild) { 31631 that.select(value); 31632 } 31633 }, 31634 31635 _click: function(e) { 31636 var that = this, 31637 li = $(e.currentTarget); 31638 31639 if (!e.isDefaultPrevented()) { 31640 that.select(li); 31641 that.options.change(li.text(), true); 31642 that.close(); 31643 } 31644 }, 31645 31646 _height: function(length) { 31647 if (length) { 31648 var that = this, 31649 list = that.list, 31650 parent = list.parent(".k-animation-container"), 31651 height = that.options.height; 31652 31653 list.add(parent) 31654 .show() 31655 .height(that.ul[0].scrollHeight > height ? height : "auto") 31656 .hide(); 31657 } 31658 }, 31659 31660 _popup: function() { 31661 var that = this, 31662 list = that.list, 31663 options = that.options, 31664 anchor = options.anchor, 31665 width; 31666 31667 that.popup = new ui.Popup(list, extend(options.popup, { 31668 anchor: anchor, 31669 open: options.open, 31670 close: options.close, 31671 animation: options.animation 31672 })); 31673 31674 width = anchor.outerWidth() - (list.outerWidth() - list.width()); 31675 31676 list.css({ 31677 fontFamily: anchor.css("font-family"), 31678 width: width 31679 }); 31680 31681 kendo.touchScroller(that.popup.element); 31682 }, 31683 31684 move: function(e) { 31685 var that = this, 31686 key = e.keyCode,
31687 ul = that.ul[0], 31688 current = that._current, 31689 down = key === keys.DOWN; 31690 31691 if (key === keys.UP || down) { 31692 if (e.altKey) { 31693 that.toggle(down); 31694 return; 31695 } else if (down) { 31696 current = current ? current[0].nextSibling : ul.firstChild; 31697 } else { 31698 current = current ? current[0].previousSibling : ul.lastChild; 31699 } 31700 31701 if (current) { 31702 that.select(current); 31703 } 31704 31705 that.options.change(that._current.text()); 31706 e.preventDefault(); 31707 31708 } else if (key === keys.ENTER || key === keys.TAB || key === keys.ESC) { 31709 e.preventDefault(); 31710 that.close(); 31711 } 31712 } 31713 }; 31714 31715 function setTime(date, time, ignoreDST) { 31716 var offset = date.getTimezoneOffset(), 31717 offsetDiff; 31718 31719 date.setTime(date.getTime() + time); 31720 31721 if (!ignoreDST) { 31722 offsetDiff = date.getTimezoneOffset() - offset; 31723 date.setTime(date.getTime() + offsetDiff * MS_PER_MINUTE); 31724 } 31725 } 31726 31727 function dst() { 31728 var today = new DATE(), 31729 midnight = new DATE(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0), 31730 noon = new DATE(today.getFullYear(), today.getMonth(), today.getDate(), 12, 0, 0); 31731 31732 return -1 * (midnight.getTimezoneOffset() - noon.getTimezoneOffset()); 31733 } 31734 31735 function getMilliseconds(date) { 31736 return date.getHours() * 60 * MS_PER_MINUTE + date.getMinutes() * MS_PER_MINUTE + date.getSeconds() * 1000 + date.getMilliseconds(); 31737 } 31738 31739 function isInRange(value, min, max) { 31740 var msMin = getMilliseconds(min), 31741 msMax = getMilliseconds(max), 31742 msValue; 31743 31744 if (!value || msMin == msMax) { 31745 return true; 31746 } 31747 31748 msValue = getMilliseconds(value); 31749 31750 if (msMin > msValue) { 31751 msValue += MS_PER_DAY; 31752 } 31753 31754 if (msMax < msMin) { 31755 msMax += MS_PER_DAY; 31756 } 31757 31758 return msValue >= msMin && msValue <= msMax; 31759 } 31760 31761 kendo.TimeView = TimeView; 31762 31763 var TimePicker = Widget.extend(/** @lends kendo.ui.TimePicker.prototype */{ 31764 /** 31765 * 31766 * Creates a TimePicker instance. 31767 * 31768 * @constructs 31769 * @extends kendo.ui.Widget 31770 * 31771 * @param {DomElement} element DOM element 31772 * @param {Object} options Configuration options. 31773 * 31774 * @option {Date} [value] <null> 31775 * Specifies the selected time. 31776 * 31777 * @option {Date} [min] <00:00> 31778 * Specifies the start value in the popup list. 31779 * 31780 * @option {Date} [max] <00:00> 31781 * Specifies the end value in the popup list. 31782 * 31783 * @option {String} [format] <h:mm tt> 31784 * Specifies the format, which is used to parse value set with value() method. 31785 * 31786 * @option {Number} [interval] <30> 31787 * Specifies the interval, between values in the popup list, in minutes. 31788 * 31789 * @option {Object} [animation] <> 31790 * Animations to be used for opening/closing the popup. Setting to false will turn of the animation. 31791 * 31792 * @option {Object} [animation.open] <> 31793 * Animation to be used for opening of the popup. 31794 * 31795 * _exampleTitle Intialize a TimePicker that fades-in the time drop-down list over 300 milliseconds 31796 * _example 31797 * $("#timePicker").kendoTimePicker({ 31798 * animation: { 31799 * open: { 31800 * effects: "fadeIn", 31801 * duration: 300, 31802 * show: true 31803 * } 31804 * } 31805 * }); 31806 * 31807 * @option {Object} [animation.close] <> 31808 * Animation to be used for closing of the popup. 31809 * 31810 * _exampleTitle Initialize a TimePicker that fades-out the time drop-down list over 300 milliseconds 31811 * _example 31812 * $("#timepicker").kendoTimePicker({ 31813 * animation: { 31814 * close: { 31815 * effects: "fadeOut", 31816 * duration: 300, 31817 * hide: true 31818 * show: false 31819 * } 31820 * } 31821 * }); 31822 * 31823 */ 31824 init: function(element, options) { 31825 var that = this; 31826 31827 Widget.fn.init.call(that, element, options); 31828 31829 element = that.element; 31830 options = that.options; 31831 31832 options.format = options.format || kendo.culture().calendar.patterns.t; 31833 31834 that._wrapper(); 31835 31836 that.timeView = new TimeView(extend({}, options, { 31837 anchor: that.wrapper, 31838 format: options.format, 31839 change: function(value, trigger) { 31840 if (trigger) { 31841 that._change(value); 31842 } else { 31843 element.val(value); 31844 } 31845 }, 31846 open: function(e) { 31847 if (that.trigger(OPEN)) { 31848 e.preventDefault(); 31849 } 31850 }, 31851 close: function(e) { 31852 if (that.trigger(CLOSE)) { 31853 e.preventDefault(); 31854 } 31855 } 31856 })); 31857 31858 that._icon(); 31859 31860 element.addClass("k-input") 31861 .bind({ 31862 keydown: proxy(that._keydown, that), 31863 focus: function(e) { 31864 that._inputWrapper.addClass(FOCUSED); 31865 }, 31866 blur: proxy(that._blur, that) 31867 }) 31868 .closest("form") 31869 .bind("reset", function() { 31870 that.value(element[0].defaultValue); 31871 }); 31872 31873 that.enable(!element.is('[disabled]')); 31874 that.value(options.value || element.val()); 31875 31876 kendo.notify(that); 31877 }, 31878 31879 options: { 31880 name: "TimePicker", 31881 min: TODAY, 31882 max: TODAY, 31883 format: "", 31884 value: null, 31885 interval: 30, 31886 height: 200, 31887 animation: {} 31888 }, 31889 31890 events: [ 31891 /** 31892 * 31893 * Triggered when the underlying value of a TimePicker is changed. 31894 * 31895 * @name kendo.ui.TimePicker#change 31896 * @event 31897 * 31898 * @param {Event} e 31899 * 31900 * @exampleTitle Attach change event handler during initialization; detach via unbind() 31901 * @example 31902 * // event change for expand 31903 * var onChange = function(e) { 31904 * // ... 31905 * }; 31906 * 31907 * // attach change event handler during initialization 31908 * var timePicker = $("#timePicker").kendoTimePicker({ 31909 * change: onChange 31910 * }); 31911 * 31912 * // detach change event handler via unbind() 31913 * timePicker.data("kendoTimePicker").unbind("change", onChange); 31914 * 31915 * @exampleTitle Attach change event handler via bind(); detach via unbind() 31916 * @example 31917 * // event change for expand 31918 * var onChange = function(e) { 31919 * // ... 31920 * }; 31921 * 31922 * // attach change event handler via bind() 31923 * $("#timePicker").data("kendoTimePicker").bind("change", onChange); 31924 * 31925 * // detach change event handler via unbind() 31926 * $("#timePicker").data("kendoTimePicker").unbind("change", onChange); 31927 * 31928 */ 31929 OPEN, 31930 CLOSE, 31931 CHANGE 31932 ], 31933 31934 setOptions: function(options) { 31935 Widget.fn.setOptions.call(this, options); 31936 31937 extend(this.timeView.options, options); 31938 31939 this.timeView.refresh();
31940 }, 31941 31942 /** 31943 * 31944 * Enables or disables a TimePicker. 31945 * 31946 * @param {Boolean} enable 31947 * Enables (<strong>true</strong> or undefined) or disables (<strong>false</strong>) a TimePicker. 31948 * 31949 * @exampleTitle Enable a TimePicker 31950 * @example 31951 * $("timepicker").data("kendoTimePicker").enable(); 31952 * 31953 * @exampleTitle Enable a TimePicker 31954 * @example 31955 * $("timepicker").data("kendoTimePicker").enable(true); 31956 * 31957 * @exampleTitle Disable a TimePicker 31958 * @example 31959 * $("timepicker").data("kendoTimePicker").enable(false); 31960 * 31961 */ 31962 enable: function(enable) { 31963 var that = this, 31964 element = that.element, 31965 arrow = that._arrow.unbind(CLICK + " " + MOUSEDOWN), 31966 wrapper = that._inputWrapper.unbind(HOVEREVENTS); 31967 31968 if (enable === false) { 31969 wrapper 31970 .removeClass(DEFAULT) 31971 .addClass(STATEDISABLED); 31972 31973 element.attr(DISABLED, DISABLED); 31974 } else { 31975 wrapper 31976 .removeClass(STATEDISABLED) 31977 .addClass(DEFAULT) 31978 .bind(HOVEREVENTS, that._toggleHover); 31979 31980 element 31981 .removeAttr(DISABLED); 31982 31983 arrow.bind(CLICK, proxy(that._click, that)) 31984 .bind(MOUSEDOWN, function(e) { e.preventDefault(); }); 31985 } 31986 }, 31987 31988 /** 31989 * 31990 * Closes the drop-down list of a TimePicker. 31991 * 31992 * @exampleTitle Close the time drop-down list of a TimePicker. 31993 * @example 31994 * $("timepicker").data("kendoTimePicker").close(); 31995 * 31996 */ 31997 close: function() { 31998 this.timeView.close(); 31999 }, 32000 32001 /** 32002 * 32003 * Opens the drop-down list of a TimePicker. 32004 * 32005 * @exampleTitle Open the time drop-down list of a TimePicker. 32006 * @example 32007 * $("timepicker").data("kendoTimePicker").open(); 32008 * 32009 */ 32010 open: function() { 32011 this.timeView.open(); 32012 }, 32013 32014 /** 32015 * 32016 * Gets or sets the minimum value of the TimePicker. 32017 * 32018 * @param {Date|String} value 32019 * The minimum time value to set for a TimePicker, expressed as a Date object or as a string. 32020 * 32021 * @returns {Date} 32022 * The minimum time value of a TimePicker. 32023 * 32024 * @exampleTitle Get the minimum value of a TimePicker 32025 * @example 32026 * var timePicker = $("#timePicker").data("kendoTimePicker"); 32027 * var minimum = timePicker.min(); 32028 * 32029 * @exampleTitle Set the minimum value of a TimePicker 32030 * @example 32031 * var timePicker = $("#timePicker").data("kendoTimePicker"); 32032 * timePicker.min(new Date(1900, 0, 1, 10, 0, 0)); 32033 * 32034 */ 32035 min: function (value) { 32036 return this._option("min", value); 32037 }, 32038 32039 /** 32040 * 32041 * Gets or sets the maximum value of the TimePicker. 32042 * 32043 * @param {Date|String} value 32044 * The maximum time value to set for a TimePicker, expressed as a Date object or as a string. 32045 * 32046 * @returns {Date} 32047 * The maximum time value of a TimePicker. 32048 * 32049 * @exampleTitle Get the maximum value of a TimePicker 32050 * @example 32051 * var timePicker = $("#timePicker").data("kendoTimePicker"); 32052 * var maximum = timePicker.max(); 32053 * 32054 * @exampleTitle Set the maximum value of a TimePicker 32055 * @example 32056 * var timePicker = $("#timePicker").data("kendoTimePicker"); 32057 * timePicker.max(new Date(1900, 0, 1, 10, 0, 0)); 32058 * 32059 */ 32060 max: function (value) { 32061 return this._option("max", value); 32062 }, 32063 32064 /** 32065 * 32066 * Gets or sets the value of the TimePicker. 32067 * 32068 * @param {Date|String} value 32069 * The time value to set for a TimePicker, expressed as a Date object or as a string. 32070 * 32071 * @returns {Date} 32072 * The time value of a TimePicker. 32073 * 32074 * @exampleTitle Get the value of a TimePicker 32075 * @example 32076 * var timePicker = $("#timePicker").data("kendoTimePicker"); 32077 * var timePickerValue = timePicker.value(); 32078 * 32079 * @exampleTitle Set the value of a TimePicker 32080 * @example 32081 * var timePicker = $("#timePicker").data("kendoTimePicker"); 32082 * timePicker.value("10:00 AM"); 32083 * 32084 */ 32085 value: function(value) { 32086 var that = this; 32087 32088 if (value === undefined) { 32089 return that._value; 32090 } 32091 32092 that._old = that._update(value); 32093 }, 32094 32095 _blur: function() { 32096 var that = this; 32097 32098 that.close(); 32099 that._change(that.element.val()); 32100 that._inputWrapper.removeClass(FOCUSED); 32101 }, 32102 32103 _click: function() { 32104 var that = this, 32105 element = that.element; 32106 32107 if (!touch && element[0] !== document.activeElement) { 32108 element.focus(); 32109 } 32110 32111 that.timeView.toggle(); 32112 }, 32113 32114 _change: function(value) { 32115 var that = this; 32116 32117 value = that._update(value); 32118 32119 if (+that._old != +value) { 32120 that._old = value; 32121 that.trigger(CHANGE); 32122 32123 // trigger the DOM change event so any subscriber gets notified 32124 that.element.trigger(CHANGE); 32125 } 32126 }, 32127 32128 _icon: function() { 32129 var that = this, 32130 element = that.element, 32131 arrow; 32132 32133 arrow = element.next("span.k-select"); 32134 32135 if (!arrow[0]) { 32136 arrow = $('<span unselectable="on" class="k-select"><span unselectable="on" class="k-icon k-icon-clock">select</span></span>').insertAfter(element); 32137 } 32138 32139 that._arrow = arrow; 32140 }, 32141 32142 _keydown: function(e) { 32143 var that = this, 32144 key = e.keyCode,
32145 enter = key == keys.ENTER, 32146 timeView = that.timeView; 32147 32148 if (timeView.popup.visible() || e.altKey || enter) { 32149 timeView.move(e); 32150 } 32151 32152 if (enter) { 32153 that._change(that.element.val()); 32154 } 32155 }, 32156 32157 _option: function(option, value) { 32158 var that = this, 32159 options = that.options; 32160 32161 if (value === undefined) { 32162 return options[option]; 32163 } 32164 32165 value = that._parse(value); 32166 32167 if (!value) { 32168 return; 32169 } 32170 32171 value = new DATE(value); 32172 32173 options[option] = value; 32174 that.timeView.options[option] = value; 32175 that.timeView.refresh(); 32176 }, 32177 32178 _parse: function(value) { 32179 var that = this, 32180 current = that._value || TODAY; 32181 32182 if (value instanceof DATE) { 32183 return value; 32184 } 32185 32186 value = kendo.parseDate(value, that.options.format); 32187 32188 if (value) { 32189 value = new DATE(current.getFullYear(), 32190 current.getMonth(), 32191 current.getDate(), 32192 value.getHours(), 32193 value.getMinutes(), 32194 value.getSeconds(), 32195 value.getMilliseconds()); 32196 } 32197 32198 return value; 32199 }, 32200 32201 _toggleHover: function(e) { 32202 if (!touch) { 32203 $(e.currentTarget).toggleClass(HOVER, e.type === "mouseenter"); 32204 } 32205 }, 32206 32207 _update: function(value) { 32208 var that = this, 32209 options = that.options, 32210 date = that._parse(value), 32211 text = kendo.toString(date, options.format); 32212 32213 if (!isInRange(date, options.min, options.max)) { 32214 date = null; 32215 } 32216 32217 that._value = date; 32218 that.element.val(date ? text : value); 32219 that.timeView.value(text); 32220 32221 return date; 32222 }, 32223 32224 _wrapper: function() { 32225 var that = this, 32226 element = that.element, 32227 wrapper; 32228 32229 wrapper = element.parents(".k-timepicker"); 32230 32231 if (!wrapper[0]) { 32232 wrapper = element.wrap(SPAN).parent().addClass("k-picker-wrap k-state-default"); 32233 wrapper = wrapper.wrap(SPAN).parent(); 32234 } 32235 32236 wrapper[0].style.cssText = element[0].style.cssText; 32237 element.css({ 32238 width: "100%", 32239 height: element[0].style.height 32240 }); 32241 32242 that.wrapper = wrapper.addClass("k-widget k-timepicker k-header"); 32243 that._inputWrapper = $(wrapper[0].firstChild); 32244 } 32245 }); 32246 32247 ui.plugin(TimePicker); 32248 32249})(jQuery); 32250/** 32251 * @fileOverview Provides a TreeView implementation which can be used to display hierarchical data in a traditional 32252 * tree structure. 32253 */ 32254 32255(function($, undefined){ 32256 /** 32257 * @name kendo.ui.TreeView.Description 32258 * 32259 * @section 32260 * <p> 32261 * The <strong>TreeView</strong> displays hierarchical data in a traditional tree structure. It supports user 32262 * interaction through the mouse or touch to perform re-ordering operations via drag-and-drop. 32263 * </p> 32264 * <p> 32265 * A <strong>TreeView</strong> can be created by leveraging HTML lists. However, it does not support binding to a 32266 * remote data source at this point in time. 32267 * </p> 32268 * <h3>Getting Started</h3> 32269 * <p>A <strong>TreeView</strong> can be created in two ways:</p> 32270 * <ol> 32271 * <li>Define a hierarchical list with static HTML</li> 32272 * <li>Use dynamic data binding</li> 32273 * </ol> 32274 * <p> 32275 * Static HTML definition is appropriate for small hierarchies and for data that does not change frequently. 32276 * Databinding should be used for larger data sets and for data that changes frequently. 32277 * </p> 32278 * <h3>Creating a TreeView from HTML</h3> 32279 * 32280 * @exampleTitle Create a hierarchical list in HTML 32281 * @example 32282 * <ul id="treeView"> 32283 * <li>Item 1 32284 * <ul> 32285 * <li>Item 1.1</li> 32286 * <li>Item 1.2</li> 32287 * </ul> 32288 * </li> 32289 * <li>Item 2</li> 32290 * </ul> 32291 * 32292 * @section 32293 * <p> 32294 * Initialization of a <strong>TreeView</strong> should occur after the DOM is fully loaded. It is recommended 32295 * that initialization the <strong>TreeView</strong> occur within a handler is provided to $(document).ready(). 32296 * </p> 32297 * 32298 * @exampleTitle Initialize a TreeView using a selector within $(document).ready() 32299 * @example 32300 * $(document).ready(function() { 32301 * $("#treeView").kendoTreeView(); 32302 * }); 32303 * 32304 * @section 32305 * <h3>Creating a TreeView with Data Binding to a Local Data Source</h3> 32306 * 32307 * @exampleTitle Create a hierarchical HTML list 32308 * @example 32309 * <div id="treeView"></div> 32310 * 32311 * @exampleTitle Initialize and bind the TreeView 32312 * @example 32313 * $(document).ready(function() { 32314 * $("#treeView").kendoTreeView({
32315 * dataSource: [ 32316 * { 32317 * text: "Item 1", 32318 * items: [ 32319 * { text: "Item 1.1" }, 32320 * { text: "Item 1.2" } 32321 * ] 32322 * }, 32323 * { text: "Item 2" } 32324 * ] 32325 * }) 32326 * }); 32327 * 32328 * @section 32329 * <p>Currently, the <strong>TreeView</strong> does not support binding to a remote data source.</p> 32330 * @exampleTitle TreeView item JSON structure 32331 * @example 32332 * var item = { 32333 * text: "Item text", 32334 * 32335 * // renders a <img class="k-image" src="/images/icon.png" /> 32336 * imageUrl: "/images/icon.png", 32337 * 32338 * // renders a <span class="k-sprite icon save" /> 32339 * spriteCssClass: "icon save", 32340 * 32341 * // specifies whether the node text should be encoded or not 32342 * // useful when rendering node-specific HTML 32343 * encoded: false, 32344 * 32345 * items: [ 32346 * // child items 32347 * ] 32348 * } 32349 * 32350 * @section 32351 * <h3>Configuring TreeView Behavior</h3> 32352 * <p> 32353 * A number of <strong>TreeView</strong> behaviors can be easily controlled by simple configuration properties, 32354 * such as animation behaviors and drag-and-drop behaviors. 32355 * </p> 32356 * 32357 * @exampleTitle Enabling drag-and-drop for TreeView nodes 32358 * @example 32359 * $("#treeView").kendoTreeView({ 32360 * dragAndDrop: true 32361 * }); 32362 * 32363 * @section 32364 * <p> 32365 * When drag-and-drop is enabled, the nodes of a <strong>TreeView</strong> can be dragged and dropped between all 32366 * levels, with useful tooltips helping indicate where the node will be dropped. 32367 * </p> 32368 * <h3>Accessing an Existing TreeView</h3> 32369 * <p> 32370 * You can reference an existing <strong>TreeView</strong> instance via 32371 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Once a reference has been established, you can 32372 * use the API to control its behavior. 32373 * </p> 32374 * 32375 * @exampleTitle Accessing an existing TreeView instance 32376 * @example 32377 * var treeView = $("#treeView").data("kendoTreeView"); 32378 * 32379 */ 32380 var kendo = window.kendo, 32381 ui = kendo.ui, 32382 extend = $.extend, 32383 template = kendo.template, 32384 Widget = ui.Widget, 32385 proxy = $.proxy, 32386 SELECT = "select", 32387 EXPAND = "expand", 32388 COLLAPSE = "collapse", 32389 DRAGSTART = "dragstart", 32390 DRAG = "drag", 32391 DROP = "drop", 32392 DRAGEND = "dragend", 32393 CLICK = "click", 32394 VISIBILITY = "visibility", 32395 KSTATEHOVER = "k-state-hover", 32396 KTREEVIEW = "k-treeview", 32397 VISIBLE = ":visible", 32398 NODE = ".k-item", 32399 templates, rendering, TreeView, 32400 subGroup, nodeContents; 32401 32402 function contentChild(filter) { 32403 return function(node) { 32404 var result = node.children(".k-animation-container"); 32405 32406 if (!result.length) { 32407 result = node; 32408 } 32409 32410 return result.children(filter); 32411 }; 32412 } 32413 32414 subGroup = contentChild(".k-group"); 32415 nodeContents = contentChild(".k-group,.k-content"); 32416 32417 function updateNodeHtml(node) { 32418 var wrapper = node.children("div"), 32419 group = node.children("ul"), 32420 toggleButton = wrapper.children(".k-icon"), 32421 innerWrapper = wrapper.children(".k-in"), 32422 currentNode, tmp; 32423 32424 if (node.hasClass("k-treeview")) { 32425 return; 32426 } 32427 32428 if (!wrapper.length) { 32429 wrapper = $("<div />").prependTo(node); 32430 } 32431 32432 if (!toggleButton.length && group.length) { 32433 toggleButton = $("<span class='k-icon' />").prependTo(wrapper); 32434 } else if (!group.length || !group.children().length) { 32435 toggleButton.remove(); 32436 group.remove(); 32437 } 32438 32439 if (!innerWrapper.length) { 32440 innerWrapper = $("<span class='k-in' />").appendTo(wrapper)[0]; 32441 32442 // move all non-group content in the k-in container 32443 currentNode = wrapper[0].nextSibling; 32444 innerWrapper = wrapper.find(".k-in")[0]; 32445 32446 while (currentNode && currentNode.nodeName.toLowerCase() != "ul") { 32447 tmp = currentNode; 32448 currentNode = currentNode.nextSibling; 32449 32450 if (tmp.nodeType == 3) { 32451 tmp.nodeValue = $.trim(tmp.nodeValue); 32452 } 32453 32454 innerWrapper.appendChild(tmp); 32455 } 32456 } 32457 } 32458 32459 function updateNodeClasses(node, groupData, nodeData) { 32460 var wrapper = node.children("div"), 32461 group = node.children("ul"); 32462 32463 if (node.hasClass("k-treeview")) { 32464 return; 32465 } 32466 32467 if (!nodeData) { 32468 nodeData = { 32469 expanded: group.css("display") != "none", 32470 index: node.index(), 32471 enabled: !wrapper.children(".k-in").hasClass("k-state-disabled") 32472 }; 32473 } 32474 32475 if (!groupData) { 32476 groupData = { 32477 firstLevel: node.parent().parent().hasClass(KTREEVIEW), 32478 length: node.parent().children().length 32479 }; 32480 } 32481 32482 // li 32483 node.removeClass("k-first k-last") 32484 .addClass(rendering.wrapperCssClass(groupData, nodeData)); 32485 32486 // div 32487 wrapper.removeClass("k-top k-mid k-bot") 32488 .addClass(rendering.cssClass(groupData, nodeData)); 32489 32490 // toggle button 32491 if (group.length) { 32492 wrapper.children(".k-icon").removeClass("k-plus k-minus k-plus-disabled k-minus-disabled") 32493 .addClass(rendering.toggleButtonClass(nodeData)); 32494 32495 group.addClass("k-group"); 32496 } 32497 } 32498 32499 32500 templates = { 32501 dragClue: template("<div class='k-header k-drag-clue'><span class='k-icon k-drag-status'></span>#= text #</div>"), 32502 group: template( 32503 "<ul class='#= groupCssClass(group) #'#= groupAttributes(group) #>" + 32504 "#= renderItems(data) #" + 32505 "</ul>" 32506 ), 32507 itemWrapper: template( 32508 "<div class='#= cssClass(group, item) #'>" + 32509 "#= toggleButton(data) #" + 32510 "#= checkbox(data) #" + 32511 "<#= tag(item) # class='#= textClass(item) #'#= textAttributes(item) #>" + 32512 "#= image(item) ##= sprite(item) #" + 32513 "#= treeview.template ? template(treeview, item) : text(item) #" + 32514 "</#= tag(item) #>" + 32515 "</div>" 32516 ), 32517 item: template( 32518 "<li class='#= wrapperCssClass(group, item) #'>" + 32519 "#= itemWrapper(data) #" + 32520 "# if (item.items) { #" + 32521 "#= subGroup(treeview, group, item) #" + 32522 "# } #" + 32523 "</li>" 32524 ), 32525 checkbox: template( 32526 "# if (treeview.checkboxTemplate) { #" + 32527 "<span class='k-checkbox'>" + 32528 "#= checkboxTemplate(treeview, group, item) #" + 32529 "</span>" + 32530 "# } #" 32531 ), 32532 image: template("<img class='k-image' alt='' src='#= imageUrl #' />"), 32533 toggleButton: template("<span class='#= toggleButtonClass(item) #'></span>"), 32534 sprite: template("<span class='k-sprite #= spriteCssClass #'></span>"), 32535 empty: template("") 32536 }; 32537 32538 TreeView = Widget.extend(/** @lends kendo.ui.TreeView.prototype */ { 32539 /** 32540 * 32541 * Creates a TreeView instance. 32542 * 32543 * @constructs 32544 * @extends kendo.ui.Widget 32545 * 32546 * @param {DomElement} element DOM element 32547 * @param {Object} options Configuration options. 32548 * 32549 * @option {Array} [dataSource] 32550 * The data that the <strong>TreeView</strong> will be bound to. 32551 * 32552 * @option {Object} [animation] 32553 * A collection of visual animations used when items are expanded or collapsed through user interaction. 32554 * Setting this option to <strong>false</strong> will disable all animations. 32555 * 32556 * _example 32557 * $("#treeView").kendoTreeView({
32558 * animation: { 32559 * expand: { 32560 * duration: 200, 32561 * hide: true, 32562 * show: false 32563 * }, 32564 * collapse: { 32565 * duration: 200, 32566 * effects: "expandVertical", 32567 * show: true 32568 * } 32569 * } 32570 * }); 32571 * 32572 * @option {Animation} [animation.expand] 32573 * The animation that will be used when expanding items. 32574 * 32575 * @option {Number} [animation.expand.duration] <200> The number of milliseconds used for the animation when a 32576 * node is expanded. 32577 * 32578 * _example 32579 * $("#treeView").kendoTreeView({ 32580 * animation: { 32581 * expand: { 32582 * duration: 1000 32583 * } 32584 * } 32585 * }); 32586 * 32587 * @option {String} [animation.expand.effects] <"expandVertical"> 32588 * A whitespace-delimited string of animation effects that are utilized when a <strong>TreeView</strong> node 32589 * is expanded. Options include <strong>"expandVertical"</strong> and <strong>"fadeIn"</strong>. 32590 * 32591 * _exampleTitle Initialize a TreeView to expand and fade-in nodes over 5000 milliseconds 32592 * _example 32593 * $("#treeView").kendoTreeView({ 32594 * animation: { 32595 * expand: { 32596 * duration: 5000, 32597 * effects: "expandVertical fadeIn" 32598 * } 32599 * } 32600 * }); 32601 * 32602 * @option {Boolean} [animation.expand.show] <true> 32603 * 32604 * @option {Boolean} [dragAndDrop] <false> 32605 * Disables (<strong>false</strong>) or enables (<b>true</b>) drag-and-drop on the nodes of a 32606 * <strong>TreeView</strong>. 32607 * 32608 * @option {Animation} [animation.collapse] 32609 * The animation that will be used when collapsing items. 32610 * 32611 * @option {Number} [animation.collapse.duration] <200> 32612 * The number of milliseconds used for the animation when a node is expanded. 32613 * 32614 * _exampleTitle Initialize a TreeView to collapse nodes over 1000 milliseconds 32615 * _example 32616 * $("#treeView").kendoTreeView({ 32617 * animation: { 32618 * collapse: { 32619 * duration: 1000 32620 * } 32621 * } 32622 * }); 32623 * 32624 * @option {String} [animation.collapse.effects] 32625 * A whitespace-delimited string of animation effects that are utilized when a <strong>TreeView</strong> node 32626 * is collapsed. Options include <strong>"fadeOut"</strong>. 32627 * 32628 * _exampleTitle Initialize a TreeView to collapse and fade-out nodes over 5000 milliseconds 32629 * _example 32630 * $("#treeView").kendoTreeView({ 32631 * animation: { 32632 * collapse: { 32633 * duration: 5000, 32634 * effects: "fadeOut" 32635 * } 32636 * } 32637 * }); 32638 * 32639 * @option {String|Function} [template] Template for rendering of the nodes of the treeview. 32640 * _example 32641 * $("#treeview").kendoTreeView({ 32642 * template: "#= item.text # <a href='\\#'>Delete</a>" 32643 * }); 32644 * 32645 * @option {String|Function} [checkboxTemplate] Template for rendering of the treeview checkboxes. 32646 * _example 32647 * $("#treeview").kendoTreeView({ 32648 * template: kendo.template( 32649 * "<input type='checkbox' name='checkedFiles[" + 32650 * item.id + 32651 * "]' value='true' />" 32652 * ) 32653 * }); 32654 */ 32655 init: function (element, options) { 32656 var that = this, 32657 clickableItems = ".k-in:not(.k-state-selected,.k-state-disabled)", 32658 MOUSEENTER = "mouseenter", 32659 dataInit; 32660 32661 if ($.isArray(options)) { 32662 dataInit = true; 32663 options = { dataSource: options }; 32664 } 32665 32666 Widget.prototype.init.call(that, element, options); 32667 32668 element = that.element; 32669 options = that.options; 32670 32671 that._animation(); 32672 32673 if (options.template && typeof options.template == "string") { 32674 options.template = template(options.template); 32675 } 32676 32677 // render treeview if it's not already rendered 32678 if (!element.hasClass(KTREEVIEW)) { 32679 that._wrapper(); 32680 32681 if (!that.root.length) { // treeview initialized from empty element 32682 that.root = that.wrapper.html(TreeView.renderGroup({ 32683 items: options.dataSource, 32684 group: { 32685 firstLevel: true, 32686 expanded: true 32687 }, 32688 treeview: options 32689 })).children("ul"); 32690 } else { 32691 that._group(that.wrapper); 32692 } 32693 } else { 32694 // otherwise just initialize properties 32695 that.wrapper = element; 32696 that.root = element.children("ul").eq(0); 32697 } 32698 32699 that.wrapper 32700 .on(MOUSEENTER, ".k-in.k-state-selected", function(e) { e.preventDefault(); }) 32701 .on(MOUSEENTER, clickableItems, function () { $(this).addClass(KSTATEHOVER); }) 32702 .on("mouseleave", clickableItems, function () { $(this).removeClass(KSTATEHOVER); }) 32703 .on(CLICK, clickableItems, proxy(that._nodeClick, that)) 32704 .on("dblclick", "div:not(.k-state-disabled) .k-in", proxy(that._toggleButtonClick, that)) 32705 .on(CLICK, ".k-plus,.k-minus", proxy(that._toggleButtonClick, that)); 32706 32707 if (options.dragAndDrop) { 32708 that.dragging = new TreeViewDragAndDrop(that); 32709 } 32710 }, 32711 32712 _animation: function() { 32713 var options = this.options; 32714 32715 if (options.animation === false) { 32716 options.animation = { 32717 expand: { show: true, effects: {} }, 32718 collapse: { hide: true, effects: {} } 32719 }; 32720 } 32721 }, 32722 32723 events: [ 32724 /** 32725 * 32726 * Triggered before the dragging of a node starts. 32727 * 32728 * @name kendo.ui.TreeView#dragstart 32729 * @event 32730 * 32731 * @param {Event} e 32732 * 32733 * @param {Node} e.sourceNode 32734 * The node that will be dragged. 32735 * 32736 * @exampleTitle Disable dragging of root nodes 32737 * @example 32738 * treeview.data("kendoTreeView").bind("dragstart", function(e) { 32739 * if ($(e.sourceNode).parentsUntil(".k-treeview", ".k-item").length == 0) { 32740 * e.preventDefault(); 32741 * } 32742 * }); 32743 * 32744 */ 32745 DRAGSTART, 32746 32747 /** 32748 * 32749 * Triggered while a node is being dragged. 32750 * 32751 * @name kendo.ui.TreeView#drag 32752 * @event 32753 * 32754 * @param {Event} e 32755 * 32756 * @param {Node} e.sourceNode 32757 * The node that is being dragged. 32758 * 32759 * @param {DomElement} e.dropTarget 32760 * The element that the node is placed over. 32761 * 32762 * @param {Integer} e.pageX 32763 * The x coordinate of the mouse. 32764 * 32765 * @param {Integer} e.pageY 32766 * The y coordinate of the mouse. 32767 * 32768 * @param {String} e.statusClass 32769 * The status that the drag clue shows. 32770 * 32771 * @param {Function} e.setStatusClass 32772 * Allows a custom drag clue status to be set. 32773 * <p>Pre-defined status classes are:</p> 32774 * <ul> 32775 * <li><strong>k-insert-top</strong> 32776 * - Indicates that the item will be inserted on top. 32777 * </li> 32778 * <li><strong>k-insert-middle</strong> 32779 * - Indicates that the item will be inserted in the middle. 32780 * </li> 32781 * <li><strong>k-insert-bottom</strong> 32782 * - Indicates that the item will be inserted at the bottom. 32783 * </li> 32784 * <li><strong>k-add</strong> 32785 * - Indicates that the item will be added/appended. 32786 * </li> 32787 * <li><strong>k-denied</strong>
32788 * - Indicates an invalid operation. Using this class will automatically 32789 * make the drop operation invalid, so there will be no need to call 32790 * <code>setValid(false)</code> in the <code>drop</code> event. 32791 * </li> 32792 * </ul> 32793 * 32794 * @exampleTitle Show the user that is not permitted to drop nodes outside of the #drop-area element 32795 * @example 32796 * treeview.data("kendoTreeView").bind("drag", function(e) { 32797 * if ($(e.dropTarget).parents("#drop-area").length ) { 32798 * e.setStatusClass("k-denied"); 32799 * } 32800 * }); 32801 * 32802 */ 32803 DRAG, 32804 32805 /** 32806 * 32807 * Triggered when a node is being dropped. 32808 * 32809 * @name kendo.ui.TreeView#drop 32810 * @event 32811 * 32812 * @param {Event} e 32813 * 32814 * @param {Node} e.sourceNode 32815 * The node that is being dropped. 32816 * 32817 * @param {Node} e.destinationNode 32818 * The node that the sourceNode is being dropped upon. 32819 * 32820 * @param {Boolean} e.valid 32821 * Whether this drop operation is permitted. 32822 * 32823 * @param {Function} e.setValid 32824 * Allows the drop to be prevented. 32825 * 32826 * @param {DomElement} e.dropTarget 32827 * The element that the node is placed over. 32828 * 32829 * @param {String} e.dropPosition 32830 * Shows where the source will be dropped. One of the values <strong>over</strong>, <strong>before</strong>, or <strong>after</strong>. 32831 * 32832 */ 32833 DROP, 32834 32835 /** 32836 * 32837 * Triggered after a node has been dropped. 32838 * 32839 * @name kendo.ui.TreeView#dragend 32840 * @event 32841 * 32842 * @param {Event} e 32843 * 32844 * @param {Node} e.sourceNode 32845 * The node that is being dropped. 32846 * 32847 * @param {Node} e.destinationNode 32848 * The node that the sourceNode is being dropped upon. 32849 * 32850 * @param {String} e.dropPosition 32851 * Shows where the source has been dropped. One of the values <strong>over</strong>, <strong>before</strong>, or <strong>after</strong>. 32852 * 32853 */ 32854 DRAGEND, 32855 /** 32856 * 32857 * Triggered before a subgroup gets expanded. 32858 * 32859 * @name kendo.ui.TreeView#expand 32860 * @event 32861 * 32862 * @param {Event} e 32863 * 32864 * @param {Node} e.node 32865 * The expanded node 32866 * 32867 */ 32868 EXPAND, 32869 32870 /** 32871 * 32872 * Triggered before a subgroup gets collapsed. 32873 * 32874 * @name kendo.ui.TreeView#collapse 32875 * @event 32876 * 32877 * @param {Event} e 32878 * 32879 * @param {Node} e.node 32880 * The collapsed node 32881 * 32882 */ 32883 COLLAPSE, 32884 32885 /** 32886 * 32887 * Triggered when a node gets selected. 32888 * 32889 * @name kendo.ui.TreeView#select 32890 * @event 32891 * 32892 * @param {Event} e 32893 * 32894 * @param {Node} e.node 32895 * The selected node 32896 * 32897 */ 32898 SELECT 32899 ], 32900 32901 options: { 32902 name: "TreeView", 32903 dataSource: {}, 32904 animation: { 32905 expand: { 32906 effects: "expand:vertical", 32907 duration: 200, 32908 show: true 32909 }, 32910 collapse: { 32911 duration: 100 32912 } 32913 }, 32914 dragAndDrop: false 32915 }, 32916 32917 setOptions: function(options) { 32918 var that = this; 32919
32920 if (("dragAndDrop" in options) && options.dragAndDrop && !that.options.dragAndDrop) { 32921 that.dragging = new TreeViewDragAndDrop(that); 32922 } 32923 32924 Widget.fn.setOptions.call(that, options); 32925 32926 that._animation(); 32927 }, 32928 32929 _trigger: function (eventName, node) { 32930 return this.trigger(eventName, { 32931 node: node.closest(NODE)[0] 32932 }); 32933 }, 32934 32935 _toggleButtonClick: function (e) { 32936 this.toggle($(e.target).closest(NODE)); 32937 }, 32938 32939 _nodeClick: function (e) { 32940 var that = this, 32941 node = $(e.target), 32942 contents = nodeContents(node.closest(NODE)), 32943 href = node.attr("href"), 32944 shouldNavigate; 32945 32946 if (href) { 32947 shouldNavigate = href == "#" || href.indexOf("#" + this.element.id + "-") >= 0; 32948 } else { 32949 shouldNavigate = contents.length && !contents.children().length; 32950 } 32951 32952 if (shouldNavigate) { 32953 e.preventDefault(); 32954 } 32955 32956 if (!node.hasClass(".k-state-selected") && !that._trigger("select", node)) { 32957 that.select(node); 32958 } 32959 }, 32960 32961 _wrapper: function() { 32962 var that = this, 32963 element = that.element, 32964 wrapper, root, 32965 wrapperClasses = "k-widget k-treeview k-reset"; 32966 32967 if (element.is("div")) { 32968 wrapper = element; 32969 root = wrapper.children("ul").eq(0); 32970 } else { // element is ul 32971 wrapper = element.wrap('<div />').parent(); 32972 root = element; 32973 } 32974 32975 that.wrapper = wrapper.addClass(wrapperClasses); 32976 that.root = root; 32977 }, 32978 32979 _group: function(item) { 32980 var that = this, 32981 firstLevel = item.hasClass(KTREEVIEW), 32982 group = { 32983 firstLevel: firstLevel, 32984 expanded: firstLevel || item.attr(kendo.attr("expanded")) === "true" 32985 }, 32986 groupElement = item.children("ul"); 32987 32988 groupElement 32989 .addClass(rendering.groupCssClass(group)) 32990 .css("display", group.expanded ? "" : "none"); 32991 32992 that._nodes(groupElement, group); 32993 }, 32994 32995 _nodes: function(groupElement, groupData) { 32996 var that = this, 32997 nodes = groupElement.children("li"), 32998 nodeData; 32999 33000 groupData = extend({ length: nodes.length }, groupData); 33001 33002 nodes.each(function(i, node) { 33003 node = $(node); 33004 33005 nodeData = { index: i, expanded: node.attr(kendo.attr("expanded")) === "true" }; 33006 33007 updateNodeHtml(node); 33008 33009 updateNodeClasses(node, groupData, nodeData); 33010 33011 // iterate over child nodes 33012 that._group(node); 33013 }); 33014 }, 33015 33016 _processNodes: function(nodes, callback) { 33017 var that = this; 33018 that.element.find(nodes).each(function(index, item) { 33019 callback.call(that, index, $(item).closest(NODE)); 33020 }); 33021 }, 33022 33023 /** 33024 * 33025 * Expands nodes. 33026 * 33027 * @param {Selector} nodes 33028 * The nodes that are to be expanded. 33029 * 33030 * @example 33031 * var treeview = $("#treeview").data("kendoTreeView"); 33032 *
33033 * // expands the node with id="firstItem" 33034 * treeview.expand(document.getElementById("firstItem")); 33035 * 33036 * // expands all nodes 33037 * treeview.expand(".k-item"); 33038 * 33039 */ 33040 expand: function (nodes) { 33041 this._processNodes(nodes, function (index, item) { 33042 var contents = nodeContents(item); 33043 33044 if (contents.length > 0 && !contents.is(VISIBLE)) { 33045 this.toggle(item); 33046 } 33047 }); 33048 }, 33049 33050 /** 33051 * 33052 * Collapses nodes. 33053 * 33054 * @param {Selector} nodes 33055 * The nodes that are to be collapsed. 33056 * 33057 * @example 33058 * var treeview = $("#treeview").data("kendoTreeView"); 33059 * 33060 * // collapse the node with id="firstItem" 33061 * treeview.collapse(document.getElementById("firstItem")); 33062 * 33063 * // collapse all nodes 33064 * treeview.collapse(".k-item"); 33065 * 33066 */ 33067 collapse: function (nodes) { 33068 this._processNodes(nodes, function (index, item) { 33069 var contents = nodeContents(item); 33070 33071 if (contents.length > 0 && contents.is(VISIBLE)) { 33072 this.toggle(item); 33073 } 33074 }); 33075 }, 33076 33077 /** 33078 * 33079 * Enables or disables nodes. 33080 * 33081 * @param {Selector} nodes 33082 * The nodes that are to be enabled/disabled. 33083 * 33084 * @param {Boolean} [enable=true] 33085 * Whether the nodes should be enabled or disabled. 33086 * 33087 * @example 33088 * var treeview = $("#treeview").data("kendoTreeView"); 33089 *
33090 * // disable the node with id="firstItem" 33091 * treeview.enable(document.getElementById("firstItem"), false); 33092 * 33093 * // enable all nodes 33094 * treeview.enable(".k-item"); 33095 * 33096 */ 33097 enable: function (nodes, enable) { 33098 enable = arguments.length == 2 ? !!enable : true; 33099 33100 this._processNodes(nodes, function (index, item) { 33101 var isCollapsed = !nodeContents(item).is(VISIBLE); 33102 33103 if (!enable) { 33104 this.collapse(item); 33105 isCollapsed = true; 33106 } 33107 33108 item.children("div") 33109 .children(".k-in") 33110 .toggleClass("k-state-default", enable) 33111 .toggleClass("k-state-disabled", !enable) 33112 .end() 33113 .children(".k-icon") 33114 .toggleClass("k-plus", isCollapsed && enable) 33115 .toggleClass("k-plus-disabled", isCollapsed && !enable) 33116 .toggleClass("k-minus", !isCollapsed && enable) 33117 .toggleClass("k-minus-disabled", !isCollapsed && !enable); 33118 }); 33119 }, 33120 33121 /** 33122 * 33123 * Gets or sets the selected node of a TreeView. 33124 * 33125 * @param {Selector} [node] 33126 * If provided, the node of a TreeView that should be selected. 33127 * 33128 * @returns {Node} 33129 * The selected node of a TreeView. 33130 * 33131 * @exampleTitle Select the node with ID, firstItem 33132 * @example 33133 * var treeView = $("#treeView").data("kendoTreeView"); 33134 * treeView.select($("#firstItem")); 33135 * 33136 * @exampleTitle Get the currently selected node 33137 * @example 33138 * var treeView = $("#treeView").data("kendoTreeView"); 33139 * var selectedNode = treeView.select(); 33140 * 33141 */ 33142 select: function (node) { 33143 var element = this.element; 33144 33145 if (!arguments.length) { 33146 return element.find(".k-state-selected").closest(NODE); 33147 } 33148 33149 node = $(node, element).closest(NODE); 33150 33151 if (node.length) { 33152 element.find(".k-in").removeClass("k-state-hover k-state-selected"); 33153 33154 node.find(".k-in:first").addClass("k-state-selected"); 33155 } 33156 }, 33157 33158 /** 33159 * 33160 * Toggles the node of a TreeView between its expanded and collapsed
33160states. 33161 * 33162 * @param {Selector} node 33163 * The node that should be toggled. 33164 * 33165 * @exampleTitle Toggle the state of a node with ID, firstItem 33166 * @example 33167 * var treeView = $("#treeView").data("kendoTreeView"); 33168 * treeView.toggle($("#firstItem")); 33169 * 33170 */ 33171 toggle: function (node) { 33172 node = $(node); 33173 33174 if (!node.find(".k-minus,.k-plus").length) { 33175 return; 33176 } 33177 33178 if (node.find("> div > .k-state-disabled").length) { 33179 return; 33180 } 33181 33182 var that = this, 33183 contents = nodeContents(node), 33184 isExpanding = !contents.is(VISIBLE), 33185 animationSettings = that.options.animation || {}, 33186 animation = animationSettings.expand, 33187 collapse = extend({}, animationSettings.collapse), 33188 hasCollapseAnimation = collapse && "effects" in collapse; 33189 33190 if (contents.data("animating")) { 33191 return; 33192 } 33193 33194 if (!isExpanding) { 33195 animation = extend( hasCollapseAnimation ? collapse 33196 : extend({ reverse: true }, animation), { show: false, hide: true }); 33197 } 33198 33199 if (contents.children().length > 0) { 33200 if (!that._trigger(isExpanding ? "expand" : "collapse", node)) { 33201 node.find("> div > .k-icon") 33202 .toggleClass("k-minus", isExpanding) 33203 .toggleClass("k-plus", !isExpanding); 33204 33205 if (!isExpanding) { 33206 contents.css("height", contents.height()).css("height"); 33207 } 33208 33209 contents.kendoStop(true, true).kendoAnimate(extend(animation, { 33210 complete: function() { 33211 if (isExpanding) { 33212 contents.css("height", ""); 33213 } 33214 } 33215 })); 33216 } 33217 } 33218 }, 33219 33220 /** 33221 * 33222 * Gets the text of a node in a TreeView. 33223 * 33224 * @param {Selector} node 33225 * The node of which the text is being retrieved. 33226 * 33227 * @returns {String} 33228 * The text of a node. 33229 * 33230 * @exampleTitle Get the text of the node with ID, firstItem 33231 * @example 33232 * var treeView = $("#treeView").data("kendoTreeView"); 33233 * var nodeText = treeView.text($("#firstItem")); 33234 * 33235 */ 33236 text: function (node) { 33237 return $(node).closest(NODE).find(">div>.k-in").text(); 33238 }, 33239 33240 _insertNode: function(nodeData, index, parentNode, group, insertCallback) { 33241 var that = this, 33242 updatedGroupLength = group.children().length + 1, 33243 isArrayData, fromNodeData, 33244 groupData = { 33245 firstLevel: parentNode.hasClass(KTREEVIEW), 33246 expanded: true, 33247 length: updatedGroupLength 33248 }, node, i, nodeHtml = ""; 33249 33250 function toNodeHtml(nodeData, index) { 33251 return TreeView.renderItem({ 33252 treeview: that.options, 33253 group: groupData, 33254 item: extend(nodeData, { index: index }) 33255 }); 33256 } 33257 33258 if (nodeData.toJSON) { 33259 nodeData = nodeData.toJSON(); 33260 } 33261 33262 isArrayData = $.isArray(nodeData); 33263 fromNodeData = isArrayData || $.isPlainObject(nodeData); 33264 33265 if (fromNodeData) { 33266 if (isArrayData) { 33267 for (i = 0; i < nodeData.length; i++) { 33268 nodeHtml += toNodeHtml(nodeData[i], index + i); 33269 } 33270 33271 } else { 33272 nodeHtml = toNodeHtml(nodeData, index); 33273 } 33274 33275 node = $(nodeHtml); 33276 } else { 33277 node = $(nodeData); 33278 33279 if (group.children()[index - 1] == node[0]) { 33280 return node; 33281 } 33282 33283 if (node.closest(".k-treeview")[0] == that.wrapper[0]) { 33284 that.detach(node); 33285 } 33286 } 33287 33288 if (!group.length) { 33289 group = $(TreeView.renderGroup({ 33290 group: groupData 33291 })).appendTo(parentNode); 33292 } 33293 33294 insertCallback(node, group); 33295 33296 if (parentNode.hasClass("k-item")) { 33297 updateNodeHtml(parentNode); 33298 updateNodeClasses(parentNode); 33299 } 33300 33301 if (!fromNodeData) { 33302 updateNodeClasses(node); 33303 } 33304 33305 updateNodeClasses(node.prev()); 33306 updateNodeClasses(node.next()); 33307 33308 return node; 33309 }, 33310 33311 /** 33312 * 33313 * Inserts a node after a specified node in a TreeView. This method may also be used to reorder the nodes of a 33314 * TreeView. 33315 * 33316 * @param {String|Selector} nodeData 33317 * A JSON-formatted string or selector that specifies the node to be inserted. 33318 * 33319 * @param {Node} referenceNode 33320 * The node that will be preceed the newly-appended node. 33321 * 33322 * @exampleTitle Insert a node with the text, "Y U NO insert node?" after the node with ID, firstItem 33323 * @example 33324 * var treeView = $("#treeView").data("kendoTreeView"); 33325 * treeView.insertAfter({ text: "Y U NO insert node?" }, $("#firstItem")); 33326 *
33327 * @exampleTitle Moves a node with ID, secondNode after a node with ID, firstItem 33328 * @example 33329 * var treeView = $("#treeView").data("kendoTreeView"); 33330 * treeView.insertAfter($("#secondNode"), $("#firstItem")); 33331 * 33332 */ 33333 insertAfter: function (nodeData, referenceNode) { 33334 var group = referenceNode.parent(); 33335 33336 return this._insertNode(nodeData, referenceNode.index() + 1, group.parent(), group, function(item, group) { 33337 item.insertAfter(referenceNode); 33338 }); 33339 }, 33340 33341 /** 33342 * 33343 * Inserts a node before another node. This method may also be used to reorder the nodes of a 33344 * TreeView. 33345 * 33346 * @param {String|Selector} nodeData 33347 * A JSON-formatted string or selector that specifies the node to be inserted. 33348 * 33349 * @param {Node} referenceNode 33350 * The node that follows the inserted node. 33351 * 33352 * @exampleTitle Inserts a new node with the text, "It's over 9000!" before the node with ID, firstItem 33353 * @example 33354 * var treeView = $("#treeView").data("kendoTreeView"); 33355 * treeView.insertBefore({ text: "It's over 9000!" }, $("#firstItem")); 33356 * 33357 * @exampleTitle Moves the node with ID, secondNode before the node with ID, firstItem 33358 * @example 33359 * var treeView = $("#treeView").data("kendoTreeView"); 33360 * treeView.insertBefore($("#secondNode"), $("#firstItem")); 33361 * 33362 */ 33363 insertBefore: function (nodeData, referenceNode) { 33364 var group = referenceNode.parent(); 33365 33366 return this._insertNode(nodeData, referenceNode.index(), group.parent(), group, function(item, group) { 33367 item.insertBefore(referenceNode); 33368 }); 33369 }, 33370 33371 /** 33372 * 33373 * Appends a node to a group of a TreeView. This method may also be used to reorder the nodes of a 33374 * TreeView. 33375 * 33376 * @param {String|Selector} nodeData 33377 * A JSON-formatted string or selector that specifies the node to be appended. 33378 * 33379 * @param {Node} [parentNode] 33380 * The node that will contain the newly appended node. If not specified, the new node will be appended to the 33381 * root group of the TreeView. 33382 * 33383 * @exampleTitle Append a new node with the text, "Meanwhile, in HTML5..." to the node with ID, firstItem 33384 * @example 33385 * var treeView = $("#treeView").data("kendoTreeView"); 33386 * treeView.append({ text: "Meanwhile, in HTML5..." }, $("#firstItem")); 33387 * 33388 * @exampleTitle Moves the node with ID, secondNode as a last child of the node with ID, firstItem 33389 * @example 33390 * var treeView = $("#treeView").data("kendoTreeView"); 33391 * treeView.append($("#secondNode"), $("#firstItem")); 33392 * 33393 */ 33394 append: function (nodeData, parentNode) { 33395 parentNode = parentNode || this.element; 33396 33397 var group = subGroup(parentNode); 33398 33399 return this._insertNode(nodeData, group.children().length, parentNode, group, function(item, group) { 33400 item.appendTo(group); 33401 }); 33402 }, 33403 33404 _remove: function (node, keepData) { 33405 var parentNode, 33406 prevSibling, nextSibling; 33407 33408 node = $(node, this.element); 33409 33410 parentNode = node.parent().parent(); 33411 prevSibling = node.prev(); 33412 nextSibling = node.next(); 33413 33414 node[keepData ? "detach" : "remove"](); 33415 33416 if (parentNode.hasClass("k-item")) { 33417 updateNodeHtml(parentNode); 33418 updateNodeClasses(parentNode); 33419 } 33420 33421 updateNodeClasses(prevSibling); 33422 updateNodeClasses(nextSibling); 33423 33424 return node; 33425 }, 33426 33427 /** 33428 * 33429 * Removes a node from a TreeView. 33430 * 33431 * @param {Selector} node 33432 * The node that is to be removed. 33433 * 33434 * @exampleTitle Remove the node with ID, firstItem 33435 * @example 33436 * var treeView = $("#treeView").data("kendoTreeView"); 33437 * treeView.remove($("#firstItem")); 33438 * 33439 */ 33440 remove: function (node) { 33441 return this._remove(node, false); 33442 }, 33443 33444 /** 33445 *
33446 * Removes a node from a TreeView, but keeps its jQuery.data() objects. 33447 * 33448 * @param {Selector} node 33449 * The node that is to be detached. 33450 * 33451 * @returns {jQueryObject} 33452 * The node that has been detached. 33453 * 33454 * @exampleTitle Remove the node with ID, firstItem 33455 * @example 33456 * var treeView = $("#treeView").data("kendoTreeView"); 33457 * var firstItem = $("#firstItem"); 33458 * firstItem.data("id", 1); 33459 * treeview.detach(firstItem); 33460 * firstItem.data("id") == 1; 33461 * 33462 */ 33463 detach: function (node) { 33464 return this._remove(node, true); 33465 }, 33466 33467 /** 33468 * 33469 * Searches a TreeView for a node that has specific text. 33470 * 33471 * @param {String} text 33472 * The text that is being searched for. 33473 * 33474 * @returns {jQueryObject} 33475 * All nodes that have the text. 33476 * 33477 * @exampleTitle Search a TreeView for the item that has the text, "CSS3 is da bomb!" 33478 * @example 33479 * var treeView = $("#treeView").data("kendoTreeView"); 33480 * var foundNode = treeView.findByText("CSS3 is da bomb!"); 33481 * 33482 */ 33483 findByText: function(text) { 33484 return $(this.element).find(".k-in").filter(function(i, element) { 33485 return $(element).text() == text; 33486 }).closest(NODE); 33487 } 33488 }); 33489 33490 function TreeViewDragAndDrop(treeview) { 33491 var that = this; 33492 33493 that.treeview = treeview; 33494 33495 that._draggable = new ui.Draggable(treeview.element, { 33496 filter: "div:not(.k-state-disabled) .k-in", 33497 hint: function(node) { 33498 return templates.dragClue({ text: node.text() }); 33499 }, 33500 cursorOffset: { 33501 left: 10, 33502 top: kendo.support.touch ? -40 / kendo.support.zoomLevel() : 10 33503 }, 33504 dragstart: proxy(that.dragstart, that), 33505 dragcancel: proxy(that.dragcancel, that), 33506 drag: proxy(that.drag, that), 33507 dragend: proxy(that.dragend, that) 33508 }); 33509 } 33510 33511 TreeViewDragAndDrop.prototype = /** @ignore */{ 33512 _hintStatus: function(newStatus) { 33513 var statusElement = this._draggable.hint.find(".k-drag-status")[0]; 33514 33515 if (newStatus) { 33516 statusElement.className = "k-icon k-drag-status " + newStatus; 33517 } else { 33518 return $.trim(statusElement.className.replace(/k-(icon|drag-status)/g, "")); 33519 } 33520 }, 33521 33522 dragstart: function (e) { 33523 var that = this, 33524 treeview = that.treeview, 33525 sourceNode = that.sourceNode = e.currentTarget.closest(NODE); 33526 33527 if (treeview.trigger(DRAGSTART, { sourceNode: sourceNode[0] })) { 33528 e.preventDefault(); 33529 } 33530 33531 that.dropHint = $("<div class='k-drop-hint' />") 33532 .css(VISIBILITY, "hidden") 33533 .appendTo(treeview.element); 33534 }, 33535 33536 drag: function (e) { 33537 var that = this, 33538 treeview = that.treeview, 33539 sourceNode = that.sourceNode, 33540 dropTarget = that.dropTarget = $(kendo.eventTarget(e)), 33541 statusClass, 33542 hoveredItem, hoveredItemPos, itemHeight, itemTop, itemContent, delta, 33543 insertOnTop, insertOnBottom, addChild; 33544 33545 if (!dropTarget.closest(".k-treeview").length) { 33546 // dragging node outside of treeview 33547 statusClass = "k-denied"; 33548 } else if ($.contains(sourceNode[0], dropTarget[0])) { 33549 // dragging node within itself 33550 statusClass = "k-denied"; 33551 } else { 33552 // moving or reordering node 33553 statusClass = "k-insert-middle"; 33554 33555 that.dropHint.css(VISIBILITY, "visible"); 33556 33557 hoveredItem = dropTarget.closest(".k-top,.k-mid,.k-bot"); 33558 33559 if (hoveredItem.length > 0) { 33560 itemHeight = hoveredItem.outerHeight(); 33561 itemTop = hoveredItem.offset().top; 33562 itemContent = dropTarget.closest(".k-in"); 33563 delta = itemHeight / (itemContent.length > 0 ? 4 : 2); 33564 33565 insertOnTop = e.pageY < (itemTop + delta); 33566 insertOnBottom = (itemTop + itemHeight - delta) < e.pageY; 33567 addChild = itemContent.length > 0 && !insertOnTop && !insertOnBottom; 33568 33569 itemContent.toggleClass(KSTATEHOVER, addChild); 33570 that.dropHint.css(VISIBILITY, addChild ? "hidden" : "visible"); 33571 33572 if (addChild) { 33573 statusClass = "k-add"; 33574 } else { 33575 hoveredItemPos = hoveredItem.position(); 33576 hoveredItemPos.top += insertOnTop ? 0 : itemHeight; 33577 33578 that.dropHint 33579 .css(hoveredItemPos) 33580 [insertOnTop ? "prependTo" : "appendTo"](dropTarget.closest(NODE).children("div:first")); 33581 33582 if (insertOnTop && hoveredItem.hasClass("k-top")) { 33583 statusClass = "k-insert-top"; 33584 } 33585 33586 if (insertOnBottom && hoveredItem.hasClass("k-bot")) { 33587 statusClass = "k-insert-bottom"; 33588 } 33589 } 33590 } else if (dropTarget[0] != that.dropHint[0]) { 33591 statusClass = "k-denied"; 33592 } 33593 } 33594 33595 treeview.trigger(DRAG, { 33596 sourceNode: sourceNode[0], 33597 dropTarget: dropTarget[0], 33598 pageY: e.pageY, 33599 pageX: e.pageX, 33600 statusClass: statusClass.substring(2), 33601 setStatusClass: function (value) { 33602 statusClass = value; 33603 } 33604 }); 33605 33606 if (statusClass.indexOf("k-insert") !== 0) { 33607 that.dropHint.css(VISIBILITY, "hidden"); 33608 } 33609 33610 that._hintStatus(statusClass); 33611 }, 33612 33613 dragcancel: function(e) { 33614 this.dropHint.remove(); 33615 //treeview.trigger("nodeDragCancelled", { item: sourceNode[0] }); 33616 }, 33617 33618 dragend: function (e) { 33619 var that = this, 33620 treeview = that.treeview, 33621 dropPosition = "over", 33622 sourceNode = that.sourceNode, 33623 destinationNode, 33624 dropHint = that.dropHint, 33625 valid, dropPrevented; 33626 33627 if (dropHint.css(VISIBILITY) == "visible") { 33628 dropPosition = dropHint.prevAll(".k-in").length > 0 ? "after" : "before"; 33629 destinationNode = dropHint.closest(NODE); 33630 } else if (that.dropTarget) { 33631 destinationNode = that.dropTarget.closest(NODE); 33632 } 33633 33634 valid = that._hintStatus() != "k-denied"; 33635 33636 dropPrevented = treeview.trigger(DROP, { 33637 sourceNode: sourceNode[0], 33638 destinationNode: destinationNode[0], 33639 valid: valid, 33640 setValid: function(newValid) { valid = newValid; }, 33641 dropTarget: e.target, 33642 dropPosition: dropPosition 33643 }); 33644 33645 dropHint.remove(); 33646 33647 if (!valid || dropPrevented) { 33648 that._draggable.dropped = valid; 33649 return; 33650 } 33651 33652 that._draggable.dropped = true; 33653 33654 // perform reorder / move 33655 if (dropPosition == "over") { 33656 treeview.append(sourceNode, destinationNode); 33657 treeview.expand(destinationNode); 33658 } else if (dropPosition == "before") { 33659 treeview.insertBefore(sourceNode, destinationNode); 33660 } else if (dropPosition == "after") { 33661 treeview.insertAfter(sourceNode, destinationNode); 33662 } 33663 33664 treeview.trigger(DRAGEND, { 33665 sourceNode: sourceNode[0], 33666 destinationNode: destinationNode[0], 33667 dropPosition: dropPosition 33668 }); 33669 } 33670 }; 33671 33672 // client-side rendering 33673 33674 extend(TreeView, { 33675 renderItem: function (options) { 33676 options = extend({ treeview: {}, group: {} }, options); 33677 33678 var empty = templates.empty, 33679 item = options.item, 33680 treeview = options.treeview; 33681 33682 return templates.item(extend(options, { 33683 image: item.imageUrl ? templates.image : empty, 33684 sprite: item.spriteCssClass ? templates.sprite : empty, 33685 itemWrapper: templates.itemWrapper, 33686 toggleButton: item.items ? templates.toggleButton : empty, 33687 checkbox: treeview.checkboxTemplate ? templates.checkbox : empty, 33688 subGroup: function(treeview, group, item) { 33689 return TreeView.renderGroup({ 33690 items: item.items, 33691 treeview: treeview, 33692 group: { 33693 expanded: item.expanded 33694 } 33695 }); 33696 } 33697 }, rendering)); 33698 }, 33699 33700 renderGroup: function (options) { 33701 return templates.group(extend({ 33702 renderItems: function(options) { 33703 var html = "", 33704 i = 0, 33705 items = options.items, 33706 len = items ? items.length : 0, 33707 group = extend({ length: len }, options.group); 33708 33709 for (; i < len; i++) { 33710 html += TreeView.renderItem(extend(options, { 33711 group: group, 33712 item: extend({ index: i }, items[i]) 33713 })); 33714 } 33715 33716 return html; 33717 } 33718 }, options, rendering)); 33719 } 33720 });
33721 33722 rendering = /** @ignore */{ 33723 wrapperCssClass: function (group, item) { 33724 var result = "k-item", 33725 index = item.index; 33726 33727 if (group.firstLevel && index === 0) { 33728 result += " k-first"; 33729 } 33730 33731 if (index == group.length-1) { 33732 result += " k-last"; 33733 } 33734 33735 return result; 33736 }, 33737 cssClass: function(group, item) { 33738 var result = "", 33739 index = item.index, 33740 groupLength = group.length - 1; 33741 33742 if (group.firstLevel && index === 0) { 33743 result += "k-top "; 33744 } 33745 33746 if (index === 0 && index != groupLength) { 33747 result += "k-top"; 33748 } else if (index == groupLength) { 33749 result += "k-bot"; 33750 } else { 33751 result += "k-mid"; 33752 } 33753 33754 return result; 33755 }, 33756 textClass: function(item) { 33757 var result = "k-in"; 33758 33759 if (item.enabled === false) { 33760 result += " k-state-disabled"; 33761 } 33762 33763 if (item.selected === true) { 33764 result += " k-state-selected"; 33765 } 33766 33767 return result; 33768 }, 33769 textAttributes: function(item) { 33770 return item.url ? " href='" + item.url + "'" : ""; 33771 }, 33772 toggleButtonClass: function(item) { 33773 var result = "k-icon"; 33774 33775 if (item.expanded !== true) { 33776 result += " k-plus"; 33777 } else { 33778 result += " k-minus"; 33779 } 33780 33781 if (item.enabled === false) { 33782 result += "-disabled"; 33783 } 33784 33785 return result; 33786 }, 33787 text: function(item) { 33788 return item.encoded === false ? item.text : kendo.htmlEncode(item.text); 33789 }, 33790 template: function(treeview, item) { 33791 return treeview.template(extend({ item: item }, rendering)); 33792 }, 33793 checkboxTemplate: function(treeview, group, item) { 33794 return treeview.checkboxTemplate(extend({ 33795 treeview: treeview, 33796 group: group, 33797 item: item 33798 }, rendering)); 33799 }, 33800 tag: function(item) { 33801 return item.url ? "a" : "span"; 33802 }, 33803 groupAttributes: function(group) { 33804 return group.expanded !== true ? " style='display:none'" : ""; 33805 }, 33806 groupCssClass: function(group) { 33807 var cssClass = "k-group"; 33808 33809 if (group.firstLevel) { 33810 cssClass += " k-treeview-lines"; 33811 } 33812 33813 return cssClass; 33814 } 33815 }; 33816 33817 ui.plugin(TreeView); 33818})(jQuery); 33819/** 33820 * @fileOverview Provides a Slider and RangeSlider implementation which can be used to display a rich input for 33821 * selecting values or ranges of values. 33822 */ 33823 33824(function($, undefined) { 33825 /** 33826 * 33827 * @name kendo.ui.Slider.Description 33828 * 33829 * @section 33830 * <p> 33831 * The <strong>Slider</strong> provides a rich input for selecting values or ranges of values. Unlike the HTML5 33832 * range input, the <strong>Slider</strong> presents a consistent experience across browsers and features a rich 33833 * API and event model. 33834 * </p> 33835 * <h3>Getting Started</h3> 33836 * <p>There are two types of <strong>Slider</strong>:</p> 33837 * <ol> 33838 * <li><strong>Slider</strong>, which presents one thumb and two opposing buttons for selecting a single value</li> 33839 * <li><strong>RangeSlider</strong>, which present two thumbs for defining a range of values</li> 33840 * </ol> 33841 * <h4>Slider</h4> 33842 * 33843 * @exampleTitle Create an input element 33844 * @example 33845 * <input id="slider" /> 33846 * 33847 * @section 33848 * <p> 33849 * Initialization of a <strong>Slider</strong> should occur after the DOM is fully loaded. It is recommended that 33850 * initialization the <strong>Slider</strong> occur within a handler is provided to $(document).ready(). 33851 * </p> 33852 * 33853 * @exampleTitle Initialize a Slider using a selector within $(document).ready() 33854 * @example 33855 * $(document).ready(function() { 33856 * $("#slider").kendoSlider(); 33857 * }); 33858 * 33859 * @section 33860 * <h4>RangeSlider</h4> 33861 * @exampleTitle Create two HTML input elements in a div 33862 * @example 33863 * <div id="rangeSlider"> 33864 * <input /> 33865 * <input /> 33866 * </div> 33867 * 33868 * @section 33869 * <p> 33870 * Initialization of a <strong>RangeSlider</strong> should occur after the DOM is fully loaded. It is recommended 33871 * that initialization the <strong>RangeSlider</strong> occur within a handler is provided to $(document).ready(). 33872 * </p> 33873 * 33874 * @exampleTitle Initialize a RangeSlider using a selector within $(document).ready() 33875 * @example 33876 * $(document).ready(function() { 33877 * $("#rangeSlider").kendoRangeSlider(); 33878 * }); 33879 * 33880 * @section 33881 * <p> 33882 * The <strong>RangeSlider</strong>
33882 requires two inputs to capture both ends of the value range. This benefits 33883 * scenarios where JavaScript is disabled, in which case users will be presented with two inputs, still allowing 33884 * them to input a valid range. 33885 * </p> 33886 * <h3>Customizing Slider Behaviors</h3> 33887 * <p> 33888 * Many facets of the <strong>Slider</strong> and <strong>RangeSlider</strong> behavior can be configured through 33889 * properties, including: 33890 * </p> 33891 * <ul> 33892 * <li>Minimum and/or maximum values</li> 33893 * <li>Orientation (horizontal or vertical)</li> 33894 * <li>Small or large step</li> 33895 * <li>Tooltip format/placement</li> 33896 * </ul> 33897 * 33898 * @exampleTitle Initialize a Slider and its properties 33899 * @example 33900 * $("#slider").kendoSlider({ 33901 * min: 10, 33902 * max: 50, 33903 * orientation: "vertical", 33904 * smallStep: 1, 33905 * largeStep: 10 33906 * }); 33907 * 33908 * @section 33909 * <h3>Accessing an Existing Slider</h3> 33910 * <p> 33911 * You can reference an existing <strong>Slider</strong> instance via 33912 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Once a reference has been established, you can 33913 * use the API to control its behavior. 33914 * </p> 33915 * 33916 * @exampleTitle Accessing an existing Slider instance 33917 * @example 33918 * var slider = $("#slider").data("kendoSlider"); 33919 * 33920 */ 33921 var kendo = window.kendo, 33922 Widget = kendo.ui.Widget, 33923 Draggable = kendo.ui.Draggable, 33924 extend = $.extend, 33925 format = kendo.format, 33926 parse = kendo.parseFloat, 33927 proxy = $.proxy, 33928 isArray = $.isArray, 33929 math = Math, 33930 support = kendo.support, 33931 touch = support.touch, 33932 pointers = support.pointers, 33933 CHANGE = "change", 33934 SLIDE = "slide", 33935 MOUSE_DOWN = touch ? "touchstart" : "mousedown", 33936 TRACK_MOUSE_DOWN = pointers ? "MSPointerDown" : "mousedown", 33937 MOUSE_UP = touch ? "touchend" : "mouseup", 33938 MOVE_SELECTION = "moveSelection", 33939 KEY_DOWN = "keydown", 33940 CLICK = "click", 33941 MOUSE_OVER = "mouseover", 33942 DRAG_HANDLE = ".k-draghandle", 33943 TRACK_SELECTOR = ".k-slider-track", 33944 TICK_SELECTOR = ".k-tick", 33945 STATE_SELECTED = "k-state-selected", 33946 STATE_DEFAULT = "k-state-default", 33947 STATE_DISABLED = "k-state-disabled", 33948 PRECISION = 3, 33949 DISABLED = "disabled"; 33950 33951 var SliderBase = Widget.extend({ 33952 init: function(element, options) { 33953 var that = this; 33954 33955 Widget.fn.init.call(that, element, options); 33956 33957 options = that.options; 33958 33959 that._distance = options.max - options.min; 33960 that._isHorizontal = options.orientation == "horizontal"; 33961 that._position = that._isHorizontal ? "left" : "bottom"; 33962 that._size = that._isHorizontal ? "width" : "height"; 33963 that._outerSize = that._isHorizontal ? "outerWidth" : "outerHeight"; 33964 33965 options.tooltip.format = options.tooltip.enabled ? options.tooltip.format || "{0}" : "{0}"; 33966 33967 that._createHtml(); 33968 that.wrapper = that.element.closest(".k-slider"); 33969 that._trackDiv = that.wrapper.find(TRACK_SELECTOR); 33970 33971 that._setTrackDivWidth(); 33972 33973 that._maxSelection = that._trackDiv[that._size](); 33974 33975 var sizeBetweenTicks = that._maxSelection / ((options.max - options.min) / options.smallStep); 33976 var pixelWidths = that._calculateItemsWidth(math.floor(that._distance / options.smallStep)); 33977 33978 if (options.tickPlacement != "none" && sizeBetweenTicks >= 2) { 33979 that._trackDiv.before(createSliderItems(options, that._distance)); 33980 that._setItemsWidth(pixelWidths); 33981 that._setItemsTitle(); 33982 that._setItemsLargeTick(); 33983 } 33984 33985 that._calculateSteps(pixelWidths); 33986 33987 that[options.enabled ? "enable" : "disable"](); 33988 33989 that._keyMap = { 33990 37: step(-options.smallStep), // left arrow 33991 40: step(-options.smallStep), // down arrow 33992 39: step(+options.smallStep), // right arrow 33993 38: step(+options.smallStep), // up arrow 33994 35: setValue(options.max), // end 33995 36: setValue(options.min), // home 33996 33: step(+options.largeStep), // page up 33997 34: step(-options.largeStep) // page down 33998 }; 33999 34000 kendo.notify(that); 34001 }, 34002 34003 events: [ 34004 /** 34005 * Fires when the slider value changes as a result of selecting a new value with the drag handle, buttons or keyboard. 34006 * @name kendo.ui.Slider#change 34007 * @event 34008 * @param {Event} e 34009 * @param {Number} e.value Represents the updated value of the slider. 34010 **/ 34011 34012 /** 34013 * Fires when the rangeSlider value changes as a result of selecting a new value with one of the drag handles or the keyboard. 34014 * @name kendo.ui.RangeSlider#change 34015 * @event 34016 * @param {Event} e 34017 * @param {Number} e.value Represents the updated array of values of the first and second drag handle. 34018 **/ 34019 CHANGE, 34020 34021 /** 34022 * Fires when the user drags the drag handle to a new position. 34023 * @name kendo.ui.Slider#slide 34024 * @event 34025 * @param {Event} e 34026 * @param {Number} e.value Represents the value from the current position of the drag handle. 34027 **/ 34028 34029 /** 34030 * Fires when the user drags the drag handle to a new position. 34031 * @name kendo.ui.RangeSlider#slide 34032 * @event 34033 * @param {Event} e 34034 * @param {Number} e.value Represents an array of values of the current positions of the first and second drag handle. 34035 **/ 34036 SLIDE 34037 ], 34038 34039 options: { 34040 enabled: true, 34041 min: 0, 34042 max: 10, 34043 smallStep: 1, 34044 largeStep: 5, 34045 orientation: "horizontal", 34046 tickPlacement: "both", 34047 tooltip: { enabled: true, format: "{0}" } 34048 }, 34049 34050 _setTrackDivWidth: function() { 34051 var that = this, 34052 trackDivPosition = parseFloat(that._trackDiv.css(that._position), 10) * 2; 34053 34054 that._trackDiv[that._size]((that.wrapper[that._size]() - 2) - trackDivPosition); 34055 }, 34056 34057 _setItemsWidth: function(pixelWidths) { 34058 var that = this, 34059 options = that.options, 34060 first = 0, 34061 last = pixelWidths.length - 1, 34062 items = that.wrapper.find(TICK_SELECTOR), 34063 i, 34064 paddingTop = 0, 34065 bordersWidth = 2, 34066 count = items.length, 34067 selection = 0; 34068 34069 for (i = 0; i < count - 2; i++) { 34070 $(items[i + 1])[that._size](pixelWidths[i]); 34071 } 34072 34073 if (that._isHorizontal) { 34074 $(items[first]).addClass("k-first")[that._size](pixelWidths[last - 1]); 34075 $(items[last]).addClass("k-last")[that._size](pixelWidths[last]); 34076 } else { 34077 $(items[last]).addClass("k-first")[that._size](pixelWidths[last]); 34078 $(items[first]).addClass("k-last")[that._size](pixelWidths[last - 1]); 34079 } 34080 34081 if (that._distance % options.smallStep !== 0 && !that._isHorizontal) { 34082 for (i = 0; i < pixelWidths.length; i++) { 34083 selection += pixelWidths[i]; 34084 } 34085 34086 paddingTop = that._maxSelection - selection; 34087 paddingTop += parseFloat(that._trackDiv.css(that._position), 10) + bordersWidth; 34088 34089 that.wrapper.find(".k-slider-items").css("padding-top", paddingTop); 34090 } 34091 }, 34092 34093 _setItemsTitle: function() { 34094 var that = this, 34095 options = that.options, 34096 items = that.wrapper.find(TICK_SELECTOR), 34097 titleNumber = options.min, 34098 count = items.length, 34099 i = that._isHorizontal ? 0 : count - 1, 34100 limit = that._isHorizontal ? count : -1, 34101 increment = that._isHorizontal ? 1 : -1; 34102 34103 for (; i - limit !== 0 ; i += increment) {
34104 $(items[i]).attr("title", format(options.tooltip.format, round(titleNumber))); 34105 titleNumber += options.smallStep; 34106 } 34107 }, 34108 34109 _setItemsLargeTick: function() { 34110 var that = this, 34111 options = that.options, 34112 i, 34113 items = that.wrapper.find(TICK_SELECTOR), 34114 item = {}, 34115 step = round(options.largeStep / options.smallStep); 34116 34117 if ((1000 * options.largeStep) % (1000 * options.smallStep) === 0) { 34118 if (that._isHorizontal) { 34119 for (i = 0; i < items.length; i = round(i + step)) { 34120 item = $(items[i]); 34121 34122 item.addClass("k-tick-large") 34123 .html("<span class='k-label'>" + item.attr("title") + "</span>"); 34124 } 34125 } else { 34126 for (i = items.length - 1; i >= 0; i = round(i - step)) { 34127 item = $(items[i]); 34128 34129 item.addClass("k-tick-large") 34130 .html("<span class='k-label'>" + item.attr("title") + "</span>"); 34131 34132 if (i !== 0 && i !== items.length - 1) { 34133 item.css("line-height", item[that._size]() + "px"); 34134 } 34135 } 34136 } 34137 } 34138 }, 34139 34140 _calculateItemsWidth: function(itemsCount) { 34141 var that = this, 34142 options = that.options, 34143 trackDivSize = parseFloat(that._trackDiv.css(that._size)) + 1, 34144 pixelStep = trackDivSize / that._distance, 34145 itemWidth, 34146 pixelWidths, 34147 i; 34148 34149 if ((that._distance / options.smallStep) - math.floor(that._distance / options.smallStep) > 0) { 34150 trackDivSize -= ((that._distance % options.smallStep) * pixelStep); 34151 } 34152 34153 itemWidth = trackDivSize / itemsCount; 34154 pixelWidths = []; 34155 34156 for (i = 0; i < itemsCount - 1; i++) { 34157 pixelWidths[i] = itemWidth; 34158 } 34159 34160 pixelWidths[itemsCount - 1] = pixelWidths[itemsCount] = itemWidth / 2; 34161 return that._roundWidths(pixelWidths); 34162 }, 34163 34164 _roundWidths: function(pixelWidthsArray) { 34165 var balance = 0, 34166 count = pixelWidthsArray.length, 34167 i; 34168 34169 for (i = 0; i < count; i++) { 34170 balance += (pixelWidthsArray[i] - math.floor(pixelWidthsArray[i])); 34171 pixelWidthsArray[i] = math.floor(pixelWidthsArray[i]); 34172 } 34173 34174 balance = math.round(balance); 34175 34176 return this._addAdditionalSize(balance, pixelWidthsArray); 34177 }, 34178 34179 _addAdditionalSize: function(additionalSize, pixelWidthsArray) { 34180 if (additionalSize === 0) { 34181 return pixelWidthsArray; 34182 } 34183 34184 //set step size 34185 var step = parseFloat(pixelWidthsArray.length - 1) / parseFloat(additionalSize == 1 ? additionalSize : additionalSize - 1), 34186 i; 34187 34188 for (i = 0; i < additionalSize; i++) { 34189 pixelWidthsArray[parseInt(math.round(step * i), 10)] += 1; 34190 } 34191 34192 return pixelWidthsArray; 34193 }, 34194 34195 _calculateSteps: function(pixelWidths) { 34196 var that = this, 34197 options = that.options, 34198 val = options.min, 34199 selection = 0, 34200 itemsCount = math.ceil(that._distance / options.smallStep), 34201 i = 1, 34202 lastItem; 34203 34204 itemsCount += (that._distance / options.smallStep) % 1 === 0 ? 1 : 0; 34205 pixelWidths.splice(0, 0, pixelWidths[itemsCount - 2] * 2); 34206 pixelWidths.splice(itemsCount -1, 1, pixelWidths.pop() * 2); 34207 34208 that._pixelSteps = [selection]; 34209 that._values = [val]; 34210 34211 if (itemsCount === 0) { 34212 return; 34213 } 34214 34215 while (i < itemsCount) { 34216 selection += (pixelWidths[i - 1] + pixelWidths[i]) / 2; 34217 that._pixelSteps[i] = selection; 34218 that._values[i] = val += options.smallStep; 34219 34220 i++; 34221 } 34222 34223 lastItem = that._distance % options.smallStep === 0 ? itemsCount - 1 : itemsCount; 34224 34225 that._pixelSteps[lastItem] = that._maxSelection; 34226 that._values[lastItem] = options.max; 34227 }, 34228 34229 _getValueFromPosition: function(mousePosition, dragableArea) { 34230 var that = this, 34231 options = that.options, 34232 step = math.max(options.smallStep * (that._maxSelection / that._distance), 0), 34233 position = 0, 34234 halfStep = (step / 2), 34235 i; 34236 34237 if (that._isHorizontal) { 34238 position = mousePosition - dragableArea.startPoint; 34239 } else { 34240 position = dragableArea.startPoint - mousePosition; 34241 } 34242 34243 if (that._maxSelection - ((parseInt(that._maxSelection % step, 10) - 3) / 2) < position) { 34244 return options.max; 34245 } 34246 34247 for (i = 0; i < that._pixelSteps.length; i++) { 34248 if (math.abs(that._pixelSteps[i] - position) - 1 <= halfStep) { 34249 return round(that._values[i]); 34250 } 34251 } 34252 }, 34253 34254 _getDragableArea: function() { 34255 var that = this, 34256 offsetLeft = that._trackDiv.offset().left, 34257 offsetTop = that._trackDiv.offset().top; 34258 34259 return { 34260 startPoint: that._isHorizontal ? offsetLeft : offsetTop + that._maxSelection, 34261 endPoint: that._isHorizontal ? offsetLeft + that._maxSelection : offsetTop 34262 }; 34263 }, 34264 34265 _createHtml: function() { 34266 var that = this, 34267 element = that.element, 34268 options = that.options, 34269 inputs = element.find("input"); 34270 34271 if (inputs.length == 2) { 34272 inputs.eq(0).val(options.selectionStart); 34273 inputs.eq(1).val(options.selectionEnd); 34274 } else { 34275 element.val(options.value); 34276 } 34277 34278 element.wrap(createWrapper(options, element, that._isHorizontal)).hide(); 34279 34280 if (options.showButtons) { 34281 element.before(createButton(options, "increase", that._isHorizontal)) 34282 .before(createButton(options, "decrease", that._isHorizontal)); 34283 } 34284 34285 element.before(createTrack(element)); 34286 } 34287 });
34288 34289 function createWrapper (options, element, isHorizontal) { 34290 var orientationCssClass = isHorizontal ? " k-slider-horizontal" : " k-slider-vertical", 34291 style = options.style ? options.style : element.attr("style"), 34292 cssClasses = element.attr("class") ? (" " + element.attr("class")) : "", 34293 tickPlacementCssClass = ""; 34294 34295 if (options.tickPlacement == "bottomRight") { 34296 tickPlacementCssClass = " k-slider-bottomright"; 34297 } else if (options.tickPlacement == "topLeft") { 34298 tickPlacementCssClass = " k-slider-topleft"; 34299 } 34300 34301 style = style ? " style='" + style + "'" : ""; 34302 34303 return "<div class='k-widget k-slider" + orientationCssClass + cssClasses + "'" + style + ">" + 34304 "<div class='k-slider-wrap" + (options.showButtons ? " k-slider-buttons" : "") + tickPlacementCssClass + 34305 "'></div></div>"; 34306 } 34307 34308 function createButton (options, type, isHorizontal) { 34309 var buttonCssClass = ""; 34310 34311 if (type == "increase") { 34312 buttonCssClass = isHorizontal ? "k-arrow-next" : "k-arrow-up"; 34313 } else { 34314 buttonCssClass = isHorizontal ? "k-arrow-prev" : "k-arrow-down"; 34315 } 34316 34317 return "<a class='k-button k-button-" + type + "'><span class='k-icon " + buttonCssClass + 34318 "' title='" + options[type + "ButtonTitle"] + "'>" + options[type + "ButtonTitle"] + "</span></a>"; 34319 } 34320 34321 function createSliderItems (options, distance) { 34322 var result = "<ul class='k-reset k-slider-items'>", 34323 count = math.floor(round(distance / options.smallStep)) + 1, 34324 i; 34325 34326 for(i = 0; i < count; i++) { 34327 result += "<li class='k-tick'> </li>"; 34328 } 34329 34330 result += "</ul>"; 34331 34332 return result; 34333 } 34334 34335 function createTrack (element) { 34336 var dragHandleCount = element.is("input") ? 1 : 2; 34337 34338 return "<div class='k-slider-track'><div class='k-slider-selection'><!-- --></div>" + 34339 "<a href='#' class='k-draghandle' title='Drag'>Drag</a>" + 34340 (dragHandleCount > 1 ? "<a href='#' class='k-draghandle' title='Drag'>Drag</a>" : "") + 34341 "</div>"; 34342 } 34343 34344 function step(stepValue) { 34345 return function (value) { 34346 return value + stepValue; 34347 }; 34348 } 34349 34350 function setValue(value) { 34351 return function () { 34352 return value; 34353 }; 34354 } 34355 34356 function formatValue(value) { 34357 return (value + "").replace(".", kendo.cultures.current.numberFormat["."]); 34358 } 34359 34360 function round(value) { 34361 value = parseFloat(value, 10); 34362 var power = math.pow(10, PRECISION || 0); 34363 return math.round(value * power) / power; 34364 } 34365 34366 function parseAttr(element, name) { 34367 return parse(element.getAttribute(name)) || undefined; 34368 } 34369 34370 var touchLocation = function(e) { 34371 return { 34372 idx: 0, 34373 x: e.pageX, 34374 y: e.pageY 34375 }; 34376 }; 34377 34378 if (support.pointers) { 34379 /** @ignore */ 34380 touchLocation = function(e) { 34381 return { 34382 idx: 0, 34383 x: e.originalEvent.clientX, 34384 y: e.originalEvent.clientY 34385 }; 34386 }; 34387 } 34388 34389 if (support.touch) { 34390 /** @ignore */ 34391 touchLocation = function(e, id) { 34392 var changedTouches = e.changedTouches || e.originalEvent.changedTouches; 34393 34394 if (id) { 34395 var output = null; 34396 $.each(changedTouches, function(idx, value) { 34397 if (id == value.identifier) { 34398 output = { 34399 idx: value.identifier, 34400 x: value.pageX, 34401 y: value.pageY 34402 }; 34403 } 34404 }); 34405 return output; 34406 } else { 34407 return { 34408 idx: changedTouches[0].identifier, 34409 x: changedTouches[0].pageX, 34410 y: changedTouches[0].pageY 34411 }; 34412 } 34413 }; 34414 } 34415 34416 var Slider = SliderBase.extend(/** @lends kendo.ui.Slider.prototype */{ 34417 /** 34418 * 34419 * Creates a Slider instance. 34420 * 34421 * @constructs 34422 * @extends kendo.ui.Widget 34423 * 34424 * @param {DomElement} element DOM element 34425 * @param {Object} options Configuration options. 34426 * 34427 * @option {Number} [min] <0> 34428 * The minimum value of the <strong>Slider</strong>. 34429 * 34430 * @option {Number} [max] <10> 34431 * The maximum value of the <strong>Slider</strong>. 34432 * 34433 * @option {Boolean} [showButtons] <true> 34434 * Can be used to show (<b>true</b>) or hide (<b>false</b>) the 34435 * increase and decrease buttons of a <strong>Slider</strong>. 34436 * 34437 * @option {Object} [tooltip] 34438 * Configuration of the <strong>Slider</strong> tooltip. 34439 * 34440 * @option {Boolean} [tooltip.enabled] <true> 34441 * Disables (<b>false</b>) or enables (<b>true</b>) the tooltip of 34442 * the <strong>Slider</strong>. 34443 * 34444 * @option {String} [tooltip.format] <"{0}">
34445 * Format string for the text of the tooltip. Note: The applied 34446 * format will also influence the appearance of the <strong>Slider</strong> 34447 * tick labels. 34448 * 34449 * @option {Number} [value] <0> 34450 * The underlying value of the <strong>Slider</strong>. 34451 * 34452 * @option {String} [orientation] <"horizontal"> 34453 * The orientation of a <strong>Slider</strong>; <strong>"horizontal"</strong> or <strong>"vertical"</strong>. 34454 * 34455 * @option {String} [tickPlacement] <"both"> 34456 * Denotes the location of the tick marks in the <strong>Slider</strong>. The available options are: 34457 * <div class="details-list"> 34458 * <dl> 34459 * <dt> 34460 * <code>"topLeft"</code> 34461 * </dt> 34462 * <dd> 34463 * Tick marks are located on the top of the horizontal widget or on the left of 34464 * the vertical widget. 34465 * </dd> 34466 * <dt> 34467 * <code>"bottomRight"</code> 34468 * </dt> 34469 * <dd> 34470 * Tick marks are located on the bottom of the horizontal widget or on the 34471 * right side of the vertical widget. 34472 * </dd> 34473 * <dt> 34474 * <code>"both"</code> 34475 * </dt> 34476 * <dd> 34477 * Tick marks are located on both sides of the widget. 34478 * </dd> 34479 * <dt> 34480 * <code>"none"</code> 34481 * </dt> 34482 * <dd> 34483 * Tick marks are not visible. 34484 * </dd> 34485 * </dl> 34486 * </div> 34487 * @option {Number} [smallStep] <1> 34488 * The small step value of the <strong>Slider</strong>. The underlying value will be changed when the end user 34489 * (1) clicks on the increase or decrease buttons of the <strong>Slider</strong>, (2) presses the arrow keys 34490 * (the drag handle must be focused), or (3) drags the drag handle. 34491 * 34492 * @option {Number} [largeStep] <5> 34493 * The delta with which the value will change when the user presses the Page Up or Page Down key (the drag 34494 * handle must be focused). Note: The allied largeStep will also set large tick for every large step. 34495 * 34496 * @option {String} [increaseButtonTitle] <"Increase"> 34497 * The title of the increase button of the <strong>Slider</strong>. 34498 * 34499 * @option {String} [decreaseButtonTitle] <"Decrease"> 34500 * The title of the decrease button of the <strong>Slider</strong>. 34501 * 34502 */ 34503 init: function(element, options) { 34504 var that = this, 34505 dragHandle; 34506 34507 element.type = "text"; 34508 34509 element = $(element); 34510 34511 options = extend({}, { 34512 value: parseAttr(element[0], "value"), 34513 min: parseAttr(element[0], "min"), 34514 max: parseAttr(element[0], "max"), 34515 smallStep: parseAttr(element[0], "step") 34516 }, options); 34517 34518 if (options && options.enabled === undefined) { 34519 options.enabled = !element.is("[disabled]"); 34520 } 34521 34522 SliderBase.fn.init.call(that, element, options); 34523 options = that.options; 34524 34525 that._setValueInRange(options.value); 34526 dragHandle = that.wrapper.find(DRAG_HANDLE); 34527 34528 new Slider.Selection(dragHandle, that, options); 34529 that._drag = new Slider.Drag(dragHandle, "", that, options); 34530 }, 34531 34532 options: { 34533 name: "Slider", 34534 value: 0, 34535 showButtons: true, 34536 increaseButtonTitle: "Increase", 34537 decreaseButtonTitle: "Decrease", 34538 tooltip: { format: "{0}" } 34539 }, 34540 34541 /** 34542 * Enable/Disable the <strong>Slider</strong> widget. 34543 * @param {Boolean} enable 34544 * The argument, which defines whether to enable/disable the <strong>Slider</strong>. 34545 * @example 34546 * // get a reference to the slider widget 34547 * var slider = $("#slider").data("kendoSlider"); 34548 * 34549 * // disables the slider 34550 * slider.enable(false); 34551 * 34552 * // enables the slider 34553 * slider.enable(true); 34554 */ 34555 enable: function (enable) { 34556 var that = this, 34557 options = that.options, 34558 clickHandler, 34559 move; 34560 34561 that.disable(); 34562 if (enable === false) { 34563 return; 34564 } 34565 34566 that.wrapper 34567 .removeClass(STATE_DISABLED) 34568 .addClass(STATE_DEFAULT); 34569 34570 that.wrapper.find("input").removeAttr(DISABLED); 34571 34572 clickHandler = function (e) { 34573 var location = touchLocation(e), 34574 mousePosition = that._isHorizontal ? location.x : location.y, 34575 dragableArea = that._getDragableArea(), 34576 target = $(e.target); 34577 34578 if (target.hasClass("k-draghandle")) { 34579 target.addClass(STATE_SELECTED); 34580 return; 34581 } 34582 34583 that._update(that._getValueFromPosition(mousePosition, dragableArea)); 34584 34585 that._drag.dragstart(e); 34586 }; 34587 34588 that.wrapper 34589 .find(TICK_SELECTOR + ", " + TRACK_SELECTOR) 34590 .bind(TRACK_MOUSE_DOWN, clickHandler) 34591 .end().bind(TRACK_MOUSE_DOWN, function() { 34592 $(document.documentElement).one("selectstart", kendo.preventDefault); 34593 }); 34594 34595 that.wrapper 34596 .find(DRAG_HANDLE) 34597 .bind(MOUSE_UP, function (e) { 34598 $(e.target).removeClass(STATE_SELECTED); 34599 }) 34600 .bind(CLICK, function (e) { 34601 e.preventDefault(); 34602 }); 34603 34604 move = proxy(function (sign) { 34605 that._setValueInRange(that._nextValueByIndex(that._valueIndex + (sign * 1))); 34606 }, that); 34607 34608 if (options.showButtons) { 34609 var mouseDownHandler = proxy(function(e, sign) { 34610 if (e.which === 1 || (touch && e.which === 0)) { 34611 move(sign); 34612 34613 this.timeout = setTimeout(proxy(function () { 34614 this.timer = setInterval(function () { 34615 move(sign); 34616 }, 60); 34617 }, this), 200); 34618 } 34619 }, that); 34620 34621 that.wrapper.find(".k-button") 34622 .bind(MOUSE_UP, proxy(function (e) { 34623 this._clearTimer(); 34624 }, that)) 34625 .bind(MOUSE_OVER, function (e) { 34626 $(e.currentTarget).addClass("k-state-hover"); 34627 }) 34628 .bind("mouseout", proxy(function (e) { 34629 $(e.currentTarget).removeClass("k-state-hover"); 34630 this._clearTimer(); 34631 }, that)) 34632 .eq(0) 34633 .bind(MOUSE_DOWN, proxy(function (e) { 34634 mouseDownHandler(e, 1); 34635 }, that)) 34636 .click(false) 34637 .end() 34638 .eq(1) 34639 .bind(MOUSE_DOWN, proxy(function (e) { 34640 mouseDownHandler(e, -1); 34641 }, that)) 34642 .click(false); 34643 } 34644 34645 that.wrapper 34646 .find(DRAG_HANDLE).bind(KEY_DOWN, proxy(this._key
34646down, that)); 34647 34648 options.enabled = true; 34649 }, 34650 34651 disable: function () { 34652 var that = this; 34653 34654 that.wrapper 34655 .removeClass(STATE_DEFAULT) 34656 .addClass(STATE_DISABLED); 34657 34658 $(that.element).attr(DISABLED, DISABLED); 34659 34660 that.wrapper 34661 .find(".k-button") 34662 .unbind(MOUSE_DOWN) 34663 .bind(MOUSE_DOWN, false) 34664 .unbind(MOUSE_UP) 34665 .bind(MOUSE_UP, false) 34666 .unbind("mouseleave") 34667 .bind("mouseleave", false) 34668 .unbind(MOUSE_OVER) 34669 .bind(MOUSE_OVER, false); 34670 34671 that.wrapper 34672 .find(TICK_SELECTOR + ", " + TRACK_SELECTOR).unbind(TRACK_MOUSE_DOWN); 34673 34674 that.wrapper 34675 .find(DRAG_HANDLE) 34676 .unbind(MOUSE_UP) 34677 .unbind(KEY_DOWN) 34678 .unbind(CLICK) 34679 .bind(KEY_DOWN, false); 34680 34681 that.options.enabled = false; 34682 }, 34683 34684 _update: function (val) { 34685 var that = this, 34686 change = that.value() != val; 34687 34688 that.value(val); 34689 34690 if (change) { 34691 that.trigger(CHANGE, { value: that.options.value }); 34692 } 34693 }, 34694 34695 /** 34696 * 34697 * Gets or sets the value of a <strong>Slider</strong>. It accepts a string or number as parameters and returns 34698 * a number representing the underlying value. 34699 * 34700 * @param {String} [value] 34701 * The value to be set for a Slider. 34702 * 34703 * @example 34704 * var slider = $("#slider").data("kendoSlider"); 34705 * var sliderValue = slider.value(); 34706 * 34707 */ 34708 value: function (value) { 34709 var that = this, 34710 options = that.options; 34711 34712 value = round(value); 34713 if (isNaN(value)) { 34714 return options.value; 34715 } 34716 34717 if (value >= options.min && value <= options.max) { 34718 if (options.value != value) { 34719 that.element.attr("value", formatValue(value)); 34720 options.value = value; 34721 that._refresh(); 34722 } 34723 } 34724 }, 34725 34726 _refresh: function () { 34727 this.trigger(MOVE_SELECTION, { value: this.options.value }); 34728 }, 34729 34730 _clearTimer: function (e) { 34731 clearTimeout(this.timeout); 34732 clearInterval(this.timer); 34733 }, 34734 34735 _keydown: function (e) { 34736 var that = this; 34737 34738 if (e.keyCode in that._keyMap) { 34739 that._setValueInRange(that._keyMap[e.keyCode](that.options.value)); 34740 e.preventDefault(); 34741 } 34742 }, 34743 34744 _setValueInRange: function (val) { 34745 var that = this, 34746 options = that.options; 34747 34748 val = round(val); 34749 if (isNaN(val)) { 34750 that._update(options.min); 34751 return; 34752 } 34753 34754 val = math.max(math.min(val, options.max), options.min); 34755 that._update(val); 34756 }, 34757 34758 _nextValueByIndex: function (index) { 34759 var count = this._values.length; 34760 return this._values[math.max(0, math.min(index, count - 1))]; 34761 } 34762 }); 34763 34764 Slider.Selection = function (dragHandle, that, options) { 34765 function moveSelection (val) { 34766 var selectionValue = val - options.min, 34767 index = that._valueIndex = math.ceil(round(selectionValue / options.smallStep)), 34768 selection = parseInt(that._pixelSteps[index], 10), 34769 selectionDiv = that._trackDiv.find(".k-slider-selection"), 34770 halfDragHanndle = parseInt(dragHandle[that._outerSize]() / 2, 10); 34771 34772 selectionDiv[that._size](selection); 34773 dragHandle.css(that._position, selection - halfDragHanndle); 34774 } 34775 34776 moveSelection(options.value); 34777 34778 that.bind([CHANGE, SLIDE, MOVE_SELECTION], function (e) { 34779 moveSelection(parseFloat(e.value, 10)); 34780 }); 34781 }; 34782 34783 Slider.Drag = function (dragHandle, type, owner, options) { 34784 var that = this; 34785 that.owner = owner; 34786 that.options = options; 34787 that.dragHandle = dragHandle; 34788 that.dragHandleSize = dragHandle[owner._outerSize](); 34789 that.type = type; 34790 34791 that.draggable = new Draggable(dragHandle, { 34792 threshold: 0, 34793 dragstart: proxy(that._dragstart, that), 34794 drag: proxy(that.drag, that), 34795 dragend: proxy(that.dragend, that), 34796 dragcancel: proxy(that.dragcancel, that) 34797 }); 34798 34799 dragHandle.click(false); 34800 }; 34801 34802 Slider.Drag.prototype = { 34803 dragstart: function(e) { 34804 // HACK to initiate click on the line 34805 this.draggable.drag._start(e); 34806 }, 34807 34808 _dragstart: function(e) { 34809 var that = this, 34810 owner = that.owner,
34811 options = that.options, 34812 tooltip = options.tooltip, 34813 html = '', 34814 tooltipTemplate, 34815 formattedSelectionStart, 34816 formattedSelectionEnd; 34817 34818 if (!options.enabled) { 34819 e.preventDefault(); 34820 return; 34821 } 34822 34823 owner.element.unbind(MOUSE_OVER); 34824 that.dragHandle.addClass(STATE_SELECTED); 34825 34826 that.dragableArea = owner._getDragableArea(); 34827 that.step = math.max(options.smallStep * (owner._maxSelection / owner._distance), 0); 34828 34829 if (that.type) { 34830 that.selectionStart = options.selectionStart; 34831 that.selectionEnd = options.selectionEnd; 34832 owner._setZIndex(that.type); 34833 } else { 34834 that.oldVal = that.val = options.value; 34835 } 34836 34837 if (tooltip.enabled) { 34838 if (tooltip.template) { 34839 tooltipTemplate = that.tooltipTemplate = kendo.template(tooltip.template); 34840 } 34841 34842 that.tooltipDiv = $("<div class='k-widget k-tooltip'><!-- --></div>").appendTo(document.body); 34843 34844 if (that.type) { 34845 if (that.tooltipTemplate) { 34846 html = tooltipTemplate({ 34847 selectionStart: that.selectionStart, 34848 selectionEnd: that.selectionEnd 34849 }); 34850 } else { 34851 formattedSelectionStart = format(tooltip.format, that.selectionStart); 34852 formattedSelectionEnd = format(tooltip.format, that.selectionEnd); 34853 34854 html = formattedSelectionStart + ' - ' + formattedSelectionEnd; 34855 } 34856 } else { 34857 that.tooltipInnerDiv = "<div class='k-callout k-callout-" + (owner._isHorizontal ? 's' : 'e') + "'><!-- --></div>"; 34858 if (that.tooltipTemplate) { 34859 html = tooltipTemplate({ 34860 value: that.val 34861 }); 34862 } else { 34863 html = format(tooltip.format, that.val); 34864 } 34865 html += that.tooltipInnerDiv; 34866 } 34867 34868 that.tooltipDiv.html(html); 34869 34870 that.moveTooltip(); 34871 } 34872 }, 34873 34874 drag: function (e) { 34875 var that = this, 34876 owner = that.owner, 34877 options = that.options, 34878 x = e.x.location, 34879 y = e.y.location, 34880 startPoint = that.dragableArea.startPoint, 34881 endPoint = that.dragableArea.endPoint, 34882 tooltip = options.tooltip, 34883 html = "", 34884 tooltipTemplate = that.tooltipTemplate, 34885 slideParams, 34886 formattedSelectionStart, 34887 formattedSelectionEnd; 34888 34889 e.preventDefault(); 34890 34891 if (owner._isHorizontal) { 34892 that.val = that.constrainValue(x, startPoint, endPoint, x >= endPoint); 34893 } else { 34894 that.val = that.constrainValue(y, endPoint, startPoint, y <= endPoint); 34895 } 34896 34897 if (that.oldVal != that.val) { 34898 that.oldVal = that.val; 34899 34900 if (that.type) { 34901 if (that.type == "firstHandle") { 34902 if (that.val < that.selectionEnd) { 34903 that.selectionStart = that.val; 34904 } else { 34905 that.selectionStart = that.selectionEnd = that.val; 34906 } 34907 } else { 34908 if (that.val > that.selectionStart) { 34909 that.selectionEnd = that.val; 34910 } else { 34911 that.selectionStart = that.selectionEnd = that.val; 34912 } 34913 } 34914 slideParams = { 34915 values: [that.selectionStart, that.selectionEnd],
34916 value: [that.selectionStart, that.selectionEnd] 34917 }; 34918 } else { 34919 slideParams = { value: that.val }; 34920 } 34921 34922 owner.trigger(SLIDE, slideParams); 34923 34924 if (tooltip.enabled) { 34925 if (that.type) { 34926 if (that.tooltipTemplate) { 34927 html = tooltipTemplate({ 34928 selectionStart: that.selectionStart, 34929 selectionEnd: that.selectionEnd 34930 }); 34931 } else { 34932 formattedSelectionStart = format(tooltip.format, that.selectionStart); 34933 formattedSelectionEnd = format(tooltip.format, that.selectionEnd); 34934 html = formattedSelectionStart + " - " + formattedSelectionEnd; 34935 } 34936 } else { 34937 if (that.tooltipTemplate) { 34938 html = tooltipTemplate({ 34939 value: that.val 34940 }); 34941 } else { 34942 html = format(tooltip.format, that.val); 34943 } 34944 34945 html += that.tooltipInnerDiv; 34946 } 34947 that.tooltipDiv.html(html); 34948 that.moveTooltip(); 34949 } 34950 } 34951 }, 34952 34953 dragcancel: function(e) { 34954 this.owner._refresh(); 34955 return this._end(); 34956 }, 34957 34958 dragend: function(e) { 34959 var that = this, 34960 owner = that.owner; 34961 34962 if (that.type) { 34963 owner._update(that.selectionStart, that.selectionEnd); 34964 } else { 34965 owner._update(that.val); 34966 } 34967 34968 return that._end(); 34969 }, 34970 34971 _end: function() { 34972 var that = this, 34973 owner = that.owner; 34974 34975 if (owner.options.tooltip.enabled && owner.options.enabled) { 34976 that.tooltipDiv.remove(); 34977 } 34978 34979 that.dragHandle.removeClass(STATE_SELECTED); 34980 owner.element.bind(MOUSE_OVER); 34981 34982 return false; 34983 }, 34984 34985 moveTooltip: function () { 34986 var that = this, 34987 owner = that.owner, 34988 positionTop = 0, 34989 positionLeft = 0, 34990 dragHandleOffset = that.dragHandle.offset(), 34991 margin = 4, 34992 callout = that.tooltipDiv.find(".k-callout"), 34993 dragHandles, 34994 firstDragHandleOffset, 34995 secondDragHandleOffset; 34996 34997 if (that.type) { 34998 dragHandles = owner.wrapper.find(DRAG_HANDLE); 34999 firstDragHandleOffset = dragHandles.eq(0).offset(); 35000 secondDragHandleOffset = dragHandles.eq(1).offset(); 35001 35002 if (owner._isHorizontal) { 35003 positionTop = secondDragHandleOffset.top; 35004 positionLeft = firstDragHandleOffset.left + ((secondDragHandleOffset.left - firstDragHandleOffset.left) / 2); 35005 } else { 35006 positionTop = firstDragHandleOffset.top + ((secondDragHandleOffset.top - firstDragHandleOffset.top) / 2); 35007 positionLeft = secondDragHandleOffset.left; 35008 } 35009 } else { 35010 positionTop = dragHandleOffset.top; 35011 positionLeft = dragHandleOffset.left; 35012 } 35013 if (owner._isHorizontal) { 35014 positionLeft -= parseInt((that.tooltipDiv.outerWidth() - that.dragHandle[owner._outerSize]()) / 2, 10); 35015 positionTop -= that.tooltipDiv.outerHeight() + callout.height() + margin; 35016 } else { 35017 positionTop -= parseInt((that.tooltipDiv.outerHeight() - that.dragHandle[owner._outerSize]()) / 2, 10); 35018 positionLeft -= that.tooltipDiv.outerWidth() + callout.width() + margin; 35019 } 35020 35021 that.tooltipDiv.css({ top: positionTop, left: positionLeft }); 35022 }, 35023 35024 constrainValue: function (position, min, max, maxOverflow) { 35025 var that = this, 35026 val = 0; 35027 35028 if (min < position && position < max) { 35029 val = that.owner._getValueFromPosition(position, that.dragableArea); 35030 } else 35031 if (maxOverflow) { 35032 val = that.options.max; 35033 } else { 35034 val = that.options.min; 35035 } 35036 35037 return val; 35038 } 35039 35040 }; 35041
35042 kendo.ui.plugin(Slider); 35043 35044 var RangeSlider = SliderBase.extend(/** @lends kendo.ui.RangeSlider.prototype */{ 35045 /** 35046 * 35047 * Creates a RangeSlider instance. 35048 * 35049 * @constructs 35050 * @extends kendo.ui.Widget 35051 * 35052 * @param {DomElement} element DOM element 35053 * @param {Object} options Configuration options. 35054 * 35055 * @option {Number} [min] <0> 35056 * The minimum value of the <strong>RangeSlider</strong>. 35057 * 35058 * @option {Number} [max] <10> 35059 * The maximum value of the <strong>RangeSlider</strong>. 35060 * 35061 * @option {Object} [tooltip] 35062 * Configuration of the <strong>RangeSlider</strong> tooltip. 35063 * 35064 * @option {String} [tooltip.format] <"{0}"> 35065 * Format string for the text of the tooltip. Note: The applied format will also influence the appearance of 35066 * the <strong>RangeSlider</strong> tick labels. 35067 * 35068 * @option {Boolean} [tooltip.enabled] <true> 35069 * Disables (<b>false</b>) or enables (<b>true</b>) the tooltip of the <strong>RangeSlider</strong>. 35070 * 35071 * @option {String} [orientation] <"horizontal"> 35072 * The orientation of a <strong>RangeSlider</strong>; <strong>"horizontal"</strong> or 35073 * <strong>"vertical"</strong>. 35074 * 35075 * @option {String} [tickPlacement] <"both"> 35076 * Denotes the location of the tick marks in the <strong>RangeSlider</strong>. The available options are: 35077 * <div class="details-list"> 35078 * <dl> 35079 * <dt> 35080 * <code>"topLeft"</code> 35081 * </dt> 35082 * <dd> 35083 * Tick marks are located on the top of the horizontal widget or on the left of 35084 * the vertical widget. 35085 * </dd> 35086 * <dt> 35087 * <code>"bottomRight"</code> 35088 * </dt> 35089 * <dd> 35090 * Tick marks are located on the bottom of the horizontal widget or on the 35091 * right side of the vertical widget. 35092 * </dd> 35093 * <dt> 35094 * <code>"both"</code> 35095 * </dt> 35096 * <dd> 35097 * Tick marks are located on both sides of the widget. 35098 * </dd> 35099 * <dt> 35100 * <code>"none"</code> 35101 * </dt> 35102 * <dd> 35103 * Tick marks are not visible. 35104 * </dd> 35105 * </dl> 35106 * </div> 35107 * 35108 * @option {Number} [smallStep] <1> 35109 * The small step value of the <strong>RangeSlider</strong>. The underlying value will be changed when the end 35110 * user (1) clicks on the increase or decrease buttons of the <strong>RangeSlider</strong>, (2) presses the 35111 * arrow keys (the drag handle must be focused), or (3) drags the drag handle. 35112 * 35113 * @option {Number} [largeStep] <5> 35114 * The delta with which the value will change when the user presses the Page Up or Page Down key (the drag 35115 * handle must be focused). Note: The allied largeStep will also set large tick for every large step. 35116 * 35117 * @option {Number} [selectionStart] <0> 35118 * The selection start value of the <strong>RangeSlider</strong>. 35119 * 35120 * @option {Number} [selectionEnd] <10> 35121 * The selection end value of the <strong>RangeSlider</strong>. 35122 * 35123 */ 35124 init: function(element, options) { 35125 var that = this, 35126 inputs = $(element).find("input"), 35127 firstInput = inputs.eq(0)[0], 35128 secondInput = inputs.eq(1)[0]; 35129 35130 firstInput.type = "text"; 35131 secondInput.type = "text"; 35132 35133 options = extend({}, { 35134 selectionStart: parseAttr(firstInput, "value"), 35135 min: parseAttr(firstInput, "min"), 35136 max: parseAttr(firstInput, "max"), 35137 smallStep: parseAttr(firstInput, "step") 35138 }, { 35139 selectionEnd: parseAttr(secondInput, "value"), 35140 min: parseAttr(secondInput, "min"), 35141 max: parseAttr(secondInput, "max"), 35142 smallStep: parseAttr(secondInput, "step") 35143 }, options); 35144 35145 if (options && options.enabled === undefined) { 35146 options.enabled = !inputs.is("[disabled]"); 35147 } 35148 35149 SliderBase.fn.init.call(that, element, options); 35150 options = that.options; 35151 that._setValueInRange(options.selectionStart, options.selectionEnd); 35152 35153 var dragHandles = that.wrapper.find(DRAG_HANDLE);
35154 35155 new RangeSlider.Selection(dragHandles, that, options); 35156 that._firstHandleDrag = new Slider.Drag(dragHandles.eq(0), "firstHandle", that, options); 35157 that._lastHandleDrag = new Slider.Drag(dragHandles.eq(1), "lastHandle" , that, options); 35158 }, 35159 35160 options: { 35161 name: "RangeSlider", 35162 selectionStart: 0, 35163 selectionEnd: 10, 35164 tooltip: { format: "{0}" } 35165 }, 35166 35167 /** 35168 * Enable/Disable the <strong>RangeSlider</strong> widget. 35169 * @param {Boolean} enable 35170 * The argument, which defines whether to enable/disable the <strong>RangeSlider</strong>. 35171 * @example 35172 * // get a reference to the range slider widget 35173 * var rangeSlider = $("#rangeSlider").data("kendoRangeSlider"); 35174 * 35175 * // disables the range slider 35176 * rangeSlider.enable(false); 35177 * 35178 * // enables the range slider 35179 * rangeSlider.enable(true); 35180 */ 35181 enable: function (enable) { 35182 var that = this, 35183 options = that.options, 35184 clickHandler; 35185 35186 that.disable(); 35187 if (enable === false) { 35188 return; 35189 } 35190 35191 that.wrapper 35192 .removeClass(STATE_DISABLED) 35193 .addClass(STATE_DEFAULT); 35194 35195 that.wrapper.find("input").removeAttr(DISABLED); 35196 35197 clickHandler = function (e) { 35198 var location = touchLocation(e), 35199 mousePosition = that._isHorizontal ? location.x : location.y, 35200 dragableArea = that._getDragableArea(), 35201 val = that._getValueFromPosition(mousePosition, dragableArea), 35202 target = $(e.target); 35203 35204 if (target.hasClass("k-draghandle")) { 35205 target.addClass(STATE_SELECTED); 35206 return; 35207 } 35208 35209 if (val < options.selectionStart) { 35210 that._setValueInRange(val, options.selectionEnd); 35211 that._firstHandleDrag.dragstart(e); 35212 } else if (val > that.selectionEnd) { 35213 that._setValueInRange(options.selectionStart, val); 35214 that._lastHandleDrag.dragstart(e); 35215 } else { 35216 if (val - options.selectionStart <= options.selectionEnd - val) { 35217 that._setValueInRange(val, options.selectionEnd); 35218 that._firstHandleDrag.dragstart(e); 35219 } else { 35220 that._setValueInRange(options.selectionStart, val); 35221 that._lastHandleDrag.dragstart(e); 35222 } 35223 } 35224 }; 35225 35226 that.wrapper 35227 .find(TICK_SELECTOR + ", " + TRACK_SELECTOR) 35228 .bind(TRACK_MOUSE_DOWN, clickHandler) 35229 .end().bind(TRACK_MOUSE_DOWN, function() { 35230 $(document.documentElement).one("selectstart", kendo.preventDefault); 35231 }); 35232 35233 that.wrapper 35234 .find(DRAG_HANDLE) 35235 .bind(MOUSE_UP, function (e) { 35236 $(e.target).removeClass(STATE_SELECTED); 35237 }) 35238 .bind(CLICK, function (e) { 35239 e.preventDefault(); 35240 }); 35241 35242 that.wrapper.find(DRAG_HANDLE) 35243 .eq(0).bind(KEY_DOWN, 35244 proxy(function(e) { 35245 this._keydown(e, "firstHandle"); 35246 }, that) 35247 ) 35248 .end() 35249 .eq(1).bind(KEY_DOWN, 35250 proxy(function(e) { 35251 this._keydown(e, "lastHandle"); 35252 }, that) 35253 ); 35254 35255 that.options.enabled = true; 35256 }, 35257 35258 disable: function () { 35259 var that = this; 35260 35261 that.wrapper 35262 .removeClass(STATE_DEFAULT) 35263 .addClass(STATE_DISABLED); 35264 35265 that.wrapper.find("input").attr(DISABLED, DISABLED); 35266 35267 that.wrapper 35268 .find(TICK_SELECTOR + ", " + TRACK_SELECTOR).unbind(TRACK_MOUSE_DOWN); 35269 35270 that.wrapper 35271 .find(DRAG_HANDLE) 35272 .unbind(MOUSE_UP) 35273 .unbind(KEY_DOWN) 35274 .unbind(CLICK) 35275 .bind(KEY_DOWN, false); 35276 35277 that.options.enabled = false; 35278 }, 35279 35280 _keydown: function (e, handle) { 35281 var that = this, 35282 selectionStartValue = that.options.selectionStart, 35283 selectionEndValue = that.options.selectionEnd; 35284 35285 if (e.keyCode in that._keyMap) { 35286 if (handle == "firstHandle") { 35287 selectionStartValue = that._keyMap[e.keyCode](selectionStartValue); 35288 35289 if (selectionStartValue > selectionEndValue) { 35290 selectionEndValue = selectionStartValue; 35291 } 35292 } else { 35293 selectionEndValue = that._keyMap[e.keyCode](selectionEndValue); 35294 35295 if (selectionStartValue > selectionEndValue) { 35296 selectionStartValue = selectionEndValue; 35297 } 35298 } 35299 35300 that._setValueInRange(selectionStartValue, selectionEndValue); 35301 e.preventDefault(); 35302 } 35303 }, 35304 35305 _update: function (selectionStart, selectionEnd) { 35306 var that = this, 35307 values = that.value(); 35308 35309 var change = values[0] != selectionStart || values[1] != selectionEnd; 35310 35311 that.value([selectionStart, selectionEnd]); 35312 35313 if (change) { 35314 that.trigger(CHANGE, { 35315 values: [selectionStart, selectionEnd], 35316 value: [selectionStart, selectionEnd] 35317 }); 35318 } 35319 }, 35320 35321 /** 35322 * 35323 * The value method gets or sets the start and end values of the <strong>RangeSlider</strong>. It 35324 * accepts an array as parameter, and returns an object array with the start and end 35325 * selection values. 35326 * 35327 * @example 35328 * var rangeSider = $("#rangeSlider").data("kendoRangeSlider"); 35329 * rangeSlider.value(); 35330 * 35331 */ 35332 value: function(value) { 35333 if (value && value.length) { 35334 return this._value(value[0], value[1]); 35335 } else {
35336 return this._value(); 35337 } 35338 }, 35339 35340 _value: function(start, end) { 35341 var that = this, 35342 options = that.options, 35343 selectionStart = options.selectionStart, 35344 selectionEnd = options.selectionEnd; 35345 35346 if (isNaN(start) && isNaN(end)) { 35347 return [selectionStart, selectionEnd]; 35348 } else { 35349 start = round(start); 35350 end = round(end); 35351 } 35352 35353 if (start >= options.min && start <= options.max && 35354 end >= options.min && end <= options.max && start <= end) { 35355 if (selectionStart != start || selectionEnd != end) { 35356 that.element.find("input") 35357 .eq(0).attr("value", formatValue(start)) 35358 .end() 35359 .eq(1).attr("value", formatValue(end)); 35360 35361 options.selectionStart = start; 35362 options.selectionEnd = end; 35363 that._refresh(); 35364 } 35365 } 35366 }, 35367 35368 values: function (start, end) { 35369 if (isArray(start)) { 35370 return this._value(start[0], start[1]); 35371 } else { 35372 return this._value(start, end); 35373 } 35374 }, 35375 35376 _refresh: function() { 35377 var that = this, 35378 options = that.options; 35379 35380 that.trigger(MOVE_SELECTION, { 35381 values: [options.selectionStart, options.selectionEnd], 35382 value: [options.selectionStart, options.selectionEnd] 35383 }); 35384 35385 if (options.selectionStart == options.max && options.selectionEnd == options.max) { 35386 that._setZIndex("firstHandle"); 35387 } 35388 }, 35389 35390 _setValueInRange: function (selectionStart, selectionEnd) { 35391 var options = this.options; 35392 35393 selectionStart = math.max(math.min(selectionStart, options.max), options.min); 35394 35395 selectionEnd = math.max(math.min(selectionEnd, options.max), options.min); 35396 35397 if (selectionStart == options.max && selectionEnd == options.max) { 35398 this._setZIndex("firstHandle"); 35399 } 35400 35401 this._update(math.min(selectionStart, selectionEnd), math.max(selectionStart, selectionEnd)); 35402 }, 35403 35404 _setZIndex: function (type) { 35405 this.wrapper.find(DRAG_HANDLE).each(function (index) { 35406 $(this).css("z-index", type == "firstHandle" ? 1 - index : index); 35407 }); 35408 } 35409 }); 35410 35411 RangeSlider.Selection = function (dragHandles, that, options) { 35412 function moveSelection(value) { 35413 value = value || []; 35414 var selectionStartValue = value[0] - options.min, 35415 selectionEndValue = value[1] - options.min, 35416 selectionStartIndex = math.ceil(round(selectionStartValue / options.smallStep)), 35417 selectionEndIndex = math.ceil(round(selectionEndValue / options.smallStep)), 35418 selectionStart = that._pixelSteps[selectionStartIndex], 35419 selectionEnd = that._pixelSteps[selectionEndIndex], 35420 halfHandle = parseInt(dragHandles.eq(0)[that._outerSize]() / 2, 10); 35421 35422 dragHandles.eq(0).css(that._position, selectionStart - halfHandle) 35423 .end() 35424 .eq(1).css(that._position, selectionEnd - halfHandle); 35425 35426 makeSelection(selectionStart, selectionEnd); 35427 } 35428 35429 function makeSelection(selectionStart, selectionEnd) { 35430 var selection = 0, 35431 selectionPosition = 0, 35432 selectionDiv = that._trackDiv.find(".k-slider-selection"); 35433 35434 selection = math.abs(selectionStart - selectionEnd); 35435 selectionPosition = selectionStart < selectionEnd ? selectionStart : selectionEnd; 35436 35437 selectionDiv[that._size](selection); 35438 selectionDiv.css(that._position, selectionPosition - 1); 35439 } 35440 35441 moveSelection(that.value()); 35442 35443 that.bind([ CHANGE, SLIDE, MOVE_SELECTION ], function (e) { 35444 moveSelection(e.values); 35445 }); 35446 }; 35447
35448 kendo.ui.plugin(RangeSlider); 35449 35450})(jQuery); 35451/** 35452 * @fileOverview Provides a Splitter implementation which can be used to display a dynamic layout of resizable and 35453 * collapsible panes. 35454 */ 35455 35456(function ($, undefined) { 35457 /** 35458 * @name kendo.ui.Splitter.Description 35459 * 35460 * @section 35461 * <p> 35462 * The <strong>Splitter</strong> provides a dynamic layout of resizable and collapsible panes. It converts the 35463 * children of an HTML element in to the interactive layout, adding resize and collapse handles based on 35464 * configuration. A <strong>Splitter</strong> can be mixed in a vertical or horizontal orientation to build 35465 * complex layouts. 35466 * </p> 35467 * <h3>Getting Started</h3> 35468 * <p> 35469 * The layout and structure of a <strong>Splitter</strong> is defined within the DOM as a div with child elements. 35470 * </p> 35471 * 35472 * @exampleTitle Create a div with children that will become panes 35473 * @example 35474 * <div id="splitter"> 35475 * <div>Area 1</div> 35476 * <div>Area 2</div> 35477 * </div> 35478 * 35479 * @section 35480 * <p> 35481 * Initialization of a <strong>Splitter</strong> should occur after the DOM is fully loaded. It is recommended 35482 * that initialization the <strong>Splitter</strong> occur within a handler is provided to $(document).ready(). 35483 * </p> 35484 * 35485 * @exampleTitle Initialize the Splitter using a selector within $(document).ready() 35486 * @example 35487 * $(document).ready(function() { 35488 * $("#splitter").kendoSplitter(); 35489 * }); 35490 * 35491 * @section 35492 * <p> 35493 * When the <strong>Splitter</strong> is initialized, a vertical split bar will be placed between the two div 35494 * elements. This bar can be moved by a user left and right to adjust the size on the panes. 35495 * </p> 35496 * <h3>Configuring Splitter Behaviors</h3> 35497 * <p> 35498 * The <strong>Splitter</strong> has a default configuration specified during initialization. However, these 35499 * options may be overriden to control the following properties: 35500 * </p> 35501 * <ul> 35502 * <li>Maximum and/or minimum pane sizes</li> 35503 * <li>Resizable and collapsible/expandable pane behaviors</li> 35504 * <li>Orientation (horizontal or vertical)</li> 35505 * </ul> 35506 * <p> 35507 * The properties of a pane must be set during initialization and set for each individual pane in a 35508 * <strong>Splitter</strong>. 35509 * </p> 35510 * 35511 * @exampleTitle Initialize a Splitter and the properties of its panes 35512 * @example 35513 * $("#splitter").kendoSplitter({ 35514 * panes: [ 35515 * { collapsible: true, min: "100px", max: "300px" }, 35516 * { collapsible: true } 35517 * ], 35518 * orientation: "vertical" 35519 * }); 35520 * 35521 * @section 35522 * <h3>Nested Splitter Layouts</h3> 35523 * <p>To achieve complex layouts, the <strong>Splitter</strong> supports nested layouts.</p> 35524 * 35525 * @exampleTitle Creating nested Splitter layout 35526 * @example 35527 * <div id="horizontalSplitter"> 35528 * <div><p>Left Side Pane Content</p></div> 35529 * <div> 35530 * <div id="verticalSplitter"> 35531 * <div><p>Right Side, Top Pane Content</p></div> 35532 * <div><p>Right Side, Bottom Pane Content</p></div> 35533 * </div> 35534 * </div> 35535 * </div> 35536 * 35537 * @exampleTitle Initialize two Splitters with differing orientations 35538 * @example 35539 * $("horizontalSplitter").kendoSplitter(); 35540 * $("verticalSplitter").kendoSplitter({ orientation: "vertical" }); 35541 * 35542 * @section 35543 * <h3>Loading Content with AJAX</h3> 35544 * <p> 35545 * While any valid technique for loading content via AJAX may be used, <strong>Splitter</strong> provides built-in 35546 * support for asynchronously loading content from URLs. These URLs should return HTML fragments that can be 35547 * loaded in the pane of a <strong>Splitter</strong>. If you want to load a whole page in an IFRAME, you may do so 35548 * by specifying the complete URL (i.e. http://kendoui.com/). 35549 * </p> 35550 * 35551 * @exampleTitle Loading Splitter content asynchronously 35552 * @example 35553 * <div id="splitter"> 35554 * <div>Area 1 with Static Content</div> 35555 * <div></div> 35556 * <div></div> 35557 * </div> 35558 * 35559 * @exampleTitle Initialize Splitter; configure async loading for one pane; and an iframe for a third pane 35560 * @example 35561 * $(document).ready(function() { 35562 * $("#splitter").kendoSplitter({ 35563 * panes: [ 35564 * {}, 35565 * { contentUrl: "html-content-snippet.html" }, 35566 * { contentUrl: "http://kendoui.com/" } 35567 * ] 35568 * }); 35569 * }); 35570 * 35571 * @section 35572 * <h3>Accessing an Existing Splitter</h3> 35573 * <p> 35574 * You can reference an existing <strong>Splitter</strong> instance via 35575 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Once a reference has been established, you can 35576 * use the API to control its behavior. 35577 * </p> 35578 * 35579 * @exampleTitle Accessing an existing Splitter instance 35580 * @example 35581 * var splitter = $("#splitter").data("kendoSplitter"); 35582 * 35583 */ 35584 var kendo = window.kendo, 35585 ui = kendo.ui, 35586 extend = $.extend, 35587 proxy = $.proxy, 35588 Widget = ui.Widget, 35589 pxUnitsRegex = /^\d+(\.\d+)?px$/i, 35590 percentageUnitsRegex = /^\d+(\.\d+)?%$/i, 35591 EXPAND = "expand", 35592 COLLAPSE = "collapse", 35593 CONTENTLOAD = "contentLoad", 35594 RESIZE = "resize", 35595 LAYOUTCHANGE = "layoutChange", 35596 HORIZONTAL = "horizontal", 35597 VERTICAL = "vertical", 35598 MOUSEENTER = "mouseenter", 35599 CLICK = "click", 35600 PANE = "pane", 35601 MOUSELEAVE = "mouseleave", 35602 KPANE = "k-" + PANE, 35603 PANECLASS = "." + KPANE; 35604 35605 function isPercentageSize(size) { 35606 return percentageUnitsRegex.test(size); 35607 } 35608 35609 function isPixelSize(size) { 35610 return pxUnitsRegex.test(size); 35611 } 35612 35613 function isFluid(size) { 35614 return !isPercentageSize(size) && !isPixelSize(size); 35615 } 35616 35617 function panePropertyAccessor(propertyName, triggersResize) { 35618 return function(pane, value) { 35619 var paneConfig = $(pane).data(PANE); 35620 35621 if (arguments.length == 1) { 35622 return paneConfig[propertyName]; 35623 } 35624 35625 paneConfig[propertyName] = value; 35626 35627 if (triggersResize) { 35628 var splitter = this.element.data("kendoSplitter"); 35629 splitter.trigger(RESIZE); 35630 } 35631 }; 35632 } 35633 35634 var Splitter = Widget.extend(/** @lends kendo.ui.Splitter.prototype */ { 35635 /** 35636 * Creates a Splitter instance. 35637 * 35638 * @constructs 35639 * @extends kendo.ui.Widget 35640 * 35641 * @param {DomElement} element DOM element 35642 * @param {Object} options Configuration options. 35643 * 35644 * @option {String} [orientation] <horizontal> 35645 * Specifies the orientation of the <strong>Splitter</strong>. 35646 * <div class="details-list"> 35647 * <dl> 35648 * <dt>"horizontal"</dt>
35649 * <dd>Define horizontal orientation of the splitter.</dd> 35650 * <dt>"vertical"</dt> 35651 * <dd>Define vertical orientation of the splitter.</dd> 35652 * </dl> 35653 * </div> 35654 * 35655 * @option {Array} [panes] 35656 * An array of pane definitions. 35657 * 35658 * _example 35659 * $("#splitter").kendoSplitter({ 35660 * panes: [ 35661 * { size: "200px", min: "100px", max: "300px" }, 35662 * { size: "20%", resizable: false }, 35663 * { collapsed: true, collapsible: true } 35664 * ] 35665 * }); 35666 * 35667 * @option {String} [panes.size] 35668 * Specifies the size of a pane defined as pixels (i.e. "200px") or as a percentage (i.e. "50%"). Note: This 35669 * value must not exceed <strong>panes.max</strong> or be less then <strong>panes.min</strong>. 35670 * 35671 * @option {String} [panes.min] 35672 * Specifies the minimum size of a pane defined as pixels (i.e. "200px") or as a percentage (i.e. "50%"). The 35673 * size of a resized pane cannot be less than the defined minimum size. 35674 * 35675 * @option {String} [panes.max] 35676 * Specifies the maximum size of a pane defined as pixels (i.e. "200px") or as a percentage (i.e. "50%"). The 35677 * size of a resized pane cannot exceed the defined maximum size. 35678 * 35679 * @option {Boolean} [panes.collapsed] <false> 35680 * Specifies whether a pane is initially collapsed (<strong>true</strong>) or expanded (<strong>true</strong>). 35681 * 35682 * @option {Boolean} [panes.collapsible] <false> 35683 * Specifies whether a pane is collapsible (<strong>true</strong>) or not collapsible (<strong>false</strong>). 35684 * 35685 * @option {Boolean} [panes.scrollable] <true> 35686 * Specifies whether a pane is scrollable (<strong>true</strong>) or not scrollable (<strong>false</strong>). 35687 * 35688 * @option {Boolean} [panes.resizable] <true> 35689 * Specifies whether a pane is resizable (<strong>true</strong>) or not resizable (<strong>false</strong>). 35690 * 35691 * @option {Boolean} [panes.contentUrl] <true> 35692 * Specifies the URL from which to load the content of a pane. 35693 * 35694 */ 35695 init: function(element, options) { 35696 var that = this, 35697 triggerResize = function() { 35698 that.trigger(RESIZE); 35699 }; 35700 35701 Widget.fn.init.call(that, element, options); 35702 35703 that.wrapper = that.element; 35704 35705 that.orientation = that.options.orientation.toLowerCase() != VERTICAL ? HORIZONTAL : VERTICAL; 35706 35707 that.bind(RESIZE, proxy(that._resize, that)); 35708 35709 that._initPanes(); 35710 35711 that._attachEvents(); 35712 35713 $(window).resize(triggerResize); 35714 35715 that.resizing = new PaneResizing(that); 35716 }, 35717 events: [ 35718 /** 35719 * Triggered when a pane of a Splitter is expanded. 35720 * 35721 * @name kendo.ui.Splitter#expand 35722 * @event 35723 * 35724 * @param {Event} e 35725 * 35726 * @param {Element} e.pane 35727 * The expanding pane of the Splitter. 35728 * 35729 * @exampleTitle Attach expand event handler during initialization; detach via unbind() 35730 * @example 35731 * // event handler for expand 35732 * var onExpand = function(e) { 35733 * // access the expanded item via e.pane (HTMLElement) 35734 * }; 35735 * 35736 * // attach expand event handler during initialization 35737 * var splitter = $("#splitter").kendoSplitter({ 35738 * expand: onExpand 35739 * }); 35740 * 35741 * // detach expand event handler via unbind() 35742 * splitter.data("kendoSplitter").unbind("expand", onExpand); 35743 * 35744 * @exampleTitle Attach expand event handler via bind(); detach via unbind() 35745 * @example 35746 * // event handler for expand 35747 * var onExpand = function(e) {
35748 * // access the expanded item via e.pane (HTMLElement) 35749 * }; 35750 * 35751 * // attach expand event handler via bind() 35752 * $("#splitter").data("kendoSplitter").bind("expand", onExpand); 35753 * 35754 * // detach expand event handler via unbind() 35755 * $("#splitter").data("kendoSplitter").unbind("expand", onExpand); 35756 * 35757 */ 35758 EXPAND, 35759 35760 /** 35761 * Triggered when a pane of a Splitter is collapsed. 35762 * 35763 * @name kendo.ui.Splitter#collapse 35764 * @event 35765 * 35766 * @param {Event} e 35767 * @param {Element} e.pane 35768 * The collapsing pane of the Splitter. 35769 * 35770 * @exampleTitle Attach expand event handler during initialization; detach via unbind() 35771 * @example 35772 * // event handler for expand 35773 * var onCollapse = function(e) { 35774 * // access the collapsed item via e.pane (HTMLElement) 35775 * }; 35776 * 35777 * // attach collapse event handler during initialization 35778 * var splitter = $("#splitter").kendoSplitter({ 35779 * collapse: onCollapse 35780 * }); 35781 * 35782 * // detach collapse event handler via unbind() 35783 * splitter.data("kendoSplitter").unbind("collapse", onCollapse); 35784 * 35785 * @exampleTitle Attach collapse event handler via bind(); detach via unbind() 35786 * @example 35787 * // event handler for collapse 35788 * var onExpand = function(e) { 35789 * // access the collapsed item via e.pane (HTMLElement) 35790 * }; 35791 * 35792 * // attach collapse event handler via bind() 35793 * $("#splitter").data("kendoSplitter").bind("collapse", onCollapse); 35794 * 35795 * // detach collapse event handler via unbind() 35796 * $("#splitter").data("kendoSplitter").unbind("collapse", onCollapse); 35797 * 35798 */ 35799 COLLAPSE, 35800 35801 /** 35802 * Triggered when the content for a pane has finished loading. 35803 * 35804 * @name kendo.ui.Splitter#contentLoad 35805 * @event 35806 * 35807 * @param {Event} e 35808 * 35809 * @param {HTMLElement} e.pane 35810 * The pane whose content has been loaded. 35811 * 35812 * @exampleTitle Attach contentLoad event handler during initialization; detach via unbind() 35813 * @example 35814 * // event handler for contentLoad 35815 * var onContentLoad = function(e) { 35816 * // access the loaded pane via e.pane (HTMLElement) 35817 * }; 35818 * 35819 * // attach contentLoad event handler during initialization 35820 * var splitter = $("#splitter").kendoSplitter({ 35821 * contentLoad: onContentLoad 35822 * }); 35823 * 35824 * // detach contentLoad event handler via unbind() 35825 * splitter.data("kendoSplitter").unbind("contentLoad", onContentLoad); 35826 * 35827 * @exampleTitle Attach contentLoad event handler via bind(); detach via unbind() 35828 * @example 35829 * // event handler for contentLoad 35830 * var onContentLoad = function(e) { 35831 * // access the loaded pane via e.pane (HTMLElement) 35832 * }; 35833 * 35834 * // attach contentLoad event handler via bind() 35835 * $("#splitter").data("kendoSplitter").bind("contentLoad", onContentLoad); 35836 * 35837 * // detach contentLoad event handler via unbind() 35838 * $("#splitter").data("kendoSplitter").unbind("contentLoad", onContentLoad); 35839 * 35840 */ 35841 CONTENTLOAD, 35842 35843 /** 35844 * Triggered when a pane is resized. 35845 * 35846 * @name kendo.ui.Splitter#resize 35847 * @event 35848 * @param {Event} e 35849 * 35850 * @exampleTitle Attach resize event handler during initialization; detach via unbind() 35851 * @example 35852 * // event handler for resize 35853 * var onResize = function(e) { 35854 * // ... 35855 * }; 35856 * 35857 * // attach resize event handler during initialization 35858 * var splitter = $("#splitter").kendoSplitter({ 35859 * resize: onResize 35860 * }); 35861 * 35862 * // detach resize event handler via unbind() 35863 * splitter.data("kendoSplitter").unbind("resize", onResize); 35864 35865 * @exampleTitle Attach resize event handler via bind(); detach via unbind() 35866 * @example 35867 * // event handler for resize 35868 * var onResize = function(e) { 35869 * // ... 35870 * }; 35871 * 35872 * // attach resize event handler via bind() 35873 * $("#splitter").data("kendoSplitter").bind("resize", onResize); 35874 * 35875 * // detach resize event handler via unbind() 35876 * $("#splitter").data("kendoSplitter").unbind("resize", onResize); 35877 * 35878 */ 35879 RESIZE, 35880 35881 /** 35882 * Fires when the splitter layout has changed 35883 * 35884 * @name kendo.ui.Splitter#layoutChange 35885 * @event 35886 * 35887 * @exampleTitle Attach layoutChange event handler during initialization; detach via unbind() 35888 * @example 35889 * // event handler for resize 35890 * var onLayoutChange = function(e) { 35891 * // ... 35892 * }; 35893 * 35894 * $("#splitter").kendoSplitter({ 35895 * layoutChange: onLayoutChange 35896 * }); 35897 * 35898 * @exampleTitle Attach layoutChange event handler via bind(); detach via unbind() 35899 * @example 35900 * // event handler for layoutChange 35901 * var onLayoutChange = function(e) { 35902 * // ... 35903 * }; 35904 * 35905 * // attach layoutChange event handler via bind() 35906 * $("#splitter").bind("layoutChange", onLayoutChange); 35907 * 35908 * // detach layoutChange event handler via unbind() 35909 * $("#splitter").unbind("layoutChange", onLayoutChange); 35910 * 35911 */ 35912 LAYOUTCHANGE 35913 ], 35914 35915 _attachEvents: function() { 35916 var that = this, 35917 orientation = that.options.orientation, 35918 splitbarSelector = ".k-splitbar-draggable-" + orientation, 35919 expandCollapseSelector = ".k-splitbar .k-icon:not(.k-resize-handle)", 35920 triggerResize = function() { 35921 that.trigger(RESIZE); 35922 }; 35923 35924 that.element 35925 .delegate(splitbarSelector, MOUSEENTER, function() { $(this).addClass("k-splitbar-" + that.orientation + "-hover"); }) 35926 .delegate(splitbarSelector, MOUSELEAVE, function() { $(this).removeClass("k-splitbar-" + that.orientation + "-hover"); }) 35927 .delegate(splitbarSelector, "mousedown", function() { that._contentFrames(this).after("<div class='k-overlay' />"); }) 35928 .delegate(splitbarSelector, "mouseup", function() { that._contentFrames(this).next(".k-overlay").remove(); }) 35929 .delegate(expandCollapseSelector, MOUSEENTER, function() { $(this).addClass("k-state-hover"); }) 35930 .delegate(expandCollapseSelector, MOUSELEAVE, function() { $(this).removeClass('k-state-hover'); }) 35931 .delegate(".k-splitbar .k-collapse-next, .k-splitbar .k-collapse-prev", CLICK, that._arrowClick(COLLAPSE)) 35932 .delegate(".k-splitbar .k-expand-next, .k-splitbar .k-expand-prev", CLICK, that._arrowClick(EXPAND)) 35933 .delegate(".k-splitbar", "dblclick", proxy(that._dbclick, that)) 35934 .parent().closest(".k-splitter").each(function() { 35935 $(this).data("kendoSplitter").bind(RESIZE, triggerResize); 35936 }); 35937 }, 35938 35939 options: { 35940 name: "Splitter", 35941 orientation: HORIZONTAL 35942 }, 35943 35944 _initPanes: function() { 35945 var that = this, 35946 panesConfig = that.options.panes || []; 35947 35948 that.element 35949 .addClass("k-widget").addClass("k-splitter") 35950 .children() 35951 .addClass(KPANE) 35952 .each(function (index, pane) { 35953 var config = panesConfig && panesConfig[index]; 35954 35955 pane = $(pane); 35956 35957 pane.data(PANE, config ? config : {}) 35958 .toggleClass("k-scrollable", config ? config.scrollable !== false : true); 35959 that.ajaxRequest(pane); 35960 }) 35961 .end(); 35962 that.trigger(RESIZE); 35963 }, 35964 35965 /** 35966 * Loads the content of a pane from a local or remote URL. 35967 * 35968 * @param {Selector | DOM Element} pane 35969 * The targetted pane whose content is to be loaded via a URL. 35970 * 35971 * @param {String} url 35972 * A local or remote URL from which the content of the pane is to be loaded. 35973 * 35974 * @param {Object | String} data 35975 * Any data that is necessary to be sent to the server.
35976 * 35977 * @example 35978 * // get a reference to the splitter 35979 * var splitter = $("#splitter").data("kendoSplitter"); 35980 * // load content into the pane with ID, pane1 35981 * splitter.ajaxRequest("#pane1", "/customer/profile", { id: 42 }); 35982 * 35983 */ 35984 ajaxRequest: function(pane, url, data) { 35985 pane = $(pane); 35986 35987 var that = this, 35988 paneConfig = pane.data(PANE); 35989 35990 url = url || paneConfig.contentUrl; 35991 35992 if (url) { 35993 pane.append("<span class='k-icon k-loading k-pane-loading' />"); 35994 35995 if (kendo.isLocalUrl(url)) { 35996 $.ajax({ 35997 url: url, 35998 data: data || {}, 35999 type: "GET", 36000 dataType: "html", 36001 success: function (data) { 36002 pane.html(data); 36003 36004 that.trigger(CONTENTLOAD, { pane: pane[0] }); 36005 } 36006 }); 36007 } else { 36008 pane.removeClass("k-scrollable") 36009 .html("<iframe src='" + url + "' frameborder='0' class='k-content-frame'>" + 36010 "This page requires frames in order to show content" + 36011 "</iframe>"); 36012 } 36013 } 36014 }, 36015 _triggerAction: function(type, pane) { 36016 if (!this.trigger(type, { pane: pane[0] })) { 36017 this[type](pane[0]); 36018 } 36019 }, 36020 _dbclick: function(e) { 36021 var that = this, 36022 target = $(e.target), 36023 arrow; 36024 36025 if (target.closest(".k-splitter")[0] != that.element[0]) { 36026 return; 36027 } 36028 36029 arrow = target.children(".k-icon:not(.k-resize-handle)"); 36030 36031 if (arrow.length !== 1) { 36032 return; 36033 } 36034 36035 if (arrow.is(".k-collapse-prev")) { 36036 that._triggerAction(COLLAPSE, target.prev()); 36037 } else if (arrow.is(".k-collapse-next")) { 36038 that._triggerAction(COLLAPSE, target.next()); 36039 } else if (arrow.is(".k-expand-prev")) { 36040 that._triggerAction(EXPAND, target.prev()); 36041 } else if (arrow.is(".k-expand-next")) { 36042 that._triggerAction(EXPAND, target.next()); 36043 } 36044 }, 36045 _arrowClick: function (arrowType) { 36046 var that = this; 36047 36048 return function(e) { 36049 var target = $(e.target), 36050 pane; 36051 36052 if (target.closest(".k-splitter")[0] != that.element[0]) { 36053 return; 36054 } 36055 36056 if (target.is(".k-" + arrowType + "-prev")) { 36057 pane = target.parent().prev(); 36058 } else { 36059 pane = target.parent().next(); 36060 } 36061 that._triggerAction(arrowType, pane); 36062 }; 36063 }, 36064 _updateSplitBar: function(splitbar, previousPane, nextPane) { 36065 var catIconIf = function(iconType, condition) { 36066 return condition ? "<div class='k-icon " + iconType + "' />" : ""; 36067 }, 36068 orientation = this.orientation, 36069 draggable = (previousPane.resizable !== false) && (nextPane.resizable !== false), 36070 prevCollapsible = previousPane.collapsible, 36071 prevCollapsed = previousPane.collapsed, 36072 nextCollapsible = nextPane.collapsible, 36073 nextCollapsed = nextPane.collapsed; 36074 36075 splitbar.addClass("k-splitbar k-state-default k-splitbar-" + orientation) 36076 .removeClass("k-splitbar-" + orientation + "-hover") 36077 .toggleClass("k-splitbar-draggable-" + orientation, 36078 draggable && !prevCollapsed && !nextCollapsed) 36079 .toggleClass("k-splitbar-static-" + orientation, 36080 !draggable && !prevCollapsible && !nextCollapsible) 36081 .html( 36082 catIconIf("k-collapse-prev", prevCollapsible && !prevCollapsed && !nextCollapsed) + 36083 catIconIf("k-expand-prev", prevCollapsible && prevCollapsed && !nextCollapsed) + 36084 catIconIf("k-resize-handle", draggable) + 36085 catIconIf("k-collapse-next", nextCollapsible && !nextCollapsed && !prevCollapsed) + 36086 catIconIf("k-expand-next", nextCollapsible && nextCollapsed && !prevCollapsed) 36087 ); 36088 }, 36089 _updateSplitBars: function() { 36090 var that = this; 36091 36092 this.element.children(".k-splitbar").each(function() { 36093 var splitbar = $(this), 36094 previousPane = splitbar.prev(PANECLASS).data(PANE), 36095 nextPane = splitbar.next(PANECLASS).data(PANE); 36096 36097 if (!nextPane) { 36098 return; 36099 } 36100 36101 that._updateSplitBar(splitbar, previousPane, nextPane); 36102 }); 36103 }, 36104 _contentFrames: function(splitbar) { 36105 return $(splitbar).siblings(PANECLASS).find("> .k-content-frame"); 36106 }, 36107 _resize: function() { 36108 var that = this, 36109 element = that.element, 36110 panes = element.children(":not(.k-splitbar)"), 36111 isHorizontal = that.orientation == HORIZONTAL, 36112 splitBars = element.children(".k-splitbar"), 36113 splitBarsCount = splitBars.length, 36114 sizingProperty = isHorizontal ? "width" : "height", 36115 totalSize = element[sizingProperty](); 36116 36117 if (splitBarsCount === 0) { 36118 splitBarsCount = panes.length - 1; 36119 panes.slice(0, splitBarsCount).after("<div class='k-splitbar' />"); 36120 that._updateSplitBars(); 36121 splitBars = element.children(".k-splitbar"); 36122 } else { 36123 that._updateSplitBars(); 36124 } 36125 36126 // discard splitbar sizes from total size 36127 splitBars.each(function() { 36128 totalSize -= this[isHorizontal ? "offsetWidth" : "offsetHeight"]; 36129 }); 36130 36131 var sizedPanesWidth = 0, 36132 sizedPanesCount = 0, 36133 freeSizedPanes = $(); 36134 36135 panes.css({ position: "absolute", top: 0 }) 36136 [sizingProperty](function() { 36137 var config = $(this).data(PANE) || {}, size; 36138 36139 if (config.collapsed) { 36140 size = 0; 36141 } else if (isFluid(config.size)) { 36142 freeSizedPanes = freeSizedPanes.add(this); 36143 return; 36144 } else { // sized in px/%, not collapsed 36145 size = parseInt(config.size, 10); 36146 36147 if (isPercentageSize(config.size)) { 36148 size = Math.floor(size * totalSize / 100); 36149 } 36150 } 36151 36152 sizedPanesCount++; 36153 sizedPanesWidth += size; 36154 36155 return size; 36156 }); 36157 36158 totalSize -= sizedPanesWidth; 36159 36160 var freeSizePanesCount = freeSizedPanes.length, 36161 freeSizePaneWidth = Math.floor(totalSize / freeSizePanesCount); 36162 36163 freeSizedPanes 36164 .slice(0, freeSizePanesCount - 1) 36165 .css(sizingProperty, freeSizePaneWidth) 36166 .end() 36167 .eq(freeSizePanesCount - 1) 36168 .css(sizingProperty, totalSize - (freeSizePanesCount - 1) * freeSizePaneWidth);
36169 36170 // arrange panes 36171 var sum = 0, 36172 alternateSizingProperty = isHorizontal ? "height" : "width", 36173 positioningProperty = isHorizontal ? "left" : "top", 36174 sizingDomProperty = isHorizontal ? "offsetWidth" : "offsetHeight"; 36175 36176 if (freeSizePanesCount === 0) { 36177 var lastNonCollapsedPane = panes.filter(function() { 36178 return !(($(this).data(PANE) || {}).collapsed); 36179 }).last(); 36180 36181 lastNonCollapsedPane[sizingProperty](totalSize + lastNonCollapsedPane[0][sizingDomProperty]); 36182 } 36183 36184 element.children() 36185 .css(alternateSizingProperty, element[alternateSizingProperty]()) 36186 .each(function (i, child) { 36187 child.style[positioningProperty] = Math.floor(sum) + "px"; 36188 sum += child[sizingDomProperty]; 36189 }); 36190 36191 that.trigger(LAYOUTCHANGE); 36192 }, 36193 36194 /** 36195 * Toggles the state of a specified pane (i.e. collapsed or expanded). Invoking this method will force the 36196 * <strong>Splitter</strong> to redraw and it will trigger layoutChange and resize events. Note: Invoking the 36197 * method will not trigger collapse or expand events. 36198 * 36199 * @param {Selector | DOM Element} pane 36200 * The pane to be collapsed. 36201 * 36202 * @param {Boolean} expand (Optional) 36203 * Represents the desired state of the specified pane; to be expanded (<strong>true</strong>) or collapsed 36204 * (<strong>false</strong>). If undefined, toggle() will collapse the pane if it is expanded or will expand the 36205 * pane if it is collapsed. 36206 * 36207 * @example 36208 * // get a reference to the splitter 36209 * var splitter = $("#splitter").data("kendoSplitter"); 36210 * // toggle the state of the pane with ID, pane1 36211 * splitter.toggle("#pane1"); 36212 * // toggle the state of the pane with ID, pane1 to be expanded 36213 * splitter.toggle("#pane1", true); 36214 * // toggle the state of the pane with ID, pane1 to be collapsed 36215 * splitter.toggle("#pane1", false); 36216 * 36217 */ 36218 toggle: function(pane, expand) { 36219 var paneConfig; 36220 36221 pane = $(pane); 36222 paneConfig = pane.data(PANE); 36223 36224 if (arguments.length == 1) { 36225 expand = paneConfig.collapsed === undefined ? false : paneConfig.collapsed; 36226 } 36227 36228 paneConfig.collapsed = !expand; 36229 36230 this.trigger(RESIZE); 36231 }, 36232 36233 /** 36234 * Collapses a specified pane. Invoking this method will force the <strong>Splitter</strong> to redraw and it 36235 * will trigger layoutChange and resize events. Note: Invoking the method will not trigger a collapse event. 36236 * 36237 * @param {Selector | DOM Element} pane 36238 * The pane to be collapsed. 36239 * 36240 * @example 36241 * // get a reference to the splitter 36242 * var splitter = $("#splitter").data("kendoSplitter"); 36243 * // collapse the pane with ID, pane1 36244 * splitter.collapse("#pane1"); 36245 * 36246 */ 36247 collapse: function(pane) { 36248 this.toggle(pane, false); 36249 }, 36250 36251 /** 36252 * Expands a specified pane. Invoking this method will force the <strong>Splitter</strong> to redraw and it 36253 * will trigger layoutChange and resize events. Note: Invoking the method will not trigger an expand event. 36254 * 36255 * @param {Selector | DOM Element} pane 36256 * The pane to be expanded. 36257 * 36258 * @example 36259 * // get a reference to the splitter 36260 * var splitter = $("#splitter").data("kendoSplitter"); 36261 * // expand the pane with ID, pane1 36262 * splitter.expand("#pane1"); 36263 * 36264 */ 36265 expand: function(pane) { 36266 this.toggle(pane, true); 36267 }, 36268 36269 /** 36270 * Set the size of the pane. Setting this value will cause the <strong>Splitter</strong> to redraw and it will 36271 * trigger layoutChange and resize events. 36272 * 36273 * @name kendo.ui.Splitter#size 36274 * @function 36275 * 36276 * @param {Selector | DOM Element} pane 36277 * The pane to be resized. 36278 * 36279 * @param {String} value 36280 * The new size of the pane defined as pixels (i.e. "200px") or as a percentage (i.e. "50%"). Note: This value 36281 * must not exceed <strong>panes.max</strong> or be less then <strong>panes.min</strong>. 36282 * 36283 * @example 36284 * // get a reference to the splitter 36285 * var splitter = $("#splitter").data("kendoSplitter"); 36286 * // set the size of the pane with ID, pane1 36287 * splitter.size("#pane1", "200px"); 36288 * 36289 */ 36290 size: panePropertyAccessor("size", true), 36291 36292 /** 36293 * Sets the minimum size of a pane. Setting this value will not cause the <strong>Splitter</strong> to 36294 * redraw, nor will it trigger any events. 36295 * 36296 * @name kendo.ui.Splitter#min 36297 * @function 36298 * 36299 * @param {Selector | DOM Element} pane 36300 * The pane being targetted for a new minimum size configuration value. 36301 * 36302 * @param {String} value 36303 * The minimum size value of the pane defined as pixels (i.e. "200px") or as a percentage (i.e. "50%"). 36304 * 36305 * @example 36306 * // get a reference to the splitter 36307 * var splitter = $("#splitter").data("kendoSplitter"); 36308 * // set the minimum size of the pane with ID, pane1 36309 * splitter.min("#pane1", "100px"); 36310 * 36311 */ 36312 min: panePropertyAccessor("min"), 36313 36314 /** 36315 * Sets the maximum size of a pane. Setting this value will not cause the <strong>Splitter</strong> to 36316 * redraw, nor will it trigger any events. 36317 * 36318 * @name kendo.ui.Splitter#max 36319 * @function 36320 * 36321 * @param {Selector | DOM Element} pane 36322 * The pane being targetted for a new minimum size configuration value. 36323 * 36324 * @param {String} value 36325 * The maximum size value of the pane defined as pixels (i.e. "200px") or as a percentage (i.e. "50%"). 36326 * 36327 * @example 36328 * // get a reference to the splitter 36329 * var splitter = $("#splitter").data("kendoSplitter"); 36330 * // set the maximum size of the pane with ID, pane1 36331 * splitter.max("#pane1", "300px"); 36332 * 36333 */ 36334 max: panePropertyAccessor("max") 36335 });
36336 36337 ui.plugin(Splitter); 36338 36339 var verticalDefaults = { 36340 sizingProperty: "height", 36341 sizingDomProperty: "offsetHeight", 36342 alternateSizingProperty: "width", 36343 positioningProperty: "top", 36344 mousePositioningProperty: "pageY" 36345 }; 36346 36347 var horizontalDefaults = { 36348 sizingProperty: "width", 36349 sizingDomProperty: "offsetWidth", 36350 alternateSizingProperty: "height", 36351 positioningProperty: "left", 36352 mousePositioningProperty: "pageX" 36353 }; 36354 36355 function PaneResizing(splitter) { 36356 var that = this, 36357 orientation = splitter.orientation; 36358 36359 that.owner = splitter; 36360 that._element = splitter.element; 36361 that.orientation = orientation; 36362 36363 extend(that, orientation === HORIZONTAL ? horizontalDefaults : verticalDefaults); 36364 36365 that._resizable = new kendo.ui.Resizable(splitter.element, { 36366 orientation: orientation, 36367 handle: splitter.element.children(".k-splitbar-draggable-" + orientation), 36368 hint: proxy(that._createHint, that), 36369 start: proxy(that._start, that), 36370 max: proxy(that._max, that), 36371 min: proxy(that._min, that), 36372 invalidClass:"k-restricted-size-" + orientation, 36373 resizeend: proxy(that._stop, that) 36374 }); 36375 } 36376 36377 PaneResizing.prototype = { 36378 _createHint: function(handle) { 36379 var that = this; 36380 return $("<div class='k-ghost-splitbar k-ghost-splitbar-" + that.orientation + " k-state-default' />") 36381 .css(that.alternateSizingProperty, handle[that.alternateSizingProperty]()); 36382 }, 36383 _start: function(e) { 36384 var that = this, 36385 splitbar = $(e.currentTarget), 36386 previousPane = splitbar.prev(), 36387 nextPane = splitbar.next(), 36388 previousPaneConfig = previousPane.data(PANE), 36389 nextPaneConfig = nextPane.data(PANE), 36390 prevBoundary = parseInt(previousPane[0].style[that.positioningProperty], 10), 36391 nextBoundary = parseInt(nextPane[0].style[that.positioningProperty], 10) + nextPane[0][that.sizingDomProperty] - splitbar[0][that.sizingDomProperty], 36392 totalSize = that._element.css(that.sizingProperty), 36393 toPx = function (value) { 36394 var val = parseInt(value, 10); 36395 return (isPixelSize(value) ? val : (totalSize * val) / 100) || 0; 36396 }, 36397 prevMinSize = toPx(previousPaneConfig.min), 36398 prevMaxSize = toPx(previousPaneConfig.max) || nextBoundary - prevBoundary, 36399 nextMinSize = toPx(nextPaneConfig.min), 36400 nextMaxSize = toPx(nextPaneConfig.max) || nextBoundary - prevBoundary; 36401 36402 that.previousPane = previousPane; 36403 that.nextPane = nextPane; 36404 that._maxPosition = Math.min(nextBoundary - nextMinSize, prevBoundary + prevMaxSize); 36405 that._minPosition = Math.max(prevBoundary + prevMinSize, nextBoundary - nextMaxSize); 36406 }, 36407 _max: function(e) { 36408 return this._maxPosition; 36409 }, 36410 _min: function(e) { 36411 return this._minPosition; 36412 }, 36413 _stop: function(e) { 36414 var that = this, 36415 splitbar = $(e.currentTarget), 36416 owner = that.owner; 36417 36418 owner._contentFrames(splitbar).next(".k-overlay").remove(); 36419 36420 if (e.keyCode !== kendo.keys.ESC) { 36421 var ghostPosition = e.position, 36422 previousPane = splitbar.prev(), 36423 nextPane = splitbar.next(), 36424 previousPaneConfig = previousPane.data(PANE), 36425 nextPaneConfig = nextPane.data(PANE), 36426 previousPaneNewSize = ghostPosition - parseInt(previousPane[0].style[that.positioningProperty], 10), 36427 nextPaneNewSize = parseInt(nextPane[0].style[that.positioningProperty], 10) + nextPane[0][that.sizingDomProperty] - ghostPosition - splitbar[0][that.sizingDomProperty], 36428 fluidPanesCount = that._element.children(PANECLASS).filter(function() { return isFluid($(this).data(PANE).size); }).length; 36429 36430 if (!isFluid(previousPaneConfig.size) || fluidPanesCount > 1) { 36431 if (isFluid(previousPaneConfig.size)) { 36432 fluidPanesCount--; 36433 } 36434 36435 previousPaneConfig.size = previousPaneNewSize + "px"; 36436 } 36437 36438 if (!isFluid(nextPaneConfig.size) || fluidPanesCount > 1) { 36439 nextPaneConfig.size = nextPaneNewSize + "px"; 36440 } 36441 36442 owner.trigger(RESIZE); 36443 } 36444 36445 return false; 36446 } 36447 }; 36448 36449})(jQuery); 36450(function($, undefined) { 36451 /** 36452 * @name kendo.ui.Upload.Description 36453 * 36454 * @section 36455 * <p> 36456 * An <strong>Upload</strong> uses progressive enhancement to deliver the best possible uploading experience to 36457 * users without requiring extra developer effort. Features highlights: 36458 * </p> 36459 * <ul> 36460 * <li>Asynchronous and synchronous (on form submit) file upload</li> 36461 * <li>Multiple file selection</li> 36462 * <li>Removing uploaded files</li> 36463 * <li>Progress tracking *</li> 36464 * <li>File drag-and-drop *</li> 36465 * <li>Cancelling upload in-progress *</li> 36466 * </ul> 36467 * <p>(*) These features are automatically enabled if supported by the browser.</p> 36468 * <p>An <strong>Upload</strong> is a standards-based widget; no plug-ins required.</p> 36469 * <h3>Getting Started</h3> 36470 * 36471 * @exampleTitle 1. Create a simple HTML form and input element of type "file" 36472 * @example 36473 * <!-- Kendo will automatically set the form enctype attribute to "multi-part/form-data" --> 36474 * <form method="post" action="handler.php"> 36475 * <div> 36476 * <input name="photos[]" id="photos" type="file" />
36477 * </div> 36478 * </form> 36479 * 36480 * @exampleTitle 2. Initialize Upload with a jQuery selector 36481 * @example 36482 * $(document).ready(function() { 36483 * $("#photos").kendoUpload(); 36484 * }); 36485 * 36486 * @section 36487 * <p> 36488 * Note the array syntax in the input name; it is used to hint the upload handler to treat photos as an array. 36489 * </p> 36490 * <p> 36491 * Please consult the documentation of your specific server technology regarding the handling of uploaded files. 36492 * </p> 36493 * <h3>See Also</h3> 36494 * <p><a href="http://www.kendoui.com/documentation/ui-widgets/upload/modes.aspx">Upload Modes</a></p> 36495 * <h3>Accessing an Existing Upload</h3> 36496 * <p> 36497 * You can reference an existing <b>Upload</b> instance via 36498 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Once a reference has been established, you can 36499 * use the API to control its behavior. 36500 * </p> 36501 * 36502 * @exampleTitle Accessing an existing Upload instance 36503 * @example 36504 * var upload = $("#upload").data("kendoUpload"); 36505 * 36506 */ 36507 var kendo = window.kendo, 36508 Widget = kendo.ui.Widget, 36509 logToConsole = kendo.logToConsole, 36510 rFileExtension = /\.([^\.]+)$/, 36511 SELECT = "select", 36512 UPLOAD = "upload", 36513 SUCCESS = "success", 36514 ERROR = "error", 36515 COMPLETE = "complete", 36516 CANCEL = "cancel", 36517 LOAD = "load", 36518 REMOVE = "remove"; 36519 36520 var Upload = Widget.extend(/** @lends kendo.ui.Upload.prototype */{ 36521 /** 36522 * 36523 * Creates an Upload instance. 36524 * 36525 * @constructs 36526 * @extends kendo.ui.Widget 36527 * 36528 * @param {DomElement} element DOM element 36529 * @param {Object} options Configuration options. 36530 * 36531 * @option {Boolean} [enabled] <true> 36532 * Enables (<strong>true</strong>) or disables (<strong>false</strong>) an <strong>Upload</strong>. A disabled 36533 * <strong>Upload</strong> may be re-enabled via enable(). 36534 * 36535 * @option {Boolean} [multiple] <true> 36536 * Enables (<strong>true</strong>) or disables (<strong>false</strong>) the ability to select multiple files. 36537 * If <strong>false</strong>, users will be able to select only one file at a time. Note: This option does not 36538 * limit the total number of uploaded files in an asynchronous configuration. 36539 * 36540 * @option {Boolean} [showFileList] <true> 36541 * Enables (<strong>true</strong>) or disables (<strong>false</strong>) the ability to display a file listing 36542 * for uploading a file(s). Disabling a file listing may be useful you wish to customize the UI; use the 36543 * client-side events to build your own UI. 36544 * 36545 * @option {Object} [async] 36546 * Configures the ability to upload a file(s) in an asynchronous manner. Please refer to the 36547 * <a href="http://www.kendoui.com/documentation/ui-widgets/upload/modes.aspx#async">async mode help topic</a> 36548 * for more details. 36549 * 36550 * @option {String} [async.saveUrl] 36551 * The URL of the handler that will receive the submitted files. The handler must accept POST requests 36552 * containing one or more fields with the same name as the original input name. 36553 * 36554 * @option {String} [async.saveField] 36555 * The name of the form field submitted to the save URL. The default value is the input name. 36556 * 36557 * @option {String} [async.removeUrl] 36558 * The URL of the handler responsible for removing uploaded files (if any). The handler must accept POST 36559 * requests containing one or more "fileNames" fields specifying the files to be deleted. 36560 * 36561 * @option {String} [async.removeVerb] <"DELETE"> 36562 * The HTTP verb to be used by the remove action. 36563 * 36564 * @option {String} [async.removeField] <"fileNames"> 36565 * The name of the form field submitted to the Remove URL. 36566 * 36567 * @option {Boolean} [async.autoUpload] <"fileNames"> 36568 * The selected files will be uploaded immediately by default. You can change this behavior b
36568y setting 36569 * autoUpload to false. 36570 * 36571 * @option {Object} [localization] 36572 * Sets the strings rendered by the Upload. 36573 * 36574 * @option {String} [localization.select] 36575 * Sets the "Select..." button text. 36576 * 36577 * @option {String} [localization.cancel] 36578 * Sets the text of the cancel button text. 36579 * 36580 * @option {String} [localization.retry] 36581 * Sets the text of the retry button text. 36582 * 36583 * @option {String} [localization.remove] 36584 * Sets the text of the remove button text. 36585 * 36586 * @option {String} [localization.uploadSelectedFiles] 36587 * Sets the text of the "Upload files" button. 36588 * 36589 * @option {String} [localization.dropFilesHere] <"drop files here to upload"> 36590 * Sets the drop zone hint. 36591 * 36592 * @option {String} [localization.statusUploading] 36593 * Sets the status message for files that are being uploaded. 36594 * 36595 * @option {String} [localization.statusUploaded] 36596 * Sets the status message for uploaded files. 36597 * 36598 * @option {String} [localization.statusFailed] 36599 * Sets the status message for failed uploads. 36600 * 36601 */ 36602 init: function(element, options) { 36603 var that = this; 36604 36605 Widget.fn.init.call(that, element, options); 36606 36607 that.name = element.name; 36608 that.multiple = that.options.multiple; 36609 that.localization = that.options.localization; 36610 36611 var activeInput = that.element; 36612 that.wrapper = activeInput.closest(".k-upload"); 36613 if (that.wrapper.length == 0) { 36614 that.wrapper = that._wrapInput(activeInput); 36615 } 36616 36617 that._activeInput(activeInput); 36618 that.toggle(that.options.enabled); 36619 36620 activeInput.closest("form").bind({ 36621 "submit": $.proxy(that._onParentFormSubmit, that), 36622 "reset": $.proxy(that._onParentFormReset, that) 36623 }); 36624 36625 if (that.options.async.saveUrl != undefined) { 36626 that._module = that._supportsFormData() ? 36627 new formDataUploadModule(that) : 36628 new iframeUploadModule(that); 36629 } else { 36630 that._module = new syncUploadModule(that); 36631 } 36632 36633 if (that._supportsDrop()) { 36634 that._setupDropZone(); 36635 } 36636 36637 that.wrapper 36638 .delegate(".k-upload-action", "click", $.proxy(that._onFileAction, that)) 36639 .delegate(".k-upload-selected", "click", $.proxy(that._onUploadSelected, that)) 36640 .delegate(".k-file", "t:progress", $.proxy(that._onFileProgress, that)) 36641 .delegate(".k-file", "t:upload-success", $.proxy(that._onUploadSuccess, that)) 36642 .delegate(".k-file", "t:upload-error", $.proxy(that._onUploadError, that)); 36643 }, 36644 36645 events: [ 36646 /** 36647 * 36648 * Triggered when a file(s) is selected. Note: Cancelling this event will prevent the selection from 36649 * occurring. 36650 * 36651 * @name kendo.ui.Upload#select 36652 * @event 36653 * 36654 * @param {Event} e 36655 * 36656 * @param {Array} e.files 36657 * An array of the selected files. 36658 * 36659 * <ul> 36660 * <li>name - the name of a selected file, including its extension</li> 36661 * <li>extension - the file extension of a selected file, including the leading dot (i.e. ".jpg")</li> 36662 * <li>size - the size (in bytes) of a selected file (null, if unavailable)</li> 36663 * <li>rawFile - an in-memory representation of a selected file</li> 36664 * </ul> 36665 * 36666 * @exampleTitle Wire-up an event handler that triggered when a user selects a file(s) 36667 * @example 36668 * var onSelect = function(e) { 36669 * $.each(e.files, function(index, value) { 36670 * console.log("Name: " + value.name); 36671 * console.log("Size: " + value.size + " bytes"); 36672 * console.log("Extension: " + value.extension); 36673 * }); 36674 * }; 36675 * 36676 * // initialize and configure an Upload widget with a select event handler 36677 * $("#photos").kendoUpload({ 36678 * // ... 36679 * select: onSelect 36680 * }); 36681 * 36682 */ 36683 SELECT, 36684 36685 /** 36686 * Fires when one or more files are about to be uploaded. 36687 * Cancelling the event will prevent the upload. 36688 * <p> 36689 * Note: The upload event fires only when the upload is in 36690 * <a href="http://www.kendoui.com/documentation/ui-widgets/upload/modes.aspx#async">async mode</a>. 36691 * </p> 36692 * @name kendo.ui.Upload#upload 36693 * @event 36694 * @param {Event} e 36695 * @param {Array} e.files 36696 * List of the files that will be uploaded. Each file has: 36697 * <ul> 36698 * <li>name</li> 36699 * <li> 36700 * extension - the file extension 36701 * inlcuding the leading dot - ".jpg", ".png", etc. 36702 * </li> 36703 * <li>size - the file size in bytes (null if not available)</li> 36704 * </ul> 36705 * @param {Object} data Optional object that will be 36706 * sent to the save handler in the form of key/value pairs. 36707 * @example 36708 * $("#photos").kendoUpload({ 36709 * // ... 36710 * upload: onUpload 36711 * }); 36712 * 36713 * function onUpload(e) { 36714 * // Array with information about the uploaded files 36715 * var files = e.files; 36716 * 36717 * // Check the extension of each file and abort the upload if it is not .jpg 36718 * $.each(files, function() { 36719 * if (this.extension != ".jpg") { 36720 * alert("Only .jpg files can be uploaded") 36721 * e.preventDefault(); 36722 * } 36723 * }); 36724 * } 36725 */ 36726 UPLOAD, 36727 36728 /** 36729 * Fires when an upload / remove operation has been completed successfully. 36730 * <p> 36731 * Note: The success event fires only when the upload is in 36732 * <a href="http://www.kendoui.com/documentation/ui-widgets/upload/modes.aspx#async">async mode</a>. 36733 * </p> 36734 * @name kendo.ui.Upload#success 36735 * @event 36736 * @param {Event} e 36737 * @param {Array} e.files 36738 * List of the files that were uploaded or removed . Each file has: 36739 * <ul> 36740 * <li>name</li> 36741 * <li> 36742 * extension - the file extension 36743 * inlcuding the leading dot - ".jpg", ".png", etc. 36744 * </li> 36745 * <li>size - the file size in bytes (null if not available)</li> 36746 * </ul> 36747 * @param {String} e.operation "upload" or "remove". 36748 * @param {String} e.response the response object returned by the server.
36749 * @param {Object} e.XMLHttpRequest 36750 * This is either the original XHR used for the operation or a stub containing: 36751 * <ul> 36752 * <li>responseText</li> 36753 * <li>status</li> 36754 * <li>statusText</li> 36755 * </ul> 36756 * Verify that this is an actual XHR before accessing any other fields. 36757 * @example 36758 * $("#photos").kendoUpload({ 36759 * // ... 36760 * success: onSuccess 36761 * }); 36762 * 36763 * function onSuccess(e) { 36764 * // Array with information about the uploaded files 36765 * var files = e.files; 36766 * 36767 * if (e.operation == "upload") { 36768 * alert("Successfully uploaded " + files.length + " files"); 36769 * } 36770 * } 36771 */ 36772 SUCCESS, 36773 36774 /** 36775 * Fires when an upload / remove operation has failed. 36776 * <p> 36777 * Note: The error event fires only when the upload is in 36778 * <a href="http://www.kendoui.com/documentation/ui-widgets/upload/modes.aspx#async">async mode</a>. 36779 * </p> 36780 * @name kendo.ui.Upload#error 36781 * @event 36782 * @param {Event} e 36783 * @param {Array} e.files 36784 * List of the files that were uploaded or removed . Each file has: 36785 * <ul> 36786 * <li>name</li> 36787 * <li> 36788 * extension - the file extension 36789 * inlcuding the leading dot - ".jpg", ".png", etc. 36790 * </li> 36791 * <li>size - the file size in bytes (null if not available)</li> 36792 * </ul> 36793 * @param {String} e.operation - "upload" or "remove". 36794 * @param {Object} e.XMLHttpRequest 36795 * This is either the original XHR used for the operation or a stub containing: 36796 * <ul> 36797 * <li>responseText</li> 36798 * <li>status</li> 36799 * <li>statusText</li> 36800 * </ul> 36801 * Verify that this is an actual XHR before accessing any other fields. 36802 * @example 36803 * $("#photos").kendoUpload({ 36804 * // ... 36805 * error: onError 36806 * }); 36807 * 36808 * function onError(e) { 36809 * // Array with information about the uploaded files 36810 * var files = e.files; 36811 * 36812 * if (e.operation == "upload") { 36813 * alert("Failed to uploaded " + files.length + " files"); 36814 * } 36815 * 36816 * // Suppress the default error message 36817 * e.preventDefault(); 36818 * } 36819 */ 36820 ERROR, 36821 36822 /** 36823 * Fires when all active uploads have completed either successfully or with errors. 36824 * <p> 36825 * Note: The complete event fires only when the upload is in 36826 * <a href="http://www.kendoui.com/documentation/ui-widgets/upload/modes.aspx#async">async mode</a>. 36827 * </p> 36828 * @name kendo.ui.Upload#complete 36829 * @event 36830 * @param {Event} e 36831 * @example 36832 * $("#photos").kendoUpload({ 36833 * // ... 36834 * complete: onComplete 36835 * }); 36836 * 36837 * function onComplete(e) { 36838 * // The upload is now idle 36839 * } 36840 */ 36841 COMPLETE, 36842 36843 /** 36844 * Fires when the upload has been cancelled while in progress. 36845 * <p> 36846 * Note: The cancel event fires only when the upload is in 36847 * <a href="http://www.kendoui.com/documentation/ui-widgets/upload/modes.aspx#async">async mode</a>. 36848 * </p> 36849 * @name kendo.ui.Upload#cancel 36850 * @event 36851 * @param {Event} e 36852 * @param {Array} e.files 36853 * List of the files that were uploaded or removed . Each file has: 36854 * <ul> 36855 * <li>name</li> 36856 * <li> 36857 * extension - the file extension 36858 * inlcuding the leading dot - ".jpg", ".png", etc. 36859 * </li> 36860 * <li>size - the file size in bytes (null if not available)</li> 36861 * </ul> 36862 * @example 36863 * $("#photos").kendoUpload({ 36864 * // ... 36865 * cancel: onCancel 36866 * }); 36867 * 36868 * function onCancel(e) { 36869 * // Array with information about the uploaded files 36870 * var files = e.files; 36871 * 36872 * // Process the Cancel event 36873 * } 36874 */ 36875 CANCEL, 36876 36877 /** 36878 * Fires when an uploaded file is about to be removed. 36879 * Cancelling the event will prevent the remove. 36880 * @name kendo.ui.Upload#remove 36881 * @event 36882 * @param {Event} e 36883 * @param {Array} e.files 36884 * List of the files that were uploaded or removed . Each file has: 36885 * <ul> 36886 * <li>name</li> 36887 * <li> 36888 * extension - the file extension 36889 * inlcuding the leading dot - ".jpg", ".png", etc. 36890 * </li> 36891 * <li>size - the file size in bytes (null if not available)</li> 36892 * </ul> 36893 * @param {Object} data Optional object that will be 36894 * sent to the save handler in the form of key/value pairs. 36895 * @example 36896 * $("#photos").kendoUpload({ 36897 * // ... 36898 * remove: onRemove 36899 * }); 36900 * 36901 * function onRemove(e) { 36902 * // Array with information about the removed files 36903 * var files = e.files; 36904 * 36905 * // Process the Remove event 36906 * // Optionally cancel the remove operation by calling 36907 * // e.preventDefault() 36908 * } 36909 */ 36910 REMOVE], 36911 options: { 36912 name: "Upload", 36913 enabled: true, 36914 multiple: true, 36915 showFileList: true, 36916 async: { 36917 removeVerb: "POST", 36918 autoUpload: true 36919 }, 36920 localization: { 36921 "select": "Select...", 36922 "cancel": "Cancel", 36923 "retry": "Retry", 36924 "remove": "Remove", 36925 "uploadSelectedFiles": "Upload files", 36926 "dropFilesHere": "drop files here to upload", 36927 "statusUploading": "uploading", 36928 "statusUploaded": "uploaded", 36929 "statusFailed": "failed" 36930 } 36931 }, 36932 36933 setOptions: function(options) { 36934 var that = this, 36935 activeInput = that.element; 36936 36937 Widget.fn.setOptions.call(that, options); 36938 36939 that.multiple = that.options.multiple; 36940 36941 activeInput.attr("multiple", that._supportsMultiple() ? that.multiple : false) 36942 that.toggle(that.options.enabled); 36943 }, 36944 36945 /** 36946 * Enables the upload. 36947 * @example 36948 * var upload = $("#upload").data("kendoUpload"); 36949 * 36950 * // enables the upload 36951 * upload.enable(); 36952 */ 36953 enable: function(enable) {
36954 enable = typeof (enable) === "undefined" ? true : enable; 36955 this.toggle(enable); 36956 }, 36957 36958 /** 36959 * Disables the upload. 36960 * @example 36961 * var upload = $("#upload").data("kendoUpload"); 36962 * 36963 * // disables the upload 36964 * upload.enable(); 36965 */ 36966 disable: function() { 36967 this.toggle(false); 36968 }, 36969 36970 /** 36971 * Toggles the upload enabled state. 36972 * @param {Boolean} enable (Optional) The new enabled state. 36973 * @example 36974 * var upload = $("#upload").data("kendoUpload"); 36975 * 36976 * // toggles the upload enabled state 36977 * upload.toggle(); 36978 */ 36979 toggle: function(enable) { 36980 enable = typeof (enable) === "undefined" ? enable : !enable; 36981 this.wrapper.toggleClass("k-state-disabled", enable); 36982 }, 36983 36984 _addInput: function(input) { 36985 var that = this; 36986 36987 input 36988 .insertAfter(that.element) 36989 .data("kendoUpload", that); 36990 36991 $(that.element) 36992 .hide() 36993 .removeAttr("id"); 36994 36995 that._activeInput(input); 36996 }, 36997 36998 _activeInput: function(input) { 36999 var that = this, 37000 wrapper = that.wrapper; 37001 37002 that.element = input; 37003 37004 input 37005 .attr("multiple", that._supportsMultiple() ? that.multiple : false) 37006 .attr("autocomplete", "off") 37007 .click(function(e) { 37008 if (wrapper.hasClass("k-state-disabled")) { 37009 e.preventDefault(); 37010 } 37011 }) 37012 .change($.proxy(that._onInputChange, that)); 37013 }, 37014 37015 _onInputChange: function(e) { 37016 var upload = this, 37017 input = $(e.target), 37018 prevented = upload.trigger(SELECT, { files: inputFiles(input) }); 37019 37020 if (prevented) { 37021 upload._addInput(input.clone().val("")); 37022 input.remove(); 37023 } else { 37024 input.trigger("t:select"); 37025 } 37026 }, 37027 37028 _onDrop: function (e) { 37029 var dt = e.originalEvent.dataTransfer, 37030 that = this, 37031 droppedFiles = dt.files; 37032 37033 stopEvent(e); 37034 37035 if (droppedFiles.length > 0) { 37036 var prevented = that.trigger(SELECT, { files: getAllFileInfo(droppedFiles) }); 37037 if (!prevented) { 37038 $(".k-dropzone", that.wrapper).trigger("t:select", [ droppedFiles ]); 37039 } 37040 } 37041 }, 37042 37043 _enqueueFile: function(name, data) { 37044 var that = this, 37045 existingFileEntries, 37046 fileEntry, 37047 fileList = $(".k-upload-files", that.wrapper); 37048 37049 if (fileList.length == 0) { 37050 fileList = $("<ul class='k-upload-files k-reset'></ul>").appendTo(that.wrapper); 37051 if (!that.options.showFileList) { 37052 fileList.hide(); 37053 } 37054 } 37055 37056 existingFileEntries = $(".k-file", fileList); 37057 fileEntry = 37058 $("<li class='k-file'><span class='k-icon'></span><span class='k-filename' title='" + name + "'>" + name + "</span></li>") 37059 .appendTo(fileList) 37060 .data(data); 37061 37062 if (!that.multiple) { 37063 existingFileEntries.trigger("t:remove"); 37064 } 37065 37066 return fileEntry; 37067 }, 37068 37069 _removeFileEntry: function(fileEntry) { 37070 var fileList = fileEntry.closest(".k-upload-files"), 37071 allFiles; 37072 37073 fileEntry.remove(); 37074 allFiles = $(".k-file", fileList); 37075 37076 if (allFiles.find("> .k-fail").length === allFiles.length) { 37077 this._hideUploadButton(); 37078 } 37079 37080 if (allFiles.length == 0) { 37081 fileList.remove(); 37082 } 37083 }, 37084 37085 _fileAction: function(fileElement, actionKey) { 37086 var classDictionary = { remove: "k-delete", cancel: "k-cancel", retry: "k-retry" }; 37087 if (!classDictionary.hasOwnProperty(actionKey)) { 37088 return; 37089 } 37090 37091 this._clearFileAction(fileElement); 37092 37093 fileElement.append( 37094 this._renderAction(classDictionary[actionKey], this.localization[actionKey]) 37095 .addClass("k-upload-action") 37096 ); 37097 }, 37098 37099 _fileState: function(fileEntry, stateKey) { 37100 var localization = this.localization, 37101 states = { 37102 uploading: { 37103 cssClass: "k-loading", 37104 text : localization.statusUploading 37105 }, 37106 uploaded: { 37107 cssClass: "k-success", 37108 text : localization.statusUploaded 37109 }, 37110 failed: { 37111 cssClass: "k-fail", 37112 text : localization.statusFailed 37113 } 37114 }, 37115 currentState = states[stateKey]; 37116 37117 if (currentState) { 37118 var icon = fileEntry.children(".k-icon").text(currentState.text); 37119 icon[0].className = "k-icon " + currentState.cssClass; 37120 } 37121 }, 37122 37123 _renderAction: function (actionClass, actionText) { 37124 if (actionClass != "") { 37125 return $( 37126 "<button type='button' class='k-button k-button-icontext'>" + 37127 "<span class='k-icon " + actionClass + "'></span>" +
37128 actionText + 37129 "</button>" 37130 ) 37131 } 37132 else { 37133 return $( 37134 "<button type='button' class='k-button'>" + 37135 actionText + 37136 "</button>" 37137 ) 37138 } 37139 }, 37140 37141 _clearFileAction: function(fileElement) { 37142 fileElement 37143 .find(".k-upload-action").remove(); 37144 }, 37145 37146 _onFileAction: function(e) { 37147 var that = this; 37148 37149 if (!that.wrapper.hasClass("k-state-disabled")) { 37150 var button = $(e.target).closest(".k-upload-action"), 37151 icon = button.find(".k-icon"), 37152 fileEntry = button.closest(".k-file"), 37153 eventArgs = { files: fileEntry.data("fileNames") }; 37154 37155 if (icon.hasClass("k-delete")) { 37156 if (!that.trigger(REMOVE, eventArgs)) { 37157 fileEntry.trigger("t:remove", eventArgs.data); 37158 } 37159 } else if (icon.hasClass("k-cancel")) { 37160 that.trigger(CANCEL, eventArgs); 37161 fileEntry.trigger("t:cancel"); 37162 this._checkAllComplete(); 37163 } else if (icon.hasClass("k-retry")) { 37164 fileEntry.trigger("t:retry"); 37165 } 37166 } 37167 37168 return false; 37169 }, 37170 37171 _onUploadSelected: function() { 37172 this.wrapper.trigger("t:saveSelected"); 37173 return false; 37174 }, 37175 37176 _onFileProgress: function(e, percentComplete) { 37177 var progressBar = $(".k-progress-status", e.target); 37178 if (progressBar.length == 0) { 37179 progressBar = 37180 $("<span class='k-progress'><span class='k-progress-status' style='width: 0;'></span></span>") 37181 .appendTo($(".k-filename", e.target)) 37182 .find(".k-progress-status"); 37183 } 37184 37185 progressBar.width(percentComplete + "%"); 37186 }, 37187 37188 _onUploadSuccess: function(e, response, xhr) { 37189 var fileEntry = getFileEntry(e); 37190 37191 this._fileState(fileEntry, "uploaded"); 37192 37193 this.trigger(SUCCESS, { 37194 files: fileEntry.data("fileNames"), 37195 response: response, 37196 operation: "upload", 37197 XMLHttpRequest: xhr 37198 }); 37199 37200 if (this._supportsRemove()) { 37201 this._fileAction(fileEntry, REMOVE); 37202 } else { 37203 this._clearFileAction(fileEntry); 37204 } 37205 37206 this._checkAllComplete(); 37207 }, 37208 37209 _onUploadError: function(e, xhr) { 37210 var fileEntry = getFileEntry(e); 37211 37212 this._fileState(fileEntry, "failed"); 37213 this._fileAction(fileEntry, "retry"); 37214 37215 var prevented = this.trigger(ERROR, { 37216 operation: "upload", 37217 files: fileEntry.data("fileNames"), 37218 XMLHttpRequest: xhr 37219 }); 37220 37221 logToConsole("Server response: " + xhr.responseText); 37222 37223 if (!prevented) { 37224 this._alert("Error! Upload failed. Unexpected server response - see console."); 37225 } 37226 37227 this._checkAllComplete(); 37228 }, 37229 37230 _showUploadButton: function() { 37231 var uploadButton = $(".k-upload-selected", this.wrapper); 37232 if (uploadButton.length == 0) { 37233 uploadButton = 37234 this._renderAction("", this.localization["uploadSelectedFiles"]) 37235 .addClass("k-upload-selected"); 37236 } 37237 37238 this.wrapper.append(uploadButton); 37239 }, 37240 37241 _hideUploadButton: function() { 37242 $(".k-upload-selected", this.wrapper).remove(); 37243 }, 37244 37245 _onParentFormSubmit: function() { 37246 var upload = this, 37247 element = upload.element; 37248 element.trigger("t:abort"); 37249 37250 if (!element.value) { 37251 var input = $(element); 37252 37253 // Prevent submitting an empty input 37254 input.attr("disabled", "disabled"); 37255 37256 window.setTimeout(function() { 37257 // Restore the input so the Upload remains functional 37258 // in case the user cancels the form submit 37259 input.removeAttr("disabled"); 37260 }, 0); 37261 } 37262 }, 37263 37264 _onParentFormReset: function() { 37265 $(".k-upload-files", this.wrapper).remove(); 37266 }, 37267 37268 _supportsFormData: function() { 37269 if ($.browser.safari) { 37270 return false; 37271 } 37272 37273 return typeof(FormData) != "undefined"; 37274 }, 37275 37276 _supportsMultiple: function() { 37277 return !$.browser.opera; 37278 }, 37279 37280 _supportsDrop: function() { 37281 var userAgent = this._userAgent().toLowerCase(), 37282 isChrome = /chrome/.test(userAgent), 37283 isSafari = !isChrome && /safari/.test(userAgent), 37284 isWindowsSafari = isSafari && /windows/.test(userAgent); 37285 37286 return !isWindowsSafari && this._supportsFormData() && (this.options.async.saveUrl != undefined); 37287 }, 37288 37289 _userAgent: function() { 37290 return navigator.userAgent; 37291 }, 37292 37293 _setupDropZone: function() { 37294 $(".k-upload-button", this.wrapper) 37295 .wrap("<div class='k-dropzone'></div>"); 37296 37297 var dropZone = $(".k-dropzone", this.wrapper) 37298 .append($("<em>" + this.localization["dropFilesHere"] + "</em>")) 37299 .bind({ 37300 "dragenter": stopEvent, 37301 "dragover": function(e) { e.preventDefault(); }, 37302 "drop" : $.proxy(this._onDrop, this) 37303 }); 37304 37305 bindDragEventWrappers(dropZone, 37306 function() { dropZone.addClass("k-dropzone-hovered"); }, 37307 function() { dropZone.removeClass("k-dropzone-hovered"); }); 37308 37309 bindDragEventWrappers($(document), 37310 function() { dropZone.addClass("k-dropzone-active"); }, 37311 function() { dropZone.removeClass("k-dropzone-active"); }); 37312 }, 37313 37314 _supportsRemove: function() { 37315 return this.options.async.removeUrl != undefined; 37316 }, 37317 37318 _submitRemove: function(fileNames, data, onSuccess, onError) { 37319 var upload = this, 37320 removeField = upload.options.async.removeField || "fileNames", 37321 params = $.extend(data, getAntiForgeryTokens()); 37322 37323 params[removeField] = fileNames; 37324 37325 $.ajax({
37326 type: this.options.async.removeVerb, 37327 dataType: "json", 37328 url: this.options.async.removeUrl, 37329 traditional: true, 37330 data: params, 37331 success: onSuccess, 37332 error: onError 37333 }); 37334 }, 37335 37336 _alert: function(message) { 37337 alert(message); 37338 }, 37339 37340 _wrapInput: function(input) { 37341 input.wrap("<div class='k-widget k-upload'><div class='k-button k-upload-button'></div></div>"); 37342 input.closest(".k-button") 37343 .append("<span>" + this.localization.select + "</span>"); 37344 37345 return input.closest(".k-upload"); 37346 }, 37347 37348 _checkAllComplete: function() { 37349 if ($(".k-file .k-icon.k-loading", this.wrapper).length == 0) { 37350 this.trigger(COMPLETE); 37351 } 37352 } 37353 }); 37354 37355 // Synchronous upload module 37356 var syncUploadModule = function(upload) { 37357 this.name = "syncUploadModule"; 37358 this.element = upload.wrapper; 37359 this.upload = upload; 37360 this.element 37361 .bind("t:select", $.proxy(this.onSelect, this)) 37362 .bind("t:remove", $.proxy(this.onRemove, this)) 37363 .closest("form") 37364 .attr("enctype", "multipart/form-data") 37365 .attr("encoding", "multipart/form-data"); 37366 }; 37367 37368 syncUploadModule.prototype = /** @ignore */ { 37369 onSelect: function(e) { 37370 var upload = this.upload; 37371 var sourceInput = $(e.target); 37372 upload._addInput(sourceInput.clone().val("")); 37373 var file = upload._enqueueFile(getFileName(sourceInput), { 37374 "relatedInput" : sourceInput, "fileNames": inputFiles(sourceInput) 37375 }); 37376 37377 upload._fileAction(file, REMOVE); 37378 }, 37379 37380 onRemove: function(e) { 37381 var fileEntry = getFileEntry(e); 37382 fileEntry.data("relatedInput").remove(); 37383 37384 this.upload._removeFileEntry(fileEntry); 37385 } 37386 }; 37387 37388 // Iframe upload module 37389 var iframeUploadModule = function(upload) { 37390 this.name = "iframeUploadModule"; 37391 this.element = upload.wrapper; 37392 this.upload = upload; 37393 this.iframes = []; 37394 this.element 37395 .bind("t:select", $.proxy(this.onSelect, this)) 37396 .bind("t:cancel", $.proxy(this.onCancel, this)) 37397 .bind("t:retry", $.proxy(this.onRetry, this)) 37398 .bind("t:remove", $.proxy(this.onRemove, this)) 37399 .bind("t:saveSelected", $.proxy(this.onSaveSelected, this)) 37400 .bind("t:abort", $.proxy(this.onAbort, this)); 37401 }; 37402 37403 Upload._frameId = 0; 37404 37405 iframeUploadModule.prototype = /** @ignore */ { 37406 onSelect: function(e) { 37407 var upload = this.upload, 37408 sourceInput = $(e.target); 37409 37410 var fileEntry = this.prepareUpload(sourceInput); 37411 37412 if (upload.options.async.autoUpload) { 37413 this.performUpload(fileEntry); 37414 } else { 37415 if (upload._supportsRemove()) { 37416 this.upload._fileAction(fileEntry, REMOVE); 37417 } 37418 37419 upload._showUploadButton(); 37420 } 37421 }, 37422 37423 prepareUpload: function(sourceInput) { 37424 var upload = this.upload; 37425 var activeInput = $(upload.element); 37426 var name = upload.options.async.saveField || sourceInput.attr("name"); 37427 upload._addInput(sourceInput.clone().val("")); 37428 37429 sourceInput.attr("name", name); 37430 37431 var iframe = this.createFrame(upload.name + "_" + Upload._frameId++); 37432 this.registerFrame(iframe); 37433 37434 var form = this.createForm(upload.options.async.saveUrl, iframe.attr("name")) 37435 .append(activeInput); 37436 37437 var fileEntry = upload._enqueueFile( 37438 getFileName(sourceInput), 37439 { "frame": iframe, "relatedInput": activeInput, "fileNames": inputFiles(sourceInput) }); 37440 37441 iframe 37442 .data({ "form": form, "file": fileEntry }); 37443 37444 return fileEntry; 37445 }, 37446 37447 performUpload: function(fileEntry) { 37448 var e = { files: fileEntry.data("fileNames") }, 37449 iframe = fileEntry.data("frame"), 37450 upload = this.upload; 37451 37452 if (!upload.trigger(UPLOAD, e)) { 37453 upload._hideUploadButton(); 37454 37455 iframe.appendTo(document.body); 37456 37457 var form = iframe.data("form") 37458 .appendTo(document.body); 37459 37460 e.data = $.extend({ }, e.data, getAntiForgeryTokens()); 37461 for (var key in e.data) { 37462 var dataInput = form.find("input[name='" + key + "']"); 37463 if (dataInput.length == 0) { 37464 dataInput = $("<input>", { type: "hidden", name: key }) 37465 .appendTo(form); 37466 } 37467 dataInput.val(e.data[key]); 37468 } 37469 37470 upload._fileAction(fileEntry, CANCEL); 37471 upload._fileState(fileEntry, "uploading"); 37472 37473 iframe 37474 .one("load", $.proxy(this.onIframeLoad, this)); 37475 37476 form[0].submit(); 37477 } else { 37478 upload._removeFileEntry(iframe.data("file"));
37479 this.cleanupFrame(iframe); 37480 this.unregisterFrame(iframe); 37481 } 37482 }, 37483 37484 onSaveSelected: function(e) { 37485 var module = this; 37486 37487 $(".k-file", this.element).each(function() { 37488 var fileEntry = $(this), 37489 started = isFileUploadStarted(fileEntry); 37490 37491 if (!started) { 37492 module.performUpload(fileEntry); 37493 } 37494 }); 37495 }, 37496 37497 onIframeLoad: function(e) { 37498 var iframe = $(e.target); 37499 37500 try { 37501 var responseText = iframe.contents().text(); 37502 } catch (e) { 37503 responseText = "Error trying to get server response: " + e; 37504 } 37505 37506 this.processResponse(iframe, responseText); 37507 }, 37508 37509 processResponse: function(iframe, responseText) { 37510 var fileEntry = iframe.data("file"), 37511 module = this, 37512 fakeXHR = { 37513 responseText: responseText 37514 }; 37515 37516 tryParseJSON(responseText, 37517 function(jsonResult) { 37518 $.extend(fakeXHR, { statusText: "OK", status: "200" }); 37519 fileEntry.trigger("t:upload-success", [ jsonResult, fakeXHR ]); 37520 module.cleanupFrame(iframe); 37521 module.unregisterFrame(iframe); 37522 }, 37523 function() { 37524 $.extend(fakeXHR, { statusText: "error", status: "500" }); 37525 fileEntry.trigger("t:upload-error", [ fakeXHR ]); 37526 } 37527 ); 37528 }, 37529 37530 onCancel: function(e) { 37531 var iframe = $(e.target).data("frame"); 37532 37533 this.stopFrameSubmit(iframe); 37534 this.cleanupFrame(iframe); 37535 this.unregisterFrame(iframe); 37536 this.upload._removeFileEntry(iframe.data("file")); 37537 }, 37538 37539 onRetry: function(e) { 37540 var fileEntry = getFileEntry(e); 37541 this.performUpload(fileEntry); 37542 }, 37543 37544 onRemove: function(e, data) { 37545 var fileEntry = getFileEntry(e); 37546 37547 var iframe = fileEntry.data("frame"); 37548 if (iframe) 37549 { 37550 this.unregisterFrame(iframe); 37551 this.upload._removeFileEntry(fileEntry); 37552 this.cleanupFrame(iframe); 37553 } else { 37554 removeUploadedFile(fileEntry, this.upload, data); 37555 } 37556 }, 37557 37558 onAbort: function() { 37559 var element = this.element, 37560 module = this; 37561 37562 $.each(this.iframes, function() { 37563 $("input", this.data("form")).appendTo(element); 37564 module.stopFrameSubmit(this[0]); 37565 this.data("form").remove(); 37566 this.remove(); 37567 }); 37568 37569 this.iframes = []; 37570 }, 37571 37572 createFrame: function(id) { 37573 return $( 37574 "<iframe" + 37575 " name='" + id + "'" + 37576 " id='" + id + "'" + 37577 " style='display:none;' />" 37578 ); 37579 }, 37580 37581 createForm: function(action, target) { 37582 return $( 37583 "<form enctype='multipart/form-data' method='POST'" + 37584 " action='" + action + "'" + 37585 " target='" + target + "'" + 37586 "/>"); 37587 }, 37588 37589 stopFrameSubmit: function(frame) {
37590 if (typeof(frame.stop) != "undefined") { 37591 frame.stop(); 37592 } else if (frame.document) { 37593 frame.document.execCommand("Stop"); 37594 frame.contentWindow.location.href = frame.contentWindow.location.href; 37595 } 37596 }, 37597 37598 registerFrame: function(frame) { 37599 this.iframes.push(frame); 37600 }, 37601 37602 unregisterFrame: function(frame) { 37603 this.iframes = $.grep(this.iframes, function(value) { 37604 return value.attr("name") != frame.attr("name"); 37605 }); 37606 }, 37607 37608 cleanupFrame: function(frame) { 37609 var form = frame.data("form"); 37610 37611 frame.data("file").data("frame", null); 37612 37613 setTimeout(function () { 37614 form.remove(); 37615 frame.remove(); 37616 }, 1); 37617 } 37618 }; 37619 37620 // FormData upload module 37621 var formDataUploadModule = function(upload) { 37622 this.name = "formDataUploadModule"; 37623 this.element = upload.wrapper; 37624 this.upload = upload; 37625 this.element 37626 .bind("t:select", $.proxy(this.onSelect, this)) 37627 .bind("t:cancel", $.proxy(this.onCancel, this)) 37628 .bind("t:remove", $.proxy(this.onRemove, this)) 37629 .bind("t:retry", $.proxy(this.onRetry, this)) 37630 .bind("t:saveSelected", $.proxy(this.onSaveSelected, this)) 37631 .bind("t:abort", $.proxy(this.onAbort, this)); 37632 }; 37633 37634 formDataUploadModule.prototype = /** @ignore */ { 37635 onSelect: function(e, rawFiles) { 37636 var upload = this.upload, 37637 module = this, 37638 sourceElement = $(e.target), 37639 files = rawFiles ? getAllFileInfo(rawFiles) : this.inputFiles(sourceElement), 37640 fileEntries = this.prepareUpload(sourceElement, files); 37641 37642 $.each(fileEntries, function() { 37643 if (upload.options.async.autoUpload) { 37644 module.performUpload(this); 37645 } else { 37646 if (upload._supportsRemove()) { 37647 upload._fileAction(this, REMOVE); 37648 } 37649 upload._showUploadButton(); 37650 } 37651 }); 37652 }, 37653 37654 prepareUpload: function(sourceElement, files) { 37655 var fileEntries = this.enqueueFiles(files); 37656 37657 if (sourceElement.is("input")) { 37658 $.each(fileEntries, function() { 37659 $(this).data("relatedInput", sourceElement); 37660 }); 37661 sourceElement.data("relatedFileEntries", fileEntries); 37662 this.upload._addInput(sourceElement.clone().val("")); 37663 } 37664 37665 return fileEntries; 37666 }, 37667 37668 enqueueFiles: function(arrFileInfo) { 37669 var upload = this.upload 37670 fileEntries = []; 37671 37672 for (var i = 0; i < arrFileInfo.length; i++) { 37673 var currentFile = arrFileInfo[i], 37674 name = currentFile.name; 37675 37676 var fileEntry = upload._enqueueFile(name, { "fileNames": [ currentFile ] }); 37677 fileEntry.data("formData", this.createFormData(arrFileInfo[i])); 37678 37679 fileEntries.push(fileEntry); 37680 } 37681 37682 return fileEntries; 37683 }, 37684 37685 inputFiles: function(sourceInput) { 37686 return inputFiles(sourceInput); 37687 }, 37688 37689 performUpload: function(fileEntry) { 37690 var upload = this.upload, 37691 formData = fileEntry.data("formData"), 37692 e = { files: fileEntry.data("fileNames") }; 37693 37694 if (!upload.trigger(UPLOAD, e)) { 37695 upload._fileAction(fileEntry, CANCEL); 37696 upload._hideUploadButton(); 37697 37698 if (!fileEntry.data("dataAppended")) { 37699 e.data = $.extend({ }, e.data, getAntiForgeryTokens()); 37700 for (var key in e.data) { 37701 formData.append(key, e.data[key]); 37702 } 37703 37704 fileEntry.data("dataAppended", true); 37705 } 37706 37707 upload._fileState(fileEntry, "uploading"); 37708 37709 this.postFormData(this.upload.options.async.saveUrl, formData, fileEntry); 37710 } else { 37711 this.removeFileEntry(fileEntry); 37712 } 37713 }, 37714 37715 onSaveSelected: function(e) { 37716 var module = this; 37717 37718 $(".k-file", this.element).each(function() { 37719 var fileEntry = $(this), 37720 started = isFileUploadStarted(fileEntry); 37721 37722 if (!started) { 37723 module.performUpload(fileEntry); 37724 } 37725 }); 37726 }, 37727 37728 onCancel: function(e) { 37729 var fileEntry = getFileEntry(e); 37730 this.stopUploadRequest(fileEntry); 37731 this.removeFileEntry(fileEntry); 37732 }, 37733 37734 onRetry: function(e) { 37735 var fileEntry = getFileEntry(e); 37736 this.performUpload(fileEntry); 37737 }, 37738 37739 onRemove: function(e, data) { 37740 var fileEntry = getFileEntry(e); 37741 37742 if (fileEntry.children(".k-icon").is(".k-success")) { 37743 removeUploadedFile(fileEntry, this.upload, data); 37744 } else { 37745 this.removeFileEntry(fileEntry); 37746 } 37747 }, 37748 37749 postFormData: function(url, data, fileEntry) { 37750 var xhr = new XMLHttpRequest(), 37751 module = this; 37752 37753 fileEntry.data("request", xhr); 37754 37755 xhr.addEventListener("load", function(e) { 37756 module.onRequestSuccess.call(module, e, fileEntry); 37757 }, false); 37758 37759 xhr.addEventListener(ERROR, function(e) { 37760 module.onRequestError.call(module, e, fileEntry); 37761 }, false); 37762 37763 xhr.upload.addEventListener("progress", function(e) { 37764 module.onRequestProgress.call(module, e, fileEntry); 37765 }, false); 37766 37767 xhr.open("POST", url); 37768 xhr.send(data); 37769 }, 37770 37771 createFormData: function(fileInfo) { 37772 var formData = new FormData(), 37773 upload = this.upload; 37774 37775 formData.append(upload.options.async.saveField || upload.name, fileInfo.rawFile); 37776 37777 return formData; 37778 }, 37779 37780 onRequestSuccess: function(e, fileEntry) { 37781 var xhr = e.target, 37782 module = this; 37783 tryParseJSON(xhr.responseText, 37784 function(jsonResult) { 37785 fileEntry.trigger("t:upload-success", [ jsonResult, xhr ]); 37786 fileEntry.trigger("t:progress", [ 100 ]); 37787 module.cleanupFileEntry(fileEntry); 37788 }, 37789 function() { 37790 fileEntry.trigger("t:upload-error", [ xhr ]); 37791 } 37792 ); 37793 }, 37794 37795 onRequestError: function(e, fileEntry) { 37796 var xhr = e.target; 37797 fileEntry.trigger("t:upload-error", [ xhr ]); 37798 }, 37799 37800 cleanupFileEntry: function(fileEntry) { 37801 var relatedInput = fileEntry.data("relatedInput"), 37802 uploadComplete = true; 37803 37804 if (relatedInput) { 37805 $.each(relatedInput.data("relatedFileEntries"), function() { 37806 // Exclude removed file entries and self 37807 if (this.parent().length > 0 && this[0] != fileEntry[0]) { 37808 uploadComplete = uploadComplete && this.children(".k-icon").is(".k-success"); 37809 } 37810 }); 37811 37812 if (uploadComplete) { 37813 relatedInput.remove(); 37814 } 37815 } 37816 37817 fileEntry.data("formData", null); 37818 }, 37819 37820 removeFileEntry: function(fileEntry) { 37821 this.cleanupFileEntry(fileEntry); 37822 this.upload._removeFileEntry(fileEntry); 37823 }, 37824 37825 onRequestProgress: function(e, fileEntry) { 37826 var percentComplete = Math.round(e.loaded * 100 / e.total); 37827 fileEntry.trigger("t:progress", [ percentComplete ]); 37828 }, 37829 37830 stopUploadRequest: function(fileEntry) { 37831 fileEntry.data("request").abort(); 37832 } 37833 }; 37834 37835 // Helper functions 37836 function getFileName(input) { 37837 return $.map(inputFiles(input), function (file) { 37838 return file.name; 37839 }).join(", "); 37840 } 37841 37842 function inputFiles($input) { 37843 var input = $input[0]; 37844 if (input.files) { 37845 return getAllFileInfo(input.files); 37846 } else { 37847 return [{ 37848 name: stripPath(input.value), 37849 extension: getFileExtension(input.value), 37850 size: null 37851 }]; 37852 } 37853 } 37854 37855 function getAllFileInfo(rawFiles) { 37856 return $.map(rawFiles, function (file) { 37857 return getFileInfo(file); 37858 }); 37859 } 37860 37861 function getFileInfo(rawFile) { 37862 // Older Firefox versions (before 3.6) use fileName and fileSize 37863 var fileName = rawFile.name || rawFile.fileName; 37864 return { 37865 name: fileName, 37866 extension: getFileExtension(fileName), 37867 size: rawFile.size || rawFile.fileSize, 37868 rawFile: rawFile 37869 }; 37870 } 37871 37872 function getFileExtension(fileName) { 37873 var matches = fileName.match(rFileExtension); 37874 return matches ? matches[0] : ""; 37875 } 37876 37877 function stripPath(name) { 37878 var slashIndex = name.lastIndexOf("\\"); 37879 return (slashIndex != -1) ? name.substr(slashIndex + 1) : n
37879ame; 37880 } 37881 37882 function removeUploadedFile(fileEntry, upload, data) { 37883 if (!upload._supportsRemove()) { 37884 return; 37885 } 37886 37887 var files = fileEntry.data("fileNames"); 37888 var fileNames = $.map(files, function(file) { return file.name }); 37889 37890 upload._submitRemove(fileNames, data, 37891 function onSuccess(data, textStatus, xhr) { 37892 upload._removeFileEntry(fileEntry); 37893 37894 upload.trigger(SUCCESS, { 37895 operation: "remove", 37896 files: files, 37897 response: data, 37898 XMLHttpRequest: xhr }); 37899 }, 37900 37901 function onError(xhr, textStatus, textStatus) { 37902 var prevented = upload.trigger(ERROR, { 37903 operation: "remove", 37904 files: files, 37905 XMLHttpRequest: xhr }); 37906 37907 logToConsole("Server response: " + xhr.responseText); 37908 37909 if (!prevented) { 37910 upload._alert("Error! Remove operation failed. Unexpected response - see console."); 37911 } 37912 } 37913 ); 37914 } 37915 37916 function tryParseJSON(input, onSuccess, onError) { 37917 try { 37918 var json = $.parseJSON(input); 37919 onSuccess(json); 37920 } catch (e) { 37921 onError(); 37922 } 37923 } 37924 37925 function stopEvent(e) { 37926 e.stopPropagation(); e.preventDefault(); 37927 } 37928 37929 function bindDragEventWrappers(element, onDragEnter, onDragLeave) { 37930 var hideInterval, lastDrag; 37931 37932 element 37933 .bind("dragenter", function(e) { 37934 onDragEnter(); 37935 lastDrag = new Date(); 37936 37937 if (!hideInterval) { 37938 hideInterval = setInterval(function() { 37939 var sinceLastDrag = new Date() - lastDrag; 37940 if (sinceLastDrag > 100) { 37941 onDragLeave(); 37942 37943 clearInterval(hideInterval); 37944 hideInterval = null; 37945 } 37946 }, 100); 37947 } 37948 }) 37949 .bind("dragover", function(e) { 37950 lastDrag = new Date(); 37951 }); 37952 } 37953 37954 function isFileUploadStarted(fileEntry) { 37955 return fileEntry.children(".k-icon").is(".k-loading, .k-success, .k-fail"); 37956 } 37957 37958 function getFileEntry(e) { 37959 return $(e.target).closest(".k-file"); 37960 } 37961 37962 function getAntiForgeryTokens() { 37963 var tokens = { }, 37964 csrf_token = $("meta[name=csrf-token]").attr("content"), 37965 csrf_param = $("meta[name=csrf-param]").attr("content"); 37966 37967 $("input[name^='__RequestVerificationToken']").each(function() { 37968 tokens[this.name] = this.value; 37969 }); 37970 37971 if (csrf_param !== undefined && csrf_token !== undefined) { 37972 tokens[csrf_param] = csrf_token; 37973 } 37974 37975 return tokens; 37976 } 37977 kendo.ui.plugin(Upload); 37978})(jQuery); 37979/** 37980 * @fileOverview Provides a Window implementation which can be used to display content in a modal or non-modal HTML 37981 * window. 37982 */ 37983 37984(function($, undefined) { 37985 /** 37986 * @name kendo.ui.Window.Description 37987 * 37988 * @section 37989 * <p> 37990 * A <strong>Window</strong> displays content in a modal or non-modal HTML window. By default, a 37991 * <strong>Window</strong> can be moved, resized, and closed. Its content can also be defined with either as 37992 * static HTML or loaded dynamically via AJAX. 37993 * </p> 37994 * <p> 37995 * A <strong>Window</strong> can be initialized from virtually any DOM element. During initialization, the 37996 * targeted content will automatically be wrapped in the div element of the <strong>Window</strong>. 37997 * </p> 37998 * <h3>Getting Started</h3> 37999 * 38000 * @exampleTitle Create a simple HTML element with the Window content 38001 * @example 38002 * <div id="window"> 38003 * Content of the Window 38004 * </div> 38005 * 38006 * @exampleTitle Initialize the Window using a selector 38007 * @example 38008 * $(document).ready(function() { 38009 * $("#window").kendoWindow(); 38010 * }); 38011 * 38012 * @section 38013 * <p> 38014 * When a <strong>Window</strong> is initialized, it will automatically be displayed open near the location of the 38015 * DOM element that was used to initialize the content. 38016 * </p> 38017 * <h3>Configuring Window Behaviors</h3> 38018 * <p> 38019 * A <strong>Window</strong> provides many configuration options that can be easily set during initialization. 38020 * Among the properties that can be controlled: 38021 * </p> 38022 * <ul> 38023 * <li>Minimum height/width</li> 38024 * <li>Available user actions (close/refresh/maximize/minimize) and ability to define custom ones</li> 38025 * <li>Title</li> 38026 * <li>Draggable and resizable behaviors</li> 38027 * </ul> 38028 * 38029 * @exampleTitle Create a modal Window with all user actions enabled 38030 * @example 38031 * $("#window").kendoWindow({ 38032 * actions: ["Custom", "Refresh", "Maximize", "Minimize", "Close"], 38033 * draggable: false, 38034 * height: "300px", 38035 * modal: true, 38036 * resizable: false, 38037 * title: "Modal Window", 38038 * width: "500px" 38039 * }); 38040 * 38041 * @section 38042 * <p> 38043 * The order of the values in the actions array determines the order in which the action buttons will be rendered 38044 * in the title of a <strong>Window</strong>. The maximize action serves both as a button for expanding a 38045 * <strong>Window</strong> to fill the screen and as a button to restore a <strong>Window</strong> to its previous 38046 * size. The minimize action collapses a <strong>Window</strong> to its title. 38047 * </p> 38048 * <p>If a non-recognized action name is supplied, it is treated as a custom action - <strong>k-icon</strong> and <strong>k-actionname</strong> 38049 * CSS classes are rendered for it and no click event handler is attached automatically. The Kendo stylesheets have a supplied icon for 38050 * actions with the name "Custom", but any name can be used. Click events can be captured and handled in a standard way:</p> 38051 * 38052 * @exampleTitle Custom actions 38053 * @example 38054 * $("#window").kendoWindow({ 38055 * actions: ["Custom", "Minimize", "Maximize", "Close"], 38056 * title: "Window Title" 38057 * }).data("kendoWindow").wrapper.find(".k-custom").click(function(e) { 38058 * alert("Custom action button clicked"); 38059 * e.preventDefault(); 38060 * }); 38061 * 38062 * <h3>Positioning and Opening a Window</h3> 38063 * <p> 38064 * In some scenarios, it is preferable to center a <strong>Window</strong> rather than open it near the HTML 38065 * element used to define the content. It is also common to open a <strong>Window</strong> as the result of the 38066 * action of a user rather than on the load event of a page. The <strong>Window</strong> API provides methods for 38067 * handling these scenarios. 38068 * </p> 38069 * 38070 * @exampleTitle Centering a Window and opening on button click 38071 * @example 38072 * <div id="window"> 38073 * Content of the Window 38074 * </div> 38075 * <button id="openButton">Open Window</button> 38076 * 38077 * @exampleTitle Initialize Window, center, and configure button click action 38078 * @example 38079 * $(document).ready(function(){ 38080 * var window = $("#window").kendoWindow({ 38081 * height: "200px", 38082 * title: "Centered Window", 38083 * visible: false, 38084 * width: "200px" 38085 * }).data("kendoWindow"); 38086 * }); 38087 * 38088 * $("#openButton").click(function(){ 38089 * var window = $("#window").data("kendoWindow"); 38090 * window.center(); 38091 * window.open(); 38092 * }); 38093 * 38094 * @section 38095 * <h3>Loading Window content via AJAX</h3> 38096 * <p> 38097 * A <strong>Window</strong>
38097 provides built-in support for asynchronously loading content from a URL. This URL 38098 * should return a HTML fragment that can be loaded in a Window content area. 38099 * </p> 38100 * 38101 * @exampleTitle Load Window content asynchronously 38102 * @example 38103 * <div id="window"></div> 38104 * 38105 * @exampleTitle Initialize window and configure content loading 38106 * @example 38107 * $(document).ready(function(){ 38108 * $("#window").kendoWindow({ 38109 * content: "html-content-snippet.html", 38110 * title: "Async Window Content" 38111 * }); 38112 * }); 38113 * 38114 * @section 38115 * <h3>Accessing an Existing Window</h3> 38116 * <p> 38117 * You can reference an existing <b>Window</b> instance via 38118 * <a href="http://api.jquery.com/jQuery.data/">jQuery.data()</a>. Once a reference has been established, you can 38119 * use the API to control its behavior. 38120 * </p> 38121 * 38122 * @exampleTitle Accessing an existing Window instance 38123 * @example 38124 * var window = $("#window").data("kendoWindow"); 38125 * 38126 */ 38127 var kendo = window.kendo, 38128 Widget = kendo.ui.Widget, 38129 Draggable = kendo.ui.Draggable, 38130 isPlainObject = $.isPlainObject, 38131 proxy = $.proxy, 38132 extend = $.extend, 38133 each = $.each, 38134 template = kendo.template, 38135 BODY = "body", 38136 templates, 38137 // classNames 38138 KWINDOW = ".k-window", 38139 KWINDOWTITLEBAR = ".k-window-titlebar", 38140 KWINDOWCONTENT = ".k-window-content", 38141 KWINDOWRESIZEHANDLES = ".k-resize-handle", 38142 KOVERLAY = ".k-overlay", 38143 KCONTENTFRAME = "k-content-frame", 38144 LOADING = "k-loading", 38145 KHOVERSTATE = "k-state-hover", 38146 // constants 38147 VISIBLE = ":visible", 38148 CURSOR = "cursor", 38149 // events 38150 OPEN = "open", 38151 ACTIVATE = "activate", 38152 DEACTIVATE = "deactivate", 38153 CLOSE = "close", 38154 REFRESH = "refresh", 38155 RESIZE = "resize", 38156 DRAGSTART = "dragstart", 38157 DRAGEND = "dragend", 38158 ERROR = "error", 38159 OVERFLOW = "overflow", 38160 ZINDEX = "zIndex", 38161 MINIMIZE_MAXIMIZE = ".k-window-actions .k-minimize,.k-window-actions .k-maximize", 38162 isLocalUrl = kendo.isLocalUrl; 38163 38164 function constrain(value, low, high) { 38165 return Math.max(Math.min(value, high), low); 38166 } 38167 38168 function windowObject(element) { 38169 return element.children(KWINDOWCONTENT).data("kendoWindow"); 38170 } 38171 38172 function openedModalWindows() { 38173 return $(KWINDOW).filter(function() { 38174 var wnd = $(this); 38175 return wnd.is(VISIBLE) && windowObject(wnd).options.modal; 38176 }).sort(function(a, b){ 38177 return +$(a).css("zIndex") - +$(b).css("zIndex"); 38178 }); 38179 } 38180 38181 function sizingAction(actionId, callback) { 38182 return function() { 38183 var that = this, 38184 wrapper = that.wrapper, 38185 style = wrapper[0].style, 38186 options = that.options; 38187 38188 if (options.isMaximized || options.isMinimized) { 38189 return; 38190 } 38191 38192 that.restoreOptions = { 38193 width: style.width, 38194 height: style.height 38195 }; 38196 38197 wrapper 38198 .find(KWINDOWRESIZEHANDLES).hide().end() 38199 .find(MINIMIZE_MAXIMIZE).parent().hide() 38200 .eq(0).before(templates.action({ name: "Restore" })); 38201 38202 callback.call(that); 38203 38204 return that; 38205 }; 38206 } 38207 38208 var Window = Widget.extend(/** @lends kendo.ui.Window.prototype */ { 38209 /** 38210 * 38211 * @constructs 38212 * @extends kendo.ui.Widget 38213 * 38214 * @param {DomElement} element 38215 * DOM element 38216 * 38217 * @param {Object} options 38218 * Configuration options. 38219 * 38220 * @option {Boolean} [modal] <false> 38221 * Specifies whether the window should block interaction with other page elements. 38222 * 38223 * @option {Boolean} [visible] <true> 38224 * Specifies whether the window will be initially visible. 38225 * 38226 * @option {Boolean} [draggable] <true> 38227 * Enables (<strong>true</strong>) or disables (<strong>false</strong>) the ability for users to move/drag a 38228 * <strong>Window</strong>. 38229 * 38230 * @option {Boolean} [resizable] <true> 38231 * Enables (<strong>true</strong>) or disables (<strong>false</strong>) the ability for users to resize a 38232 * <strong>Window</strong>. 38233 * 38234 * @option {Integer}
38234 [minWidth] <50> 38235 * The minimum width (in pixels) that may be achieved by resizing the window. 38236 * 38237 * @option {Integer} [minHeight] <50> 38238 * The minimum height (in pixels) that may be achieved by resizing the window. 38239 * 38240 * @option {Integer} [maxWidth] <Infinity> 38241 * The maximum width (in pixels) that may be achieved by resizing the window. 38242 * 38243 * @option {Integer} [maxHeight] <Infinity> 38244 * The maximum height (in pixels) that may be achieved by resizing the window. 38245 * 38246 * @option {Object|String} [content] 38247 * Specifies a URL or request options that the window should load its content from. For remote URLs, a 38248 * container iframe element is automatically created. 38249 * 38250 * @option {String} [content.template] 38251 * Template for the content of a <strong>Window</strong>. 38252 * 38253 * @option {Boolean} [iframe] 38254 * Explicitly states whether content iframe should be created. 38255 * 38256 * @option {Array} [actions] <["Close"]> 38257 * The buttons for interacting with the window. Predefined array values are "Close", "Refresh", "Minimize", 38258 * and "Maximize". 38259 * 38260 * @option {String} [title] 38261 * The text in the window title bar. 38262 * 38263 * @option {Object} [appendTo] <document.body> 38264 * The element that the Window will be appended to. 38265 * 38266 * @option {Object} [animation] 38267 * A collection of {Animation} objects, used to change default animations. A value of <strong>false</strong> 38268 * will disable all animations in the widget. 38269 * 38270 * @option {Animation} [animation.open] 38271 * The animation that will be used when a Window opens. 38272 * 38273 * @option {Animation} [animation.close] 38274 * The animation that will be used when a Window closes. 38275 * 38276 */ 38277 init: function(element, options) { 38278 var that = this, 38279 wrapper, 38280 offset, visibility, display, 38281 isVisible = false, 38282 content; 38283 38284 Widget.fn.init.call(that, element, options); 38285 options = that.options; 38286 element = that.element; 38287 content = options.content; 38288 38289 that.appendTo = $(options.appendTo || document.body); 38290 38291 that._animations(); 38292 38293 if (!isPlainObject(content)) { 38294 content = options.content = { url: content }; 38295 } 38296 38297 if (!element.parent().is(that.appendTo)) { 38298 if (element.is(VISIBLE)) { 38299 offset = element.offset(); 38300 isVisible = true; 38301 } else { 38302 visibility = element.css("visibility"); 38303 display = element.css("display"); 38304 38305 element.css({ visibility: "hidden", display: "" }); 38306 offset = element.offset(); 38307 element.css({ visibility: visibility, display: display }); 38308 } 38309 } 38310 38311 wrapper = that.wrapper = element.closest(KWINDOW); 38312 38313 if (!element.is(".k-content") || !wrapper[0]) { 38314 element.addClass("k-window-content k-content"); 38315 that._createWindow(element, options); 38316 wrapper = that.wrapper = element.closest(KWINDOW); 38317 38318 that._dimensions(); 38319 } 38320 38321 if (offset) { 38322 if (isVisible) { 38323 wrapper.css({ 38324 top: offset.top, 38325 left: offset.left 38326 }); 38327 } else { 38328 wrapper.css({ 38329 top: offset.top, 38330 left: offset.left, 38331 visibility: "visible", 38332 display: "none" 38333 }); 38334 } 38335 } 38336 38337 if (content) { 38338 that.refresh(content); 38339 } 38340 38341 that.toFront(); 38342 38343 if (options.modal) { 38344 that._overlay(wrapper.is(VISIBLE)).css({ opacity: 0.5 }); 38345 } 38346 38347 wrapper.on({ 38348 mouseenter: function () { $(this).addClass(KHOVERSTATE); }, 38349 mouseleave: function () { $(this).removeClass(KHOVERSTATE); }, 38350 click: proxy(that._windowActionHandler, that) 38351 }, ".k-window-titlebar .k-window-action"); 38352 38353 if (options.resizable) {
38354 wrapper.on("dblclick", KWINDOWTITLEBAR, proxy(that.toggleMaximization, that)); 38355 38356 each("n e s w se sw ne nw".split(" "), function(index, handler) { 38357 wrapper.append(templates.resizeHandle(handler)); 38358 }); 38359 38360 that.resizing = new WindowResizing(that); 38361 } 38362 38363 if (options.draggable) { 38364 that.dragging = new WindowDragging(that); 38365 } 38366 38367 wrapper.add(wrapper.find(".k-resize-handle,.k-window-titlebar")) 38368 .on("mousedown", proxy(that.toFront, that)); 38369 38370 that.touchScroller = kendo.touchScroller(element); 38371 38372 $(window).resize(proxy(that._onDocumentResize, that)); 38373 38374 if (wrapper.is(VISIBLE)) { 38375 that.trigger(OPEN); 38376 that.trigger(ACTIVATE); 38377 } 38378 38379 kendo.notify(that); 38380 }, 38381 38382 _dimensions: function() { 38383 var that = this, 38384 wrapper = that.wrapper, 38385 element = that.element, 38386 options = that.options; 38387 38388 that.title(options.title); 38389 38390 if (options.width) { 38391 wrapper.width(options.width); 38392 } 38393 38394 if (options.height) { 38395 wrapper.height(options.height); 38396 } 38397 38398 each(["minWidth","minHeight","maxWidth","maxHeight"], function(_, prop) { 38399 var value = options[prop]; 38400 if (value && value != Infinity) { 38401 element.css(prop, value); 38402 } 38403 }); 38404 38405 if (!options.visible) { 38406 wrapper.hide(); 38407 } 38408 }, 38409 38410 _animations: function() { 38411 var options = this.options; 38412 38413 if (options.animation === false) { 38414 options.animation = { open: { show: true, effects: {} }, close: { hide:true, effects: {} } }; 38415 } 38416 }, 38417 38418 setOptions: function(options) { 38419 Widget.fn.setOptions.call(this, options); 38420 this._animations(); 38421 this._dimensions(); 38422 }, 38423 38424 events:[ 38425 /** 38426 * 38427 * Triggered when a Window is opened (i.e. the open() method is called). 38428 * 38429 * @name kendo.ui.Window#open 38430 * @event 38431 * @cancellable 38432 * 38433 * @param {Event} e 38434 * 38435 * @exampleTitle Attach open event handler during initialization; detach via unbind() 38436 * @example 38437 * // event handler for expand 38438 * var onOpen = function(e) { 38439 * // ... 38440 * }; 38441 * 38442 * // attach open event handler during initialization 38443 * var kendoWindow = $("#window").kendoWindow({ 38444 * open: onOpen 38445 * }); 38446 * 38447 * // detach expand event handler via unbind() 38448 * kendoWindow.data("kendoWindow").unbind("open", onOpen); 38449 * 38450 * @exampleTitle Attach open event handler via bind(); detach via unbind() 38451 * @example 38452 * // event handler for open 38453 * var onOpen = function(e) { 38454 * // ... 38455 * }; 38456 * 38457 * // attach open event handler via bind() 38458 * $("#window").data("kendoWindow").bind("open", onOpen); 38459 * 38460 * // detach open event handler via unbind() 38461 * $("#window").data("kendoWindow").unbind("open", onOpen); 38462 * 38463 */ 38464 OPEN, 38465 /** 38466 * 38467 * Triggered when a Window has finished its opening animation. 38468 * 38469 * @name kendo.ui.Window#activate 38470 * @event 38471 * 38472 * @param {Event} e 38473 * 38474 * @exampleTitle Attach activate event handler during initialization; detach via unbind() 38475 * @example 38476 * // event handler for activate 38477 * var onActivate = function(e) { 38478 * // ... 38479 * }; 38480 * 38481 * // attach activate event handler during initialization 38482 * var kendoWindow = $("#window").kendoWindow({ 38483 * activate: onActivate 38484 * }); 38485 * 38486 * // detach activate event handler via unbind() 38487 * kendoWindow.data("kendoWindow").unbind("activate", onActivate); 38488 * 38489 * @exampleTitle Attach activate event handler via bind(); detach via unbind() 38490 * @example 38491 * // event handler for activate 38492 * var onActivate = function(e) { 38493 * // ... 38494 * }; 38495 * 38496 * // attach activate event handler via bind() 38497 * $("#window").data("kendoWindow").bind("activate", onActivate); 38498 * 38499 * // detach activate event handler via unbind() 38500 * $("#window").data("kendoWindow").unbind("activate", onActivate); 38501 * 38502 */ 38503 ACTIVATE, 38504 /** 38505 * 38506 * Triggered when a Window has finished its closing animation. 38507 * 38508 * @name kendo.ui.Window#deactivate 38509 * @event 38510 * 38511 * @param {Event} e 38512 * 38513 * @exampleTitle Attach deactivate event handler during initialization; detach via unbind() 38514 * @example 38515 * // event handler for deactivate 38516 * var onDeactivate = function(e) { 38517 * // ... 38518 * }; 38519 * 38520 * // attach deactivate event handler during initialization 38521 * var kendoWindow = $("#window").kendoWindow({ 38522 * deactivate: onDeactivate 38523 * }); 38524 * 38525 * // detach deactivate event handler via unbind() 38526 * kendoWindow.data("kendoWindow").unbind("deactivate", onDeactivate); 38527 * 38528 * @exampleTitle Attach deactivate event handler via bind(); detach via unbind() 38529 * @example 38530 * // event handler for deactivate 38531 * var onDeactivate = function(e) { 38532 * // ... 38533 * }; 38534 * 38535 * // attach deactivate event handler via bind() 38536 * $("#window").data("kendoWindow").bind("deactivate", onDeactivate); 38537 * 38538 * // detach deactivate event handler via unbind() 38539 * $("#window").data("kendoWindow").unbind("deactivate", onDeactivate); 38540 * 38541 */ 38542 DEACTIVATE, 38543 /** 38544 * 38545 * Triggered when a Window is closed (by a user or through the close() method). 38546 * 38547 * @name kendo.ui.Window#close 38548 * @event 38549 * @cancellable 38550 * 38551 * @param {Event} e 38552 * 38553 * @exampleTitle Attach close event handler during initialization; detach via unbind() 38554 * @example 38555 * // event handler for close 38556 * var onClose = function(e) { 38557 * // ... 38558 * }; 38559 * 38560 * // attach close event handler during initialization 38561 * var kendoWindow = $("#window").kendoWindow({ 38562 * close: onClose 38563 * }); 38564 * 38565 * // detach close event handler via unbind() 38566 * kendoWindow.data("kendoWindow").unbind("close", onClose); 38567 * 38568 * @exampleTitle Attach close event handler via bind(); detach via unbind() 38569 * @example 38570 * // event handler for close 38571 * var onClose = function(e) { 38572 * // ... 38573 * }; 38574 * 38575 * // attach close event handler via bind() 38576 * $("#window").data("kendoWindow").bind("close", onClose); 38577 * 38578 * // detach close event handler via unbind() 38579 * $("#window").data("kendoWindow").unbind("close", onClose); 38580 * 38581 */ 38582 CLOSE, 38583 /** 38584 * 38585 * Triggered when the content of a Window have been refreshed via AJAX. 38586 * 38587 * @name kendo.ui.Window#refresh 38588 * @event 38589 * 38590 * @param {Event} e 38591 * 38592 * @exampleTitle Attach refresh event handler during initialization; detach via unbind() 38593 * @example 38594 * // event handler for refresh 38595 * var onRefresh = function(e) { 38596 * // ... 38597 * }; 38598 * 38599 * // attach refresh event handler during initialization 38600 * var kendoWindow = $("#window").kendoWindow({ 38601 * refresh: onRefresh 38602 * }); 38603 * 38604 * // detach refresh event handler via unbind() 38605 * kendoWindow.data("kendoWindow").unbind("refresh", onRefresh); 38606 * 38607 * @exampleTitle Attach refresh event handler via bind(); detach via unbind() 38608 * @example 38609 * // event handler for refresh 38610 * var onRefresh = function(e) { 38611 * // ... 38612 * }; 38613 * 38614 * // attach refresh event handler via bind() 38615 * $("#window").data("kendoWindow").bind("refresh", onRefresh); 38616 * 38617 * // detach refresh event handler via unbind() 38618 * $("#window").data("kendoWindow").unbind("refresh", onRefresh); 38619 * 38620 */ 38621 REFRESH, 38622 /** 38623 * 38624 * Triggered when a Window has been resized by a user. 38625 * 38626 * @name kendo.ui.Window#resize 38627 * @event 38628 * 38629 * @param {Event} e 38630 * 38631 * @exampleTitle Attach resize event handler during initialization; detach via unbind() 38632 * @example 38633 * // event handler for resize 38634 * var onResize = function(e) { 38635 * // ... 38636 * }; 38637 * 38638 * // attach resize event handler during initialization 38639 * var kendoWindow = $("#window").kendoWindow({ 38640 * resize: onResize 38641 * }); 38642 * 38643 * // detach resize event handler via unbind() 38644 * kendoWindow.data("kendoWindow").unbind("resize", onResize); 38645 * 38646 * @exampleTitle Attach resize event handler via bind(); detach via unbind() 38647 * @example 38648 * // event handler for resize 38649 * var onResize = function(e) { 38650 * // ... 38651 * }; 38652 * 38653 * // attach resize event handler via bind() 38654 * $("#window").data("kendoWindow").bind("resize", onResize); 38655 * 38656 * // detach resize event handler via unbind() 38657 * $("#window").data("kendoWindow").unbind("resize", onResize); 38658 * 38659 */ 38660 RESIZE, 38661 38662 /** 38663 * Triggered when the user starts to move the window. 38664 * @name kendo.ui.Window#dragstart 38665 * @event 38666 * @param {Event} e 38667 */ 38668 DRAGSTART, 38669 38670 /** 38671 * 38672 * Triggered when a Window has been moved by a user. 38673 * 38674 * @name kendo.ui.Window#dragend 38675 * @event 38676 * 38677 * @param {Event} e 38678 * 38679 * @exampleTitle Attach dragEnd event handler during initialization; detach via unbind() 38680 * @example 38681 * // event handler for dragEnd 38682 * var onDragEnd = function(e) { 38683 * // ... 38684 * }; 38685 * 38686 * // attach dragEnd event handler during initialization 38687 * var kendoWindow = $("#window").kendoWindow({ 38688 * dragend: onDragEnd 38689 * }); 38690 * 38691 * // detach dragEnd event handler via unbind() 38692 * kendoWindow.data("kendoWindow").unbind("dragend", onDragEnd); 38693 * 38694 * @exampleTitle Attach dragEnd event handler via bind(); detach via unbind() 38695 * @example 38696 * // event handler for dragEnd 38697 * var onDragEnd = function(e) { 38698 * // ... 38699 * }; 38700 * 38701 * // attach dragEnd event handler via bind() 38702 * $("#window").data("kendoWindow").bind("dragend", onDragEnd); 38703 * 38704 * // detach dragEnd event handler via unbind() 38705 * $("#window").data("kendoWindow").unbind("dragend", onDragEnd); 38706 * 38707 */ 38708 DRAGEND, 38709 38710 /** 38711 * 38712 * Triggered when an AJAX request for content fails. 38713 * 38714 * @name kendo.ui.Window#error 38715 * @event 38716 * 38717 * @param {Event} e 38718 * 38719 * @exampleTitle Attach error event handler during initialization; detach via unbind() 38720 * @example 38721 * // event handler for error 38722 * var onError = function(e) { 38723 * // ... 38724 * }; 38725 * 38726 * // attach dragEnd event handler during initialization 38727 * var kendoWindow = $("#window").kendoWindow({ 38728 * error: onError 38729 * }); 38730 * 38731 * // detach error event handler via unbind() 38732 * kendoWindow.data("kendoWindow").unbind("error", onError); 38733 * 38734 * @exampleTitle Attach error event handler via bind(); detach via unbind() 38735 * @example 38736 * // event handler for error 38737 * var onError = function(e) { 38738 * // ... 38739 * }; 38740 * 38741 * // attach error event handler via bind() 38742 * $("#window").data("kendoWindow").bind("error", onError); 38743 * 38744 * // detach error event handler via unbind() 38745 * $("#window").data("kendoWindow").unbind("error", onError); 38746 * 38747 */ 38748 ERROR 38749 ], 38750 38751 options: { 38752 name: "Window", 38753 animation: { 38754 open: { 38755 effects: { zoom: { direction: "in" }, fade: { direction: "in" } }, 38756 duration: 350, 38757 show: true 38758 }, 38759 close: { 38760 effects: { zoom: { direction: "out", properties: { scale: 0.7 } }, fade: { direction: "out" } }, 38761 duration: 350, 38762 hide: true 38763 } 38764 }, 38765 title: "", 38766 actions: ["Close"], 38767 modal: false, 38768 resizable: true, 38769 draggable: true, 38770 minWidth: 90, 38771 minHeight: 50, 38772 maxWidth: Infinity, 38773 maxHeight: Infinity, 38774 visible: true 38775 }, 38776 38777 _overlay: function (visible) {
38778 var overlay = this.appendTo.children(".k-overlay"), 38779 wrapper = this.wrapper; 38780 38781 if (!overlay.length) { 38782 overlay = $("<div class='k-overlay' />"); 38783 } 38784 38785 overlay 38786 .insertBefore(wrapper[0]) 38787 .toggle(visible) 38788 .css(ZINDEX, parseInt(wrapper.css(ZINDEX), 10) - 1); 38789 38790 return overlay; 38791 }, 38792 38793 _windowActionHandler: function (e) { 38794 var target = $(e.target).closest(".k-window-action").find(".k-icon"), 38795 that = this; 38796 38797 each({ 38798 "k-close": that.close, 38799 "k-maximize": that.maximize, 38800 "k-minimize": that.minimize, 38801 "k-restore": that.restore, 38802 "k-refresh": that.refresh 38803 }, function (commandName, handler) { 38804 if (target.hasClass(commandName)) { 38805 e.preventDefault(); 38806 handler.call(that); 38807 return false; 38808 } 38809 }); 38810 }, 38811 38812 /** 38813 * 38814 * Centers a <strong>Window</strong> within the viewport. 38815 * 38816 * @returns {Window} 38817 * Returns the (Kendo UI) Window object to support chaining. 38818 * 38819 * @example 38820 * var kendoWindow = $("#window").data("kendoWindow"); 38821 * kendoWindow.center(); 38822 * 38823 */ 38824 center: function () { 38825 var wrapper = this.wrapper, 38826 documentWindow = $(window); 38827 38828 wrapper.css({ 38829 left: documentWindow.scrollLeft() + Math.max(0, (documentWindow.width() - wrapper.width()) / 2), 38830 top: documentWindow.scrollTop() + Math.max(0, (documentWindow.height() - wrapper.height()) / 2) 38831 }); 38832 38833 return this; 38834 }, 38835 38836 /** 38837 * 38838 * Gets or set the title of a <strong>Window</strong>. 38839 * 38840 * @param {String} [text] 38841 * The title of the Window. 38842 * 38843 * @returns {Window} 38844 * If a title is provided, this method will return the (Kendo UI) Window object to support chaining. Otherwise, 38845 * it will return the current title of the (Kendo UI) Window. 38846 * 38847 * @exampleTitle Get the existing title of the Window 38848 * @example 38849 * var kendoWindow = $("#window").data("kendoWindow"); 38850 * var windowTitle = kendoWindow.title(); 38851 * 38852 * @exampleTitle Set the title of a Window; utilize chaining (if necessary) 38853 * @example 38854 * var kendoWindow = $("#window").data("kendoWindow").title("Do a barrel roll!"); 38855 * 38856 */ 38857 title: function (text) { 38858 var that = this, 38859 wrapper = that.wrapper, 38860 options = that.options, 38861 titleBar = wrapper.find(KWINDOWTITLEBAR), 38862 title = titleBar.children(".k-window-title"), 38863 titleBarHeight = titleBar.outerHeight(); 38864 38865 if (!arguments.length) { 38866 return title.text(); 38867 } 38868 38869 if (text === false) { 38870 wrapper.addClass("k-window-titleless"); 38871 titleBar.remove(); 38872 } else { 38873 if (!titleBar.length) { 38874 wrapper.prepend(templates.titlebar(extend(templates, options))); 38875 } 38876 38877 wrapper.css("padding-top", titleBarHeight); 38878 titleBar.css("margin-top", -titleBarHeight); 38879 } 38880 38881 title.text(text); 38882 38883 return that; 38884 }, 38885 38886 /** 38887 * 38888 * Gets or set the content of a <strong>Window</strong>. 38889 * 38890 * @param {String} [content] 38891 * The content of the Window. 38892 * 38893 * @returns {Window} 38894 * If content is provided, this method will return the (Kendo UI) Window object to support chaining. Otherwise, 38895 * it will return the current content of the (Kendo UI) Window. 38896 * 38897 * @exampleTitle Get the existing content of the Window 38898 * @example 38899 * var kendoWindow = $("#window").data("kendoWindow"); 38900 * var windowContent = kendoWindow.content(); 38901 * 38902 * @exampleTitle Set the title of a Window; utilize chaining (if necessary) 38903 * @example 38904 * var kendoWindow = $("#window").data("kendoWindow").content("Kendo UI for all the things!"); 38905 * 38906 */ 38907 content: function (html) { 38908 var content = this.wrapper.children(KWINDOWCONTENT); 38909 38910 if (!html) { 38911 return content.html(); 38912 } 38913 38914 content.html(html); 38915 return this; 38916 }, 38917 38918 /** 38919 * 38920 * Opens a Window. 38921 * 38922 * @returns {Window}
38923 * Returns the (Kendo UI) Window object to support chaining. 38924 * 38925 * @exampleTitle Open a Window; utilize chaining (if necessary) 38926 * @example 38927 * var kendoWindow = $("#window").data("kendoWindow").open(); 38928 * 38929 */ 38930 open: function () { 38931 var that = this, 38932 wrapper = that.wrapper, 38933 showOptions = that.options.animation.open, 38934 contentElement = wrapper.children(KWINDOWCONTENT), 38935 initialOverflow = contentElement.css(OVERFLOW); 38936 38937 if (!that.trigger(OPEN)) { 38938 that.toFront(); 38939 38940 if (that.options.modal) { 38941 var overlay = that._overlay(false); 38942 38943 if (showOptions.duration) { 38944 overlay.kendoStop().kendoAnimate({ 38945 effects: { fade: { direction: "out", properties: { opacity: 0.5 } } }, 38946 duration: showOptions.duration, 38947 show: true 38948 }); 38949 } else { 38950 overlay.css("opacity", 0.5).show(); 38951 } 38952 } 38953 38954 if (!wrapper.is(VISIBLE)) { 38955 contentElement.css(OVERFLOW, "hidden"); 38956 wrapper.show().kendoStop().kendoAnimate({ 38957 effects: showOptions.effects, 38958 duration: showOptions.duration, 38959 complete: function() { 38960 that.trigger(ACTIVATE); 38961 contentElement.css(OVERFLOW, initialOverflow); 38962 } 38963 }); 38964 } 38965 } 38966 38967 if (that.options.isMaximized) { 38968 $("html, body").css(OVERFLOW, "hidden"); 38969 } 38970 38971 return that; 38972 }, 38973 38974 /** 38975 * 38976 * Closes a Window. 38977 * 38978 * @returns {Window} 38979 * Returns the (Kendo UI) Window object to support chaining. 38980 * 38981 * @exampleTitle Close a Window; utilize chaining (if necessary) 38982 * @example 38983 * var kendoWindow = $("#window").data("kendoWindow").close(); 38984 * 38985 */ 38986 close: function () { 38987 var that = this, 38988 wrapper = that.wrapper, 38989 options = that.options, 38990 showOptions = options.animation.open, 38991 hideOptions = options.animation.close, 38992 modalWindows, 38993 shouldHideOverlay, overlay; 38994 38995 if (wrapper.is(VISIBLE) && !that.trigger(CLOSE)) { 38996 modalWindows = openedModalWindows(); 38997 38998 shouldHideOverlay = options.modal && modalWindows.length == 1; 38999 39000 overlay = options.modal ? that._overlay(true) : $(undefined); 39001 39002 if (shouldHideOverlay) { 39003 if (hideOptions.duration) { 39004 overlay.kendoStop().kendoAnimate({ 39005 effects: { fadeOut: { properties: { opacity: 0 } } }, 39006 duration: hideOptions.duration, 39007 hide: true 39008 }); 39009 } else { 39010 overlay.hide(); 39011 } 39012 } else if (modalWindows.length) { 39013 windowObject(modalWindows.eq(modalWindows.length - 2))._overlay(true); 39014 } 39015 39016 wrapper.kendoStop().kendoAnimate({ 39017 effects: hideOptions.effects || showOptions.effects, 39018 reverse: hideOptions.reverse === true, 39019 duration: hideOptions.duration, 39020 complete: function() { 39021 wrapper.hide(); 39022 that.trigger(DEACTIVATE); 39023 } 39024 }); 39025 } 39026 39027 if (that.options.isMaximized) { 39028 $("html, body").css(OVERFLOW, ""); 39029 } 39030 39031 return that; 39032 }, 39033 39034 /** 39035 * 39036 * Brings forward a Window to the top of the z-index. 39037 * 39038 * @returns {Window}
39039 * Returns the (Kendo UI) Window object to support chaining. 39040 * 39041 * @exampleTitle Bring forward a Window; utilize chaining (if necessary) 39042 * @example 39043 * var kendoWindow = $("#window").data("kendoWindow").toFront(); 39044 * 39045 */ 39046 toFront: function () { 39047 var that = this, 39048 wrapper = that.wrapper, 39049 currentWindow = wrapper[0], 39050 zIndex = +wrapper.css(ZINDEX), 39051 originalZIndex = zIndex; 39052 39053 $(KWINDOW).each(function(i, element) { 39054 var windowObject = $(element), 39055 zIndexNew = windowObject.css(ZINDEX), 39056 contentElement = windowObject.find(KWINDOWCONTENT); 39057 39058 if (!isNaN(zIndexNew)) { 39059 zIndex = Math.max(+zIndexNew, zIndex); 39060 } 39061 39062 // Add overlay to windows with iframes and lower z-index to prevent 39063 // trapping of events when resizing / dragging 39064 if (element != currentWindow && contentElement.find("> ." + KCONTENTFRAME).length > 0) { 39065 contentElement.append(templates.overlay); 39066 } 39067 }); 39068 39069 if (zIndex == 10001 || originalZIndex < zIndex) { 39070 wrapper.css(ZINDEX, zIndex + 2); 39071 that.element.find("> .k-overlay").remove(); 39072 } 39073 39074 return that; 39075 }, 39076 39077 /** 39078 * 39079 * Toggles a Window between a maximized and restored state. 39080 * 39081 * @returns {Window} 39082 * Returns the (Kendo UI) Window object to support chaining. 39083 * 39084 * @exampleTitle Toggle the state of a Window; utilize chaining (if necessary) 39085 * @example 39086 * var kendoWindow = $("#window").data("kendoWindow").toggleMaximization(); 39087 * 39088 */ 39089 toggleMaximization: function () { 39090 return this[this.options.isMaximized ? "restore" : "maximize"](); 39091 }, 39092 39093 /** 39094 * 39095 * Restores a maximized or minimized Window to its previous state. 39096 * 39097 * @returns {Window} 39098 * Returns the (Kendo UI) Window object to support chaining. 39099 * 39100 * @exampleTitle Restore the state of a Window; utilize chaining (if necessary) 39101 * @example 39102 * var kendoWindow = $("#window").data("kendoWindow").restore(); 39103 * 39104 */ 39105 restore: function () { 39106 var that = this, 39107 options = that.options, 39108 restoreOptions = that.restoreOptions; 39109 39110 if (!options.isMaximized && !options.isMinimized) { 39111 return; 39112 } 39113 39114 that.wrapper 39115 .css({ 39116 position: "absolute", 39117 left: restoreOptions.left, 39118 top: restoreOptions.top, 39119 width: restoreOptions.width, 39120 height: restoreOptions.height 39121 }) 39122 .find(".k-window-content,.k-resize-handle").show().end() 39123 .find(".k-window-titlebar .k-restore").parent().remove().end().end() 39124 .find(MINIMIZE_MAXIMIZE).parent().show(); 39125 39126 $("html, body").css(OVERFLOW, ""); 39127 39128 options.isMaximized = options.isMinimized = false; 39129 39130 that.trigger(RESIZE); 39131 39132 return that; 39133 }, 39134 39135 /** 39136 * 39137 * Maximizes a Window to the entire viewing area of the user agent. 39138 * 39139 * @function 39140 * @returns {Window} 39141 * Returns the (Kendo UI) Window object to support chaining. 39142 * 39143 * @exampleTitle Maximize a Window 39144 * @example 39145 * $("#window").data("kendoWindow").maximize(); 39146 * 39147 */ 39148 maximize: sizingAction("maximize", function() { 39149 var that = this, 39150 wrapper = that.wrapper, 39151 position = wrapper.position(); 39152 39153 extend(that.restoreOptions, { 39154 left: position.left, 39155 top: position.top 39156 }); 39157 39158 wrapper.css({ 39159 left: 0, 39160 top: 0, 39161 position: "fixed" 39162 }); 39163 39164 $("html, body").css(OVERFLOW, "hidden"); 39165 39166 that.options.isMaximized = true; 39167 39168 that._onDocumentResize(); 39169 }), 39170 39171 /** 39172 * 39173 * Maximizes a Window to its title bar. 39174 * 39175 * @function 39176 * @returns {Window}
39177 * Returns the (Kendo UI) Window object to support chaining. 39178 * 39179 * @exampleTitle Minimize a Window 39180 * @example 39181 * $("#window").data("kendoWindow").minimize(); 39182 * 39183 */ 39184 minimize: sizingAction("minimize", function() { 39185 var that = this; 39186 39187 that.wrapper.css("height", ""); 39188 that.element.hide(); 39189 39190 that.options.isMinimized = true; 39191 }), 39192 39193 _onDocumentResize: function () { 39194 var that = this, 39195 wrapper = that.wrapper, 39196 wnd = $(window); 39197 39198 if (!that.options.isMaximized) { 39199 return; 39200 } 39201 39202 wrapper.css({ 39203 width: wnd.width(), 39204 height: wnd.height() - parseInt(wrapper.css("padding-top"), 10) 39205 }); 39206 39207 that.trigger(RESIZE); 39208 }, 39209 39210 /** 39211 * 39212 * Refreshes the content of a Window from a remote URL. 39213 * 39214 * @param {Object|String} options 39215 * Options for requesting data from the server. 39216 * If omitted, the window uses the <code>content</code> property 39217 * that was supplied when the window was created. 39218 * Any options specified here are passed to jQuery.ajax(). 39219 * 39220 * @param {String} options.url 39221 * The server URL that will be requested. 39222 * 39223 * @param {Object} options.data 39224 * A JSON object containing the data that will be passed to the server. 39225 * 39226 * @param {String} options.type 39227 * The HTTP request method ("GET", "POST"). 39228 * 39229 * @param {String} options.template 39230 * A template to be used for displaying the requested data. 39231 * 39232 * @returns {Window} 39233 * Returns the (Kendo UI) Window object to support chaining. 39234 * 39235 * @example 39236 * var windowObject = $("#window").data("kendoWindow"); 39237 * windowObject.refresh("/feedbackForm"); 39238 * 39239 * windowObject.refresh({ 39240 * url: "/feedbackForm", 39241 * data: { userId: 42 } 39242 * }); 39243 * 39244 * windowObject.refresh({ 39245 * url: "/userInfo", 39246 * data: { userId: 42 }, 39247 * template: "Hello, #= firstName # #= lastName #" 39248 * }); 39249 * 39250 */ 39251 refresh: function (options) { 39252 var that = this, 39253 initOptions = that.options, 39254 element = $(that.element), 39255 iframe, 39256 showIframe = initOptions.iframe, 39257 url; 39258 39259 if (!isPlainObject(options)) { 39260 options = { url: options }; 39261 } 39262 39263 options = extend({}, initOptions.content, options); 39264 39265 url = options.url; 39266 39267 if (url) { 39268 if (typeof showIframe == "undefined") { 39269 showIframe = !isLocalUrl(url); 39270 } 39271 39272 if (!showIframe) { 39273 // perform AJAX request 39274 that._ajaxRequest(options); 39275 } else { 39276 iframe = element.find("." + KCONTENTFRAME)[0]; 39277 39278 if (iframe) { 39279 // refresh existing iframe 39280 iframe.src = url || iframe.src; 39281 } else { 39282 // render new iframe 39283 element.html(templates.contentFrame(extend({}, initOptions, { content: options }))); 39284 } 39285 } 39286 } else if (options.template) { 39287 // refresh template 39288 that.content(template(options.template)({})); 39289 } 39290 39291 return that; 39292 }, 39293 39294 _ajaxRequest: function (options) { 39295 var that = this, 39296 contentTemplate = options.template, 39297 refreshIcon = that.wrapper.find(".k-window-titlebar .k-refresh"), 39298 loadingIconTimeout = setTimeout(function () { 39299 refreshIcon.addClass(LOADING); 39300 }, 100); 39301 39302 $.ajax(extend({ 39303 type: "GET", 39304 dataType: "html", 39305 cache: false, 39306 error: proxy(function (xhr, status) { 39307 that.trigger(ERROR); 39308 }, that), 39309 complete: function () { 39310 clearTimeout(loadingIconTimeout); 39311 refreshIcon.removeClass(LOADING); 39312 }, 39313 success: proxy(function (data, textStatus) { 39314 if (contentTemplate) { 39315 data = template(contentTemplate)(data || {}); 39316 } 39317 39318 that.element.html(data); 39319 39320 that.trigger(REFRESH); 39321 }, that) 39322 }, options)); 39323 }, 39324 39325 /** 39326 * Destroys the window and its modal overlay, if necessary. Useful for removing modal windows. 39327 */ 39328 destroy: function () { 39329 var that = this, 39330 modalWindows, 39331 shouldHideOverlay; 39332 39333 that.wrapper.remove(); 39334 39335 modalWindows = openedModalWindows(); 39336 39337 shouldHideOverlay = that.options.modal && !modalWindows.length; 39338 39339 if (shouldHideOverlay) { 39340 that._overlay(false).remove(); 39341 } else if (modalWindows.length > 0) { 39342 windowObject(modalWindows.eq(modalWindows.length - 2))._overlay(true); 39343 } 39344 }, 39345 39346 _createWindow: function() { 39347 var that = this, 39348 contentHtml = that.element, 39349 options = that.options, 39350 iframeSrcAttributes, 39351 wrapper; 39352 39353 if (options.scrollable === false) { 39354 contentHtml.attr("style", "overflow:hidden;"); 39355 } 39356 39357 if (options.iframe) { 39358 contentHtml.html(templates.contentFrame(options)); 39359 } 39360 39361 wrapper = $(templates.wrapper(options)); 39362 39363 if (options.title !== false) { 39364 wrapper.append(templates.titlebar(extend(templates, options))); 39365 } 39366 39367 wrapper.toggleClass("k-rtl", !!that.element.closest(".k-rtl").length); 39368 39369 // Collect the src attributes of all iframes and then set them to empty string. 39370 // This seems to fix this IE9 "feature": http://msdn.microsoft.com/en-us
39370/library/gg622929%28v=VS.85%29.aspx?ppud=4 39371 iframeSrcAttributes = contentHtml.find("iframe").map(function(iframe) { 39372 var src = this.getAttribute("src"); 39373 this.src = ""; 39374 return src; 39375 }); 39376 39377 // Make sure the wrapper is appended to the body only once. IE9+ will throw exceptions if you move iframes in DOM 39378 wrapper 39379 .appendTo(that.appendTo) 39380 .append(contentHtml) 39381 .find("iframe").each(function(index) { 39382 // Restore the src attribute of the iframes when they are part of the live DOM tree 39383 this.src = iframeSrcAttributes[index]; 39384 }); 39385 } 39386 }); 39387 39388 templates = { 39389 wrapper: template("<div class='k-widget k-window' />"), 39390 action: template( 39391 "<a href='\\#' class='k-window-action k-link'>" + 39392 "<span class='k-icon k-#= name.toLowerCase() #'>#= name #</span>" + 39393 "</a>" 39394 ), 39395 titlebar: template( 39396 "<div class='k-window-titlebar k-header'> " + 39397 "<span class='k-window-title'>#= title #</span>" + 39398 "<div class='k-window-actions k-header'>" + 39399 "# for (var i = 0; i < actions.length; i++) { #" + 39400 "#= action({ name: actions[i] }) #" + 39401 "# } #" + 39402 "</div>" + 39403 "</div>" 39404 ), 39405 overlay: "<div class='k-overlay' />", 39406 contentFrame: template( 39407 "<iframe src='#= content.url #' title='#= title #' frameborder='0'" + 39408 " class='" + KCONTENTFRAME + "'>" + 39409 "This page requires frames in order to show content" + 39410 "</iframe>" 39411 ), 39412 resizeHandle: template("<div class='k-resize-handle k-resize-#= data #'></div>") 39413 }; 39414 39415 39416 function WindowResizing(wnd) { 39417 var that = this; 39418 39419 that.owner = wnd; 39420 that._draggable = new Draggable(wnd.wrapper, { 39421 filter: KWINDOWRESIZEHANDLES, 39422 group: wnd.wrapper.id + "-resizing", 39423 dragstart: proxy(that.dragstart, that), 39424 drag: proxy(that.drag, that), 39425 dragend: proxy(that.dragend, that) 39426 }); 39427 } 39428 39429 WindowResizing.prototype = /** @ignore */ { 39430 dragstart: function (e) { 39431 var that = this, 39432 wnd = that.owner, 39433 wrapper = wnd.wrapper; 39434 39435 that.elementPadding = parseInt(wnd.wrapper.css("padding-top"), 10); 39436 that.initialCursorPosition = wrapper.offset(); 39437 39438 that.resizeDirection = e.currentTarget.prop("className").replace("k-resize-handle k-resize-", "").split(""); 39439 39440 that.initialSize = { 39441 width: wrapper.width(), 39442 height: wrapper.height() 39443 }; 39444 39445 that.containerOffset = wnd.appendTo.offset(), 39446 39447 wrapper 39448 .append(templates.overlay) 39449 .find(KWINDOWRESIZEHANDLES).not(e.currentTarget).hide(); 39450 39451 $(BODY).css(CURSOR, e.currentTarget.css(CURSOR)); 39452 }, 39453 drag: function (e) { 39454 var that = this, 39455 wnd = that.owner, 39456 wrapper = wnd.wrapper, 39457 options = wnd.options, 39458 direction = that.resizeDirection, 39459 containerOffset = that.containerOffset, 39460 initialPosition = that.initialCursorPosition, 39461 initialSize = that.initialSize, 39462 newWidth, newHeight, 39463 windowBottom, windowRight, 39464 x = e.x.location, 39465 y = e.y.location; 39466 39467 if (direction.indexOf("e") >= 0) { 39468 newWidth = x - initialPosition.left; 39469 39470 wrapper.width(constrain(newWidth, options.minWidth, options.maxWidth)); 39471 } else if (direction.indexOf("w") >= 0) { 39472 windowRight = initialPosition.left + initialSize.width; 39473 newWidth = constrain(windowRight - x, options.minWidth, options.maxWidth); 39474 39475 wrapper.css({ 39476 left: windowRight - newWidth - containerOffset.left, 39477 width: newWidth 39478 }); 39479 } 39480 39481 if (direction.indexOf("s") >= 0) { 39482 newHeight = y - initialPosition.top - that.elementPadding; 39483 39484 wrapper.height(constrain(newHeight, options.minHeight, options.maxHeight)); 39485 } else if (direction.indexOf("n") >= 0) { 39486 windowBottom = initialPosition.top + initialSize.height; 39487 newHeight = constrain(windowBottom - y, options.minHeight, options.maxHeight); 39488 39489 wrapper.css({ 39490 top: windowBottom - newHeight - containerOffset.top, 39491 height: newHeight 39492 }); 39493 } 39494 39495 wnd.trigger(RESIZE); 39496 }, 39497 dragend: function (e) { 39498 var that = this, 39499 wnd = that.owner,
39500 wrapper = wnd.wrapper; 39501 39502 wrapper 39503 .find(KOVERLAY).remove().end() 39504 .find(KWINDOWRESIZEHANDLES).not(e.currentTarget).show(); 39505 39506 $(BODY).css(CURSOR, ""); 39507 39508 if (wnd.touchScroller) { 39509 wnd.touchScroller.reset(); 39510 } 39511 if (e.keyCode == 27) { 39512 wrapper.css(that.initialCursorPosition) 39513 .css(that.initialSize); 39514 } 39515 39516 return false; 39517 } 39518 }; 39519 39520 function WindowDragging(wnd) { 39521 var that = this; 39522 39523 that.owner = wnd; 39524 that._draggable = new Draggable(wnd.wrapper, { 39525 filter: KWINDOWTITLEBAR, 39526 group: wnd.wrapper.id + "-moving", 39527 dragstart: proxy(that.dragstart, that), 39528 drag: proxy(that.drag, that), 39529 dragend: proxy(that.dragend, that), 39530 dragcancel: proxy(that.dragcancel, that) 39531 }); 39532 } 39533 39534 WindowDragging.prototype = /** @ignore */{ 39535 dragstart: function (e) { 39536 var wnd = this.owner, 39537 element = wnd.element, 39538 actions = element.find(".k-window-actions"), 39539 containerOffset = wnd.appendTo.offset(); 39540 39541 wnd.trigger(DRAGSTART); 39542 39543 wnd.initialWindowPosition = wnd.wrapper.position(); 39544 39545 wnd.startPosition = { 39546 left: e.x.client - wnd.initialWindowPosition.left, 39547 top: e.y.client - wnd.initialWindowPosition.top 39548 }; 39549 39550 if (actions.length > 0) { 39551 wnd.minLeftPosition = actions.outerWidth() + parseInt(actions.css("right"), 10) - element.outerWidth(); 39552 } else { 39553 wnd.minLeftPosition = 20 - element.outerWidth(); // at least 20px remain visible 39554 } 39555 39556 wnd.minLeftPosition -= containerOffset.left; 39557 wnd.minTopPosition = -containerOffset.top; 39558 39559 wnd.wrapper 39560 .append(templates.overlay) 39561 .find(KWINDOWRESIZEHANDLES).hide(); 39562 39563 $(BODY).css(CURSOR, e.currentTarget.css(CURSOR)); 39564 }, 39565 39566 drag: function (e) { 39567 var wnd = this.owner, 39568 coordinates = { 39569 left: Math.max(e.x.client - wnd.startPosition.left, wnd.minLeftPosition), 39570 top: Math.max(e.y.client - wnd.startPosition.top, wnd.minTopPosition) 39571 }; 39572 39573 $(wnd.wrapper).css(coordinates); 39574 }, 39575 39576 dragcancel: function (e) { 39577 var wnd = this.owner; 39578 39579 wnd.wrapper 39580 .find(KWINDOWRESIZEHANDLES).show().end() 39581 .find(KOVERLAY).remove(); 39582 39583 $(BODY).css(CURSOR, ""); 39584 39585 e.currentTarget.closest(KWINDOW).css(wnd.initialWindowPosition); 39586 }, 39587 39588 dragend: function (e) { 39589 var wnd = this.owner; 39590 39591 wnd.wrapper 39592 .find(KWINDOWRESIZEHANDLES).show().end() 39593 .find(KOVERLAY).remove(); 39594 39595 $(BODY).css(CURSOR, ""); 39596 39597 wnd.trigger(DRAGEND); 39598 39599 return false; 39600 } 39601 }; 39602 39603 kendo.ui.plugin(Window); 39604 39605})(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.