006.jquery.uniform.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. /*
  2. Uniform v2.1.2
  3. Copyright © 2009 Josh Pyles / Pixelmatrix Design LLC
  4. http://pixelmatrixdesign.com
  5. Requires jQuery 1.3 or newer
  6. Much thanks to Thomas Reynolds and Buck Wilson for their help and advice on
  7. this.
  8. Disabling text selection is made possible by Mathias Bynens
  9. <http://mathiasbynens.be/> and his noSelect plugin.
  10. <https://github.com/mathiasbynens/jquery-noselect>, which is embedded.
  11. Also, thanks to David Kaneda and Eugene Bond for their contributions to the
  12. plugin.
  13. Tyler Akins has also rewritten chunks of the plugin, helped close many issues,
  14. and ensured version 2 got out the door.
  15. License:
  16. MIT License - http://www.opensource.org/licenses/mit-license.php
  17. Enjoy!
  18. */
  19. /*global jQuery, document, navigator*/
  20. (function (wind, $, undef) {
  21. "use strict";
  22. /**
  23. * Use .prop() if jQuery supports it, otherwise fall back to .attr()
  24. *
  25. * @param jQuery $el jQuery'd element on which we're calling attr/prop
  26. * @param ... All other parameters are passed to jQuery's function
  27. * @return The result from jQuery
  28. */
  29. function attrOrProp($el) {
  30. var args = Array.prototype.slice.call(arguments, 1);
  31. if ($el.prop) {
  32. // jQuery 1.6+
  33. return $el.prop.apply($el, args);
  34. }
  35. // jQuery 1.5 and below
  36. return $el.attr.apply($el, args);
  37. }
  38. /**
  39. * For backwards compatibility with older jQuery libraries, only bind
  40. * one thing at a time. Also, this function adds our namespace to
  41. * events in one consistent location, shrinking the minified code.
  42. *
  43. * The properties on the events object are the names of the events
  44. * that we are supposed to add to. It can be a space separated list.
  45. * The namespace will be added automatically.
  46. *
  47. * @param jQuery $el
  48. * @param Object options Uniform options for this element
  49. * @param Object events Events to bind, properties are event names
  50. */
  51. function bindMany($el, options, events) {
  52. var name, namespaced;
  53. for (name in events) {
  54. if (events.hasOwnProperty(name)) {
  55. namespaced = name.replace(/ |$/g, options.eventNamespace);
  56. $el.bind(namespaced, events[name]);
  57. }
  58. }
  59. }
  60. /**
  61. * Bind the hover, active, focus, and blur UI updates
  62. *
  63. * @param jQuery $el Original element
  64. * @param jQuery $target Target for the events (our div/span)
  65. * @param Object options Uniform options for the element $target
  66. */
  67. function bindUi($el, $target, options) {
  68. bindMany($el, options, {
  69. focus: function () {
  70. $target.addClass(options.focusClass);
  71. },
  72. blur: function () {
  73. $target.removeClass(options.focusClass);
  74. $target.removeClass(options.activeClass);
  75. },
  76. mouseenter: function () {
  77. $target.addClass(options.hoverClass);
  78. },
  79. mouseleave: function () {
  80. $target.removeClass(options.hoverClass);
  81. $target.removeClass(options.activeClass);
  82. },
  83. "mousedown touchbegin": function () {
  84. if (!$el.is(":disabled")) {
  85. $target.addClass(options.activeClass);
  86. }
  87. },
  88. "mouseup touchend": function () {
  89. $target.removeClass(options.activeClass);
  90. }
  91. });
  92. }
  93. /**
  94. * Remove the hover, focus, active classes.
  95. *
  96. * @param jQuery $el Element with classes
  97. * @param Object options Uniform options for the element
  98. */
  99. function classClearStandard($el, options) {
  100. $el.removeClass(options.hoverClass + " " + options.focusClass + " " + options.activeClass);
  101. }
  102. /**
  103. * Add or remove a class, depending on if it's "enabled"
  104. *
  105. * @param jQuery $el Element that has the class added/removed
  106. * @param String className Class or classes to add/remove
  107. * @param Boolean enabled True to add the class, false to remove
  108. */
  109. function classUpdate($el, className, enabled) {
  110. if (enabled) {
  111. $el.addClass(className);
  112. } else {
  113. $el.removeClass(className);
  114. }
  115. }
  116. /**
  117. * Updating the "checked" property can be a little tricky. This
  118. * changed in jQuery 1.6 and now we can pass booleans to .prop().
  119. * Prior to that, one either adds an attribute ("checked=checked") or
  120. * removes the attribute.
  121. *
  122. * @param jQuery $tag Our Uniform span/div
  123. * @param jQuery $el Original form element
  124. * @param Object options Uniform options for this element
  125. */
  126. function classUpdateChecked($tag, $el, options) {
  127. setTimeout(function() { // sunhater@sunhater.com
  128. var c = "checked",
  129. isChecked = $el.is(":" + c);
  130. if ($el.prop) {
  131. // jQuery 1.6+
  132. $el.prop(c, isChecked);
  133. } else {
  134. // jQuery 1.5 and below
  135. if (isChecked) {
  136. $el.attr(c, c);
  137. } else {
  138. $el.removeAttr(c);
  139. }
  140. }
  141. classUpdate($tag, options.checkedClass, isChecked);
  142. }, 1);
  143. }
  144. /**
  145. * Set or remove the "disabled" class for disabled elements, based on
  146. * if the element is detected to be disabled.
  147. *
  148. * @param jQuery $tag Our Uniform span/div
  149. * @param jQuery $el Original form element
  150. * @param Object options Uniform options for this element
  151. */
  152. function classUpdateDisabled($tag, $el, options) {
  153. classUpdate($tag, options.disabledClass, $el.is(":disabled"));
  154. }
  155. /**
  156. * Wrap an element inside of a container or put the container next
  157. * to the element. See the code for examples of the different methods.
  158. *
  159. * Returns the container that was added to the HTML.
  160. *
  161. * @param jQuery $el Element to wrap
  162. * @param jQuery $container Add this new container around/near $el
  163. * @param String method One of "after", "before" or "wrap"
  164. * @return $container after it has been cloned for adding to $el
  165. */
  166. function divSpanWrap($el, $container, method) {
  167. switch (method) {
  168. case "after":
  169. // Result: <element /> <container />
  170. $el.after($container);
  171. return $el.next();
  172. case "before":
  173. // Result: <container /> <element />
  174. $el.before($container);
  175. return $el.prev();
  176. case "wrap":
  177. // Result: <container> <element /> </container>
  178. $el.wrap($container);
  179. return $el.parent();
  180. }
  181. return null;
  182. }
  183. /**
  184. * Create a div/span combo for uniforming an element
  185. *
  186. * @param jQuery $el Element to wrap
  187. * @param Object options Options for the element, set by the user
  188. * @param Object divSpanConfig Options for how we wrap the div/span
  189. * @return Object Contains the div and span as properties
  190. */
  191. function divSpan($el, options, divSpanConfig) {
  192. var $div, $span, id;
  193. if (!divSpanConfig) {
  194. divSpanConfig = {};
  195. }
  196. divSpanConfig = $.extend({
  197. bind: {},
  198. divClass: null,
  199. divWrap: "wrap",
  200. spanClass: null,
  201. spanHtml: null,
  202. spanWrap: "wrap"
  203. }, divSpanConfig);
  204. $div = $('<div />');
  205. $span = $('<span />');
  206. // Automatically hide this div/span if the element is hidden.
  207. // Do not hide if the element is hidden because a parent is hidden.
  208. if (options.autoHide && $el.is(':hidden') && $el.css('display') === 'none') {
  209. $div.hide();
  210. }
  211. if (divSpanConfig.divClass) {
  212. $div.addClass(divSpanConfig.divClass);
  213. }
  214. if (options.wrapperClass) {
  215. $div.addClass(options.wrapperClass);
  216. }
  217. if (divSpanConfig.spanClass) {
  218. $span.addClass(divSpanConfig.spanClass);
  219. }
  220. id = attrOrProp($el, 'id');
  221. if (options.useID && id) {
  222. attrOrProp($div, 'id', options.idPrefix + '-' + id);
  223. }
  224. if (divSpanConfig.spanHtml) {
  225. $span.html(divSpanConfig.spanHtml);
  226. }
  227. $div = divSpanWrap($el, $div, divSpanConfig.divWrap);
  228. $span = divSpanWrap($el, $span, divSpanConfig.spanWrap);
  229. classUpdateDisabled($div, $el, options);
  230. return {
  231. div: $div,
  232. span: $span
  233. };
  234. }
  235. /**
  236. * Wrap an element with a span to apply a global wrapper class
  237. *
  238. * @param jQuery $el Element to wrap
  239. * @param object options
  240. * @return jQuery Wrapper element
  241. */
  242. function wrapWithWrapperClass($el, options) {
  243. var $span;
  244. if (!options.wrapperClass) {
  245. return null;
  246. }
  247. $span = $('<span />').addClass(options.wrapperClass);
  248. $span = divSpanWrap($el, $span, "wrap");
  249. return $span;
  250. }
  251. /**
  252. * Test if high contrast mode is enabled.
  253. *
  254. * In high contrast mode, background images can not be set and
  255. * they are always returned as 'none'.
  256. *
  257. * @return boolean True if in high contrast mode
  258. */
  259. function highContrast() {
  260. var c, $div, el, rgb;
  261. // High contrast mode deals with white and black
  262. rgb = 'rgb(120,2,153)';
  263. $div = $('<div style="width:0;height:0;color:' + rgb + '">');
  264. $('body').append($div);
  265. el = $div.get(0);
  266. // $div.css() will get the style definition, not
  267. // the actually displaying style
  268. if (wind.getComputedStyle) {
  269. c = wind.getComputedStyle(el, '').color;
  270. } else {
  271. c = (el.currentStyle || el.style || {}).color;
  272. }
  273. $div.remove();
  274. return c.replace(/ /g, '') !== rgb;
  275. }
  276. /**
  277. * Change text into safe HTML
  278. *
  279. * @param String text
  280. * @return String HTML version
  281. */
  282. function htmlify(text) {
  283. if (!text) {
  284. return "";
  285. }
  286. return $('<span />').text(text).html();
  287. }
  288. /**
  289. * If not MSIE, return false.
  290. * If it is, return the version number.
  291. *
  292. * @return false|number
  293. */
  294. function isMsie() {
  295. return navigator.cpuClass && !navigator.product;
  296. }
  297. /**
  298. * Return true if this version of IE allows styling
  299. *
  300. * @return boolean
  301. */
  302. function isMsieSevenOrNewer() {
  303. if (wind.XMLHttpRequest !== undefined) {
  304. return true;
  305. }
  306. return false;
  307. }
  308. /**
  309. * Test if the element is a multiselect
  310. *
  311. * @param jQuery $el Element
  312. * @return boolean true/false
  313. */
  314. function isMultiselect($el) {
  315. var elSize;
  316. if ($el[0].multiple) {
  317. return true;
  318. }
  319. elSize = attrOrProp($el, "size");
  320. if (!elSize || elSize <= 1) {
  321. return false;
  322. }
  323. return true;
  324. }
  325. /**
  326. * Meaningless utility function. Used mostly for improving minification.
  327. *
  328. * @return false
  329. */
  330. function returnFalse() {
  331. return false;
  332. }
  333. /**
  334. * noSelect plugin, very slightly modified
  335. * http://mths.be/noselect v1.0.3
  336. *
  337. * @param jQuery $elem Element that we don't want to select
  338. * @param Object options Uniform options for the element
  339. */
  340. function noSelect($elem, options) {
  341. var none = 'none';
  342. bindMany($elem, options, {
  343. 'selectstart dragstart mousedown': returnFalse
  344. });
  345. $elem.css({
  346. MozUserSelect: none,
  347. msUserSelect: none,
  348. webkitUserSelect: none,
  349. userSelect: none
  350. });
  351. }
  352. /**
  353. * Updates the filename tag based on the value of the real input
  354. * element.
  355. *
  356. * @param jQuery $el Actual form element
  357. * @param jQuery $filenameTag Span/div to update
  358. * @param Object options Uniform options for this element
  359. */
  360. function setFilename($el, $filenameTag, options) {
  361. var filename = $el.val();
  362. if (filename === "") {
  363. filename = options.fileDefaultHtml;
  364. } else {
  365. filename = filename.split(/[\/\\]+/);
  366. filename = filename[(filename.length - 1)];
  367. }
  368. $filenameTag.text(filename);
  369. }
  370. /**
  371. * Function from jQuery to swap some CSS values, run a callback,
  372. * then restore the CSS. Modified to pass JSLint and handle undefined
  373. * values with 'use strict'.
  374. *
  375. * @param jQuery $el Element
  376. * @param object newCss CSS values to swap out
  377. * @param Function callback Function to run
  378. */
  379. function swap($elements, newCss, callback) {
  380. var restore, item;
  381. restore = [];
  382. $elements.each(function () {
  383. var name;
  384. for (name in newCss) {
  385. if (Object.prototype.hasOwnProperty.call(newCss, name)) {
  386. restore.push({
  387. el: this,
  388. name: name,
  389. old: this.style[name]
  390. });
  391. this.style[name] = newCss[name];
  392. }
  393. }
  394. });
  395. callback();
  396. while (restore.length) {
  397. item = restore.pop();
  398. item.el.style[item.name] = item.old;
  399. }
  400. }
  401. /**
  402. * The browser doesn't provide sizes of elements that are not visible.
  403. * This will clone an element and add it to the DOM for calculations.
  404. *
  405. * @param jQuery $el
  406. * @param String method
  407. */
  408. function sizingInvisible($el, callback) {
  409. var targets;
  410. // We wish to target ourselves and any parents as long as
  411. // they are not visible
  412. targets = $el.parents();
  413. targets.push($el[0]);
  414. targets = targets.not(':visible');
  415. swap(targets, {
  416. visibility: "hidden",
  417. display: "block",
  418. position: "absolute"
  419. }, callback);
  420. }
  421. /**
  422. * Standard way to unwrap the div/span combination from an element
  423. *
  424. * @param jQuery $el Element that we wish to preserve
  425. * @param Object options Uniform options for the element
  426. * @return Function This generated function will perform the given work
  427. */
  428. function unwrapUnwrapUnbindFunction($el, options) {
  429. return function () {
  430. $el.unwrap().unwrap().unbind(options.eventNamespace);
  431. };
  432. }
  433. var allowStyling = true, // False if IE6 or other unsupported browsers
  434. highContrastTest = false, // Was the high contrast test ran?
  435. uniformHandlers = [ // Objects that take care of "unification"
  436. {
  437. // Buttons
  438. match: function ($el) {
  439. return $el.is("a, button, :submit, :reset, input[type='button']");
  440. },
  441. apply: function ($el, options) {
  442. var $div, defaultSpanHtml, ds, getHtml, doingClickEvent;
  443. defaultSpanHtml = options.submitDefaultHtml;
  444. if ($el.is(":reset")) {
  445. defaultSpanHtml = options.resetDefaultHtml;
  446. }
  447. if ($el.is("a, button")) {
  448. // Use the HTML inside the tag
  449. getHtml = function () {
  450. return $el.html() || defaultSpanHtml;
  451. };
  452. } else {
  453. // Use the value property of the element
  454. getHtml = function () {
  455. return htmlify(attrOrProp($el, "value")) || defaultSpanHtml;
  456. };
  457. }
  458. ds = divSpan($el, options, {
  459. divClass: options.buttonClass,
  460. spanHtml: getHtml()
  461. });
  462. $div = ds.div;
  463. bindUi($el, $div, options);
  464. doingClickEvent = false;
  465. bindMany($div, options, {
  466. "click touchend": function () {
  467. var ev, res, target, href;
  468. if (doingClickEvent) {
  469. return;
  470. }
  471. if ($el.is(':disabled')) {
  472. return;
  473. }
  474. doingClickEvent = true;
  475. if ($el[0].dispatchEvent) {
  476. ev = document.createEvent("MouseEvents");
  477. ev.initEvent("click", true, true);
  478. res = $el[0].dispatchEvent(ev);
  479. if ($el.is('a') && res) {
  480. target = attrOrProp($el, 'target');
  481. href = attrOrProp($el, 'href');
  482. if (!target || target === '_self') {
  483. document.location.href = href;
  484. } else {
  485. wind.open(href, target);
  486. }
  487. }
  488. } else {
  489. $el.click();
  490. }
  491. doingClickEvent = false;
  492. }
  493. });
  494. noSelect($div, options);
  495. return {
  496. remove: function () {
  497. // Move $el out
  498. $div.after($el);
  499. // Remove div and span
  500. $div.remove();
  501. // Unbind events
  502. $el.unbind(options.eventNamespace);
  503. return $el;
  504. },
  505. update: function () {
  506. classClearStandard($div, options);
  507. classUpdateDisabled($div, $el, options);
  508. $el.detach();
  509. ds.span.html(getHtml()).append($el);
  510. }
  511. };
  512. }
  513. },
  514. {
  515. // Checkboxes
  516. match: function ($el) {
  517. return $el.is(":checkbox");
  518. },
  519. apply: function ($el, options) {
  520. var ds, $div, $span;
  521. ds = divSpan($el, options, {
  522. divClass: options.checkboxClass
  523. });
  524. $div = ds.div;
  525. $span = ds.span;
  526. // Add focus classes, toggling, active, etc.
  527. bindUi($el, $div, options);
  528. bindMany($el, options, {
  529. "click touchend": function () {
  530. classUpdateChecked($span, $el, options);
  531. }
  532. });
  533. classUpdateChecked($span, $el, options);
  534. return {
  535. remove: unwrapUnwrapUnbindFunction($el, options),
  536. update: function () {
  537. classClearStandard($div, options);
  538. $span.removeClass(options.checkedClass);
  539. classUpdateChecked($span, $el, options);
  540. classUpdateDisabled($div, $el, options);
  541. }
  542. };
  543. }
  544. },
  545. {
  546. // File selection / uploads
  547. match: function ($el) {
  548. return $el.is(":file");
  549. },
  550. apply: function ($el, options) {
  551. var ds, $div, $filename, $button;
  552. // The "span" is the button
  553. ds = divSpan($el, options, {
  554. divClass: options.fileClass,
  555. spanClass: options.fileButtonClass,
  556. spanHtml: options.fileButtonHtml,
  557. spanWrap: "after"
  558. });
  559. $div = ds.div;
  560. $button = ds.span;
  561. $filename = $("<span />").html(options.fileDefaultHtml);
  562. $filename.addClass(options.filenameClass);
  563. $filename = divSpanWrap($el, $filename, "after");
  564. // Set the size
  565. if (!attrOrProp($el, "size")) {
  566. attrOrProp($el, "size", $div.width() / 10);
  567. }
  568. // Actions
  569. function filenameUpdate() {
  570. setFilename($el, $filename, options);
  571. }
  572. bindUi($el, $div, options);
  573. // Account for input saved across refreshes
  574. filenameUpdate();
  575. // IE7 doesn't fire onChange until blur or second fire.
  576. if (isMsie()) {
  577. // IE considers browser chrome blocking I/O, so it
  578. // suspends tiemouts until after the file has
  579. // been selected.
  580. bindMany($el, options, {
  581. click: function () {
  582. $el.trigger("change");
  583. setTimeout(filenameUpdate, 0);
  584. }
  585. });
  586. } else {
  587. // All other browsers behave properly
  588. bindMany($el, options, {
  589. change: filenameUpdate
  590. });
  591. }
  592. noSelect($filename, options);
  593. noSelect($button, options);
  594. return {
  595. remove: function () {
  596. // Remove filename and button
  597. $filename.remove();
  598. $button.remove();
  599. // Unwrap parent div, remove events
  600. return $el.unwrap().unbind(options.eventNamespace);
  601. },
  602. update: function () {
  603. classClearStandard($div, options);
  604. setFilename($el, $filename, options);
  605. classUpdateDisabled($div, $el, options);
  606. }
  607. };
  608. }
  609. },
  610. {
  611. // Input fields (text)
  612. match: function ($el) {
  613. if ($el.is("input")) {
  614. var t = (" " + attrOrProp($el, "type") + " ").toLowerCase(),
  615. allowed = " color date datetime datetime-local email month number password search tel text time url week ";
  616. return allowed.indexOf(t) >= 0;
  617. }
  618. return false;
  619. },
  620. apply: function ($el, options) {
  621. var elType, $wrapper;
  622. elType = attrOrProp($el, "type");
  623. $el.addClass(options.inputClass);
  624. $wrapper = wrapWithWrapperClass($el, options);
  625. bindUi($el, $el, options);
  626. if (options.inputAddTypeAsClass) {
  627. $el.addClass(elType);
  628. }
  629. return {
  630. remove: function () {
  631. $el.removeClass(options.inputClass);
  632. if (options.inputAddTypeAsClass) {
  633. $el.removeClass(elType);
  634. }
  635. if ($wrapper) {
  636. $el.unwrap();
  637. }
  638. },
  639. update: returnFalse
  640. };
  641. }
  642. },
  643. {
  644. // Radio buttons
  645. match: function ($el) {
  646. return $el.is(":radio");
  647. },
  648. apply: function ($el, options) {
  649. var ds, $div, $span;
  650. ds = divSpan($el, options, {
  651. divClass: options.radioClass
  652. });
  653. $div = ds.div;
  654. $span = ds.span;
  655. // Add classes for focus, handle active, checked
  656. bindUi($el, $div, options);
  657. bindMany($el, options, {
  658. "click touchend": function () {
  659. // Find all radios with the same name, then update
  660. // them with $.uniform.update() so the right
  661. // per-element options are used
  662. $.uniform.update($(':radio[name="' + attrOrProp($el, "name") + '"]'));
  663. }
  664. });
  665. classUpdateChecked($span, $el, options);
  666. return {
  667. remove: unwrapUnwrapUnbindFunction($el, options),
  668. update: function () {
  669. classClearStandard($div, options);
  670. classUpdateChecked($span, $el, options);
  671. classUpdateDisabled($div, $el, options);
  672. }
  673. };
  674. }
  675. },
  676. {
  677. // Select lists, but do not style multiselects here
  678. match: function ($el) {
  679. if ($el.is("select") && !isMultiselect($el)) {
  680. return true;
  681. }
  682. return false;
  683. },
  684. apply: function ($el, options) {
  685. var ds, $div, $span, origElemWidth;
  686. if (options.selectAutoWidth) {
  687. sizingInvisible($el, function () {
  688. origElemWidth = $el.width();
  689. });
  690. }
  691. ds = divSpan($el, options, {
  692. divClass: options.selectClass,
  693. spanHtml: ($el.find(":selected:first") || $el.find("option:first")).html(),
  694. spanWrap: "before"
  695. });
  696. $div = ds.div;
  697. $span = ds.span;
  698. if (options.selectAutoWidth) {
  699. // Use the width of the select and adjust the
  700. // span and div accordingly
  701. sizingInvisible($el, function () {
  702. // Force "display: block" - related to bug #287
  703. swap($([ $span[0], $div[0] ]), {
  704. display: "block"
  705. }, function () {
  706. var spanPad;
  707. spanPad = $span.outerWidth() - $span.width();
  708. $div.width(origElemWidth + spanPad);
  709. $span.width(origElemWidth);
  710. });
  711. });
  712. } else {
  713. // Force the select to fill the size of the div
  714. $div.addClass('fixedWidth');
  715. }
  716. // Take care of events
  717. bindUi($el, $div, options);
  718. bindMany($el, options, {
  719. change: function () {
  720. $span.html($el.find(":selected").html());
  721. $div.removeClass(options.activeClass);
  722. },
  723. "click touchend": function () {
  724. // IE7 and IE8 may not update the value right
  725. // until after click event - issue #238
  726. var selHtml = $el.find(":selected").html();
  727. if ($span.html() !== selHtml) {
  728. // Change was detected
  729. // Fire the change event on the select tag
  730. $el.trigger('change');
  731. }
  732. },
  733. keyup: function () {
  734. $span.html($el.find(":selected").html());
  735. }
  736. });
  737. noSelect($span, options);
  738. return {
  739. remove: function () {
  740. // Remove sibling span
  741. $span.remove();
  742. // Unwrap parent div
  743. $el.unwrap().unbind(options.eventNamespace);
  744. return $el;
  745. },
  746. update: function () {
  747. if (options.selectAutoWidth) {
  748. // Easier to remove and reapply formatting
  749. $.uniform.restore($el);
  750. $el.uniform(options);
  751. } else {
  752. classClearStandard($div, options);
  753. // Reset current selected text
  754. $span.html($el.find(":selected").html());
  755. classUpdateDisabled($div, $el, options);
  756. }
  757. }
  758. };
  759. }
  760. },
  761. {
  762. // Select lists - multiselect lists only
  763. match: function ($el) {
  764. if ($el.is("select") && isMultiselect($el)) {
  765. return true;
  766. }
  767. return false;
  768. },
  769. apply: function ($el, options) {
  770. var $wrapper;
  771. $el.addClass(options.selectMultiClass);
  772. $wrapper = wrapWithWrapperClass($el, options);
  773. bindUi($el, $el, options);
  774. return {
  775. remove: function () {
  776. $el.removeClass(options.selectMultiClass);
  777. if ($wrapper) {
  778. $el.unwrap();
  779. }
  780. },
  781. update: returnFalse
  782. };
  783. }
  784. },
  785. {
  786. // Textareas
  787. match: function ($el) {
  788. return $el.is("textarea");
  789. },
  790. apply: function ($el, options) {
  791. var $wrapper;
  792. $el.addClass(options.textareaClass);
  793. $wrapper = wrapWithWrapperClass($el, options);
  794. bindUi($el, $el, options);
  795. return {
  796. remove: function () {
  797. $el.removeClass(options.textareaClass);
  798. if ($wrapper) {
  799. $el.unwrap();
  800. }
  801. },
  802. update: returnFalse
  803. };
  804. }
  805. }
  806. ];
  807. // IE6 can't be styled - can't set opacity on select
  808. if (isMsie() && !isMsieSevenOrNewer()) {
  809. allowStyling = false;
  810. }
  811. $.uniform = {
  812. // Default options that can be overridden globally or when uniformed
  813. // globally: $.uniform.defaults.fileButtonHtml = "Pick A File";
  814. // on uniform: $('input').uniform({fileButtonHtml: "Pick a File"});
  815. defaults: {
  816. activeClass: "active",
  817. autoHide: true,
  818. buttonClass: "button",
  819. checkboxClass: "checker",
  820. checkedClass: "checked",
  821. disabledClass: "disabled",
  822. eventNamespace: ".uniform",
  823. fileButtonClass: "action",
  824. fileButtonHtml: "Choose File",
  825. fileClass: "uploader",
  826. fileDefaultHtml: "No file selected",
  827. filenameClass: "filename",
  828. focusClass: "focus",
  829. hoverClass: "hover",
  830. idPrefix: "uniform",
  831. inputAddTypeAsClass: true,
  832. inputClass: "uniform-input",
  833. radioClass: "radio",
  834. resetDefaultHtml: "Reset",
  835. resetSelector: false, // We'll use our own function when you don't specify one
  836. selectAutoWidth: true,
  837. selectClass: "selector",
  838. selectMultiClass: "uniform-multiselect",
  839. submitDefaultHtml: "Submit", // Only text allowed
  840. textareaClass: "uniform",
  841. useID: true,
  842. wrapperClass: null
  843. },
  844. // All uniformed elements - DOM objects
  845. elements: []
  846. };
  847. $.fn.uniform = function (options) {
  848. var el = this;
  849. options = $.extend({}, $.uniform.defaults, options);
  850. // If we are in high contrast mode, do not allow styling
  851. if (!highContrastTest) {
  852. highContrastTest = true;
  853. if (highContrast()) {
  854. allowStyling = false;
  855. }
  856. }
  857. // Only uniform on browsers that work
  858. if (!allowStyling) {
  859. return this;
  860. }
  861. // Code for specifying a reset button
  862. if (options.resetSelector) {
  863. $(options.resetSelector).mouseup(function () {
  864. wind.setTimeout(function () {
  865. $.uniform.update(el);
  866. }, 10);
  867. });
  868. }
  869. return this.each(function () {
  870. var $el = $(this), i, handler, callbacks;
  871. // Avoid uniforming elements already uniformed - just update
  872. if ($el.data("uniformed")) {
  873. $.uniform.update($el);
  874. return;
  875. }
  876. // See if we have any handler for this type of element
  877. for (i = 0; i < uniformHandlers.length; i = i + 1) {
  878. handler = uniformHandlers[i];
  879. if (handler.match($el, options)) {
  880. callbacks = handler.apply($el, options);
  881. $el.data("uniformed", callbacks);
  882. // Store element in our global array
  883. $.uniform.elements.push($el.get(0));
  884. return;
  885. }
  886. }
  887. // Could not style this element
  888. });
  889. };
  890. $.uniform.restore = $.fn.uniform.restore = function (elem) {
  891. if (elem === undef) {
  892. elem = $.uniform.elements;
  893. }
  894. $(elem).each(function () {
  895. var $el = $(this), index, elementData;
  896. elementData = $el.data("uniformed");
  897. // Skip elements that are not uniformed
  898. if (!elementData) {
  899. return;
  900. }
  901. // Unbind events, remove additional markup that was added
  902. elementData.remove();
  903. // Remove item from list of uniformed elements
  904. index = $.inArray(this, $.uniform.elements);
  905. if (index >= 0) {
  906. $.uniform.elements.splice(index, 1);
  907. }
  908. $el.removeData("uniformed");
  909. });
  910. };
  911. $.uniform.update = $.fn.uniform.update = function (elem) {
  912. if (elem === undef) {
  913. elem = $.uniform.elements;
  914. }
  915. $(elem).each(function () {
  916. var $el = $(this), elementData;
  917. elementData = $el.data("uniformed");
  918. // Skip elements that are not uniformed
  919. if (!elementData) {
  920. return;
  921. }
  922. elementData.update($el, elementData.options);
  923. });
  924. };
  925. }(this, jQuery));