1/* globals jQuery */ 2 3(function ($) { 4 // Selector to select only not already processed elements 5 $.expr[":"].notmdproc = function (obj) { 6 if ($(obj).data("mdproc")) { 7 return false; 8 } else { 9 return true; 10 } 11 }; 12 13 function _isChar(evt) { 14 if (typeof evt.which == "undefined") { 15 return true; 16 } else if (typeof evt.which == "number" && evt.which > 0) { 17 return ( 18 !evt.ctrlKey 19 && !evt.metaKey 20 && !evt.altKey 21 && evt.which != 8 // backspace 22 && evt.which != 9 // tab 23 && evt.which != 13 // enter 24 && evt.which != 16 // shift 25 && evt.which != 17 // ctrl 26 && evt.which != 20 // caps lock 27 && evt.which != 27 // escape 28 ); 29 } 30 return false; 31 } 32 33 function _addFormGroupFocus(element) { 34 var $element = $(element); 35 if (!$element.prop('disabled')) { // this is showing as undefined on chrome but works fine on firefox?? 36 $element.closest(".form-group").addClass("is-focused"); 37 } 38 } 39 40 function _toggleTypeFocus($input) { 41 $input.closest('label').hover(function () { 42 var $i = $(this).find('input'); 43 if (!$i.prop('disabled')) { // hack because the _addFormGroupFocus() wasn't identifying the property on chrome 44 _addFormGroupFocus($i); // need to find the input so we can check disablement 45 } 46 }, function () { 47 _removeFormGroupFocus($(this).find('input')); 48 }); 49 } 50 51 function _removeFormGroupFocus(element) { 52 $(element).closest(".form-group").removeClass("is-focused"); // remove class from form-group 53 } 54 55 $.material = { 56 "options": { 57 // These options set what will be started by $.material.init() 58 "validate": true, 59 "input": true, 60 "ripples": true, 61 "checkbox": true, 62 "togglebutton": true, 63 "radio": true, 64 "arrive": true, 65 "autofill": false, 66 67 "withRipples": [ 68 ".btn:not(.btn-link)", 69 ".card-image", 70 ".navbar a:not(.withoutripple)", 71 ".dropdown-menu a", 72 ".nav-tabs a:not(.withoutripple)", 73 ".withripple", 74 ".pagination li:not(.active):not(.disabled) a:not(.withoutripple)" 75 ].join(","), 76 "inputElements": "input.form-control, textarea.form-control, select.form-control", 77 "checkboxElements": ".checkbox > label > input[type=checkbox]", 78 "togglebuttonElements": ".togglebutton > label > input[type=checkbox]", 79 "radioElements": ".radio > label > input[type=radio]" 80 }, 81 "checkbox": function (selector) { 82 // Add fake-checkbox to material checkboxes 83 var $input = $((selector) ? selector : this.options.checkboxElements) 84 .filter(":notmdproc") 85 .data("mdproc", true) 86 .after("<span class='checkbox-material'><span class='check'></span></span>"); 87 88 _toggleTypeFocus($input); 89 }, 90 "togglebutton": function (selector) { 91 // Add fake-checkbox to material checkboxes 92 var $input = $((selector) ? selector : this.options.togglebuttonElements) 93 .filter(":notmdproc") 94 .data("mdproc", true) 95 .after("<span class='toggle'></span>"); 96 97 _toggleTypeFocus($input); 98 }, 99 "radio": function (selector) { 100 // Add fake-radio to material radios 101 var $input = $((selector) ? selector : this.options.radioElements) 102 .filter(":notmdproc") 103 .data("mdproc", true) 104 .after("<span class='circle'></span><span class='check'></span>"); 105 106 _toggleTypeFocus($input); 107 }, 108 "input": function (selector) { 109 $((selector) ? selector : this.options.inputElements) 110 .filter(":notmdproc") 111 .data("mdproc", true) 112 .each(function () { 113 var $input = $(this); 114 115 // Requires form-group standard markup (will add it if necessary) 116 var $formGroup = $input.closest(".form-group"); // note that form-group may be grandparent in the case of an input-group 117 if ($formGroup.length === 0) { 118 $input.wrap("<div class='form-group'></div>"); 119 $formGroup = $input.closest(".form-group"); // find node after attached (otherwise additional attachments don't work) 120 } 121 122 // Legacy - Add hint label if using the old shorthand data-hint attribute on the input 123 if ($input.attr("data-hint")) { 124 $input.after("<p class='help-block'>" + $input.attr("data-hint") + "</p>"); 125 $input.removeAttr("data-hint"); 126 } 127 128 // Legacy - Change input-sm/lg to form-group-sm/lg instead (preferred standard and simpler css/less variants) 129 var legacySizes = { 130 "input-lg": "form-group-lg", 131 "input-sm": "form-group-sm" 132 }; 133 $.each(legacySizes, function (legacySize, standardSize) { 134 if ($input.hasClass(legacySize)) { 135 $input.removeClass(legacySize); 136 $formGroup.addClass(standardSize); 137 } 138 }); 139 140 // Legacy - Add label-floating if using old shorthand <input class="floating-label" placeholder="foo"> 141 if ($input.hasClass("floating-label")) { 142 var placeholder = $input.attr("placeholder"); 143 $input.attr("placeholder", null).removeClass("floating-label"); 144 var id = $input.attr("id"); 145 var forAttribute = ""; 146 if (id) { 147 forAttribute = "for='" + id + "'"; 148 } 149 $formGroup.addClass("label-floating"); 150 $input.after("<label " + forAttribute + "class='control-label'>" + placeholder + "</label>"); 151 } 152 153 // Set as empty if is empty (damn I must improve this...) 154 if ($input.val() === null || $input.val() == "undefined" || $input.val() === "") { 155 $formGroup.addClass("is-empty"); 156 } 157 158 // Add at the end of the form-group 159 $formGroup.append("<span class='material-input'></span>"); 160
161 // Support for file input 162 if ($formGroup.find("input[type=file]").length > 0) { 163 $formGroup.addClass("is-fileinput"); 164 } 165 }); 166 }, 167 "attachInputEventHandlers": function () { 168 var validate = this.options.validate; 169 170 $(document) 171 .on("change", ".checkbox input[type=checkbox]", function () { 172 $(this).blur(); 173 }) 174 .on("keydown paste", ".form-control", function (e) { 175 if (_isChar(e)) { 176 $(this).closest(".form-group").removeClass("is-empty"); 177 } 178 }) 179 .on("keyup change", ".form-control", function () { 180 var $input = $(this); 181 var $formGroup = $input.closest(".form-group"); 182 var isValid = (typeof $input[0].checkValidity === "undefined" || $input[0].checkValidity()); 183 184 if ($input.val() === "") { 185 $formGroup.addClass("is-empty"); 186 } 187 else { 188 $formGroup.removeClass("is-empty"); 189 } 190 191 // Validation events do not bubble, so they must be attached directly to the input: http://jsfiddle.net/PEpRM/1/ 192 // Further, even the bind method is being caught, but since we are already calling #checkValidity here, just alter 193 // the form-group on change. 194 // 195 // NOTE: I'm not sure we should be intervening regarding validation, this seems better as a README and snippet of code. 196 // BUT, I've left it here for backwards compatibility. 197 if (validate) { 198 if (isValid) { 199 $formGroup.removeClass("has-error"); 200 } 201 else { 202 $formGroup.addClass("has-error"); 203 } 204 } 205 }) 206 .on("focus", ".form-control, .form-group.is-fileinput", function () { 207 _addFormGroupFocus(this); 208 }) 209 .on("blur", ".form-control, .form-group.is-fileinput", function () { 210 _removeFormGroupFocus(this); 211 }) 212 // make sure empty is added back when there is a programmatic value change. 213 // NOTE: programmatic changing of value using $.val() must trigger the change event i.e. $.val('x').trigger('change') 214 .on("change", ".form-group input", function () { 215 var $input = $(this); 216 if ($input.attr("type") == "file") { 217 return; 218 } 219 220 var $formGroup = $input.closest(".form-group"); 221 var value = $input.val(); 222 if (value) { 223 $formGroup.removeClass("is-empty"); 224 } else { 225 $formGroup.addClass("is-empty"); 226 } 227 }) 228 // set the fileinput readonly field with the name of the file 229 .on("change", ".form-group.is-fileinput input[type='file']", function () { 230 var $input = $(this); 231 var $formGroup = $input.closest(".form-group"); 232 var value = ""; 233 $.each(this.files, function (i, file) { 234 value += file.name + ", "; 235 }); 236 value = value.substring(0, value.length - 2); 237 if (value) { 238 $formGroup.removeClass("is-empty"); 239 } else { 240 $formGroup.addClass("is-empty"); 241 } 242 $formGroup.find("input.form-control[readonly]").val(value); 243 }); 244 }, 245 "ripples": function (selector) { 246 $((selector) ? selector : this.options.withRipples).ripples(); 247 }, 248 "autofill": function () { 249 // This part of code will detect autofill when the page is loading (username and password inputs for example) 250 var loading = setInterval(function () { 251 $("input[type!=checkbox]").each(function () { 252 var $this = $(this); 253 if ($this.val() && $this.val() !== $this.attr("value")) { 254 $this.trigger("change"); 255 } 256 }); 257 }, 100); 258 259 // After 10 seconds we are quite sure all the needed inputs are autofilled then we can stop checking them 260 setTimeout(function () { 261 clearInterval(loading); 262 }, 10000); 263 }, 264 "attachAutofillEventHandlers": function () { 265 // Listen on inputs of the focused form (because user can select from the autofill dropdown only when the input has focus) 266 var focused; 267 $(document) 268 .on("focus", "input", function () {
269 var $inputs = $(this).parents("form").find("input").not("[type=file]"); 270 focused = setInterval(function () { 271 $inputs.each(function () { 272 var $this = $(this); 273 if ($this.val() !== $this.attr("value")) { 274 $this.trigger("change"); 275 } 276 }); 277 }, 100); 278 }) 279 .on("blur", ".form-group input", function () { 280 clearInterval(focused); 281 }); 282 }, 283 "init": function (options) { 284 this.options = $.extend({}, this.options, options); 285 var $document = $(document); 286 287 if ($.fn.ripples && this.options.ripples) { 288 this.ripples(); 289 } 290 if (this.options.input) { 291 this.input(); 292 this.attachInputEventHandlers(); 293 } 294 if (this.options.checkbox) { 295 this.checkbox(); 296 } 297 if (this.options.togglebutton) { 298 this.togglebutton(); 299 } 300 if (this.options.radio) { 301 this.radio(); 302 } 303 if (this.options.autofill) { 304 this.autofill(); 305 this.attachAutofillEventHandlers(); 306 } 307 308 if (document.arrive && this.options.arrive) { 309 if ($.fn.ripples && this.options.ripples) { 310 $document.arrive(this.options.withRipples, function () { 311 $.material.ripples($(this)); 312 }); 313 } 314 if (this.options.input) { 315 $document.arrive(this.options.inputElements, function () { 316 $.material.input($(this)); 317 }); 318 } 319 if (this.options.checkbox) { 320 $document.arrive(this.options.checkboxElements, function () { 321 $.material.checkbox($(this)); 322 }); 323 } 324 if (this.options.radio) { 325 $document.arrive(this.options.radioElements, function () { 326 $.material.radio($(this)); 327 }); 328 } 329 if (this.options.togglebutton) { 330 $document.arrive(this.options.togglebuttonElements, function () { 331 $.material.togglebutton($(this)); 332 }); 333 } 334 335 } 336 } 337 }; 338 339})(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.