Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

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