~johnsca/charms/trusty/cloudfoundry/better-basic-reconciler-status

« back to all changes in this revision

Viewing changes to reconciler/ui/static/semantic/less/modules/behavior/state.js

  • Committer: Whit Morriss
  • Date: 2014-10-13 06:50:17 UTC
  • mto: (132.2.1 reconciler) (145.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 156.
  • Revision ID: whit.morriss@canonical.com-20141013065017-0feo2ku3yllymkol
reorg reconciler

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * # Semantic - State
 
3
 * http://github.com/jlukic/semantic-ui/
 
4
 *
 
5
 *
 
6
 * Copyright 2014 Contributors
 
7
 * Released under the MIT license
 
8
 * http://opensource.org/licenses/MIT
 
9
 *
 
10
 */
 
11
 
 
12
;(function ( $, window, document, undefined ) {
 
13
 
 
14
$.fn.state = function(parameters) {
 
15
  var
 
16
    $allModules     = $(this),
 
17
    settings        = $.extend(true, {}, $.fn.state.settings, parameters),
 
18
 
 
19
    moduleSelector  = $allModules.selector || '',
 
20
 
 
21
    time            = new Date().getTime(),
 
22
    performance     = [],
 
23
 
 
24
    query           = arguments[0],
 
25
    methodInvoked   = (typeof query == 'string'),
 
26
    queryArguments  = [].slice.call(arguments, 1),
 
27
 
 
28
    // shortcuts
 
29
    error         = settings.error,
 
30
    metadata      = settings.metadata,
 
31
    className     = settings.className,
 
32
    namespace     = settings.namespace,
 
33
    states        = settings.states,
 
34
    text          = settings.text,
 
35
 
 
36
    eventNamespace  = '.' + namespace,
 
37
    moduleNamespace = namespace + '-module',
 
38
 
 
39
 
 
40
    returnedValue
 
41
  ;
 
42
  $allModules
 
43
    .each(function() {
 
44
      var
 
45
        $module       = $(this),
 
46
 
 
47
        element       = this,
 
48
        instance      = $module.data(moduleNamespace),
 
49
 
 
50
        module
 
51
      ;
 
52
      module = {
 
53
 
 
54
        initialize: function() {
 
55
          module.verbose('Initializing module');
 
56
 
 
57
          // allow module to guess desired state based on element
 
58
          if(settings.automatic) {
 
59
            module.add.defaults();
 
60
          }
 
61
 
 
62
          // bind events with delegated events
 
63
          if(settings.context && moduleSelector !== '') {
 
64
            if( module.allows('hover') ) {
 
65
              $(element, settings.context)
 
66
                .on(moduleSelector, 'mouseenter' + eventNamespace, module.enable.hover)
 
67
                .on(moduleSelector, 'mouseleave' + eventNamespace, module.disable.hover)
 
68
              ;
 
69
            }
 
70
            if( module.allows('down') ) {
 
71
              $(element, settings.context)
 
72
                .on(moduleSelector, 'mousedown' + eventNamespace, module.enable.down)
 
73
                .on(moduleSelector, 'mouseup'   + eventNamespace, module.disable.down)
 
74
              ;
 
75
            }
 
76
            if( module.allows('focus') ) {
 
77
              $(element, settings.context)
 
78
                .on(moduleSelector, 'focus' + eventNamespace, module.enable.focus)
 
79
                .on(moduleSelector, 'blur'  + eventNamespace, module.disable.focus)
 
80
              ;
 
81
            }
 
82
            $(settings.context)
 
83
              .on(moduleSelector, 'mouseenter' + eventNamespace, module.change.text)
 
84
              .on(moduleSelector, 'mouseleave' + eventNamespace, module.reset.text)
 
85
              .on(moduleSelector, 'click'      + eventNamespace, module.toggle.state)
 
86
            ;
 
87
          }
 
88
          else {
 
89
            if( module.allows('hover') ) {
 
90
              $module
 
91
                .on('mouseenter' + eventNamespace, module.enable.hover)
 
92
                .on('mouseleave' + eventNamespace, module.disable.hover)
 
93
              ;
 
94
            }
 
95
            if( module.allows('down') ) {
 
96
              $module
 
97
                .on('mousedown' + eventNamespace, module.enable.down)
 
98
                .on('mouseup'   + eventNamespace, module.disable.down)
 
99
              ;
 
100
            }
 
101
            if( module.allows('focus') ) {
 
102
              $module
 
103
                .on('focus' + eventNamespace, module.enable.focus)
 
104
                .on('blur'  + eventNamespace, module.disable.focus)
 
105
              ;
 
106
            }
 
107
            $module
 
108
              .on('mouseenter' + eventNamespace, module.change.text)
 
109
              .on('mouseleave' + eventNamespace, module.reset.text)
 
110
              .on('click'      + eventNamespace, module.toggle.state)
 
111
            ;
 
112
          }
 
113
          module.instantiate();
 
114
        },
 
115
 
 
116
        instantiate: function() {
 
117
          module.verbose('Storing instance of module', module);
 
118
          instance = module;
 
119
          $module
 
120
            .data(moduleNamespace, module)
 
121
          ;
 
122
        },
 
123
 
 
124
        destroy: function() {
 
125
          module.verbose('Destroying previous module', instance);
 
126
          $module
 
127
            .off(eventNamespace)
 
128
            .removeData(moduleNamespace)
 
129
          ;
 
130
        },
 
131
 
 
132
        refresh: function() {
 
133
          module.verbose('Refreshing selector cache');
 
134
          $module = $(element);
 
135
        },
 
136
 
 
137
        add: {
 
138
          defaults: function() {
 
139
            var
 
140
              userStates = parameters && $.isPlainObject(parameters.states)
 
141
                ? parameters.states
 
142
                : {}
 
143
            ;
 
144
            $.each(settings.defaults, function(type, typeStates) {
 
145
              if( module.is[type] !== undefined && module.is[type]() ) {
 
146
                module.verbose('Adding default states', type, element);
 
147
                $.extend(settings.states, typeStates, userStates);
 
148
              }
 
149
            });
 
150
          }
 
151
        },
 
152
 
 
153
        is: {
 
154
 
 
155
          active: function() {
 
156
            return $module.hasClass(className.active);
 
157
          },
 
158
          loading: function() {
 
159
            return $module.hasClass(className.loading);
 
160
          },
 
161
          inactive: function() {
 
162
            return !( $module.hasClass(className.active) );
 
163
          },
 
164
 
 
165
          enabled: function() {
 
166
            return !( $module.is(settings.filter.active) );
 
167
          },
 
168
          disabled: function() {
 
169
            return ( $module.is(settings.filter.active) );
 
170
          },
 
171
          textEnabled: function() {
 
172
            return !( $module.is(settings.filter.text) );
 
173
          },
 
174
 
 
175
          // definitions for automatic type detection
 
176
          button: function() {
 
177
            return $module.is('.button:not(a, .submit)');
 
178
          },
 
179
          input: function() {
 
180
            return $module.is('input');
 
181
          }
 
182
        },
 
183
 
 
184
        allow: function(state) {
 
185
          module.debug('Now allowing state', state);
 
186
          states[state] = true;
 
187
        },
 
188
        disallow: function(state) {
 
189
          module.debug('No longer allowing', state);
 
190
          states[state] = false;
 
191
        },
 
192
 
 
193
        allows: function(state) {
 
194
          return states[state] || false;
 
195
        },
 
196
 
 
197
        enable: {
 
198
          state: function(state) {
 
199
            if(module.allows(state)) {
 
200
              $module.addClass( className[state] );
 
201
            }
 
202
          },
 
203
          // convenience
 
204
          focus: function() {
 
205
            $module.addClass(className.focus);
 
206
          },
 
207
          hover: function() {
 
208
            $module.addClass(className.hover);
 
209
          },
 
210
          down: function() {
 
211
            $module.addClass(className.down);
 
212
          },
 
213
        },
 
214
 
 
215
        disable: {
 
216
          state: function(state) {
 
217
            if(module.allows(state)) {
 
218
              $module.removeClass( className[state] );
 
219
            }
 
220
          },
 
221
          // convenience
 
222
          focus: function() {
 
223
            $module.removeClass(className.focus);
 
224
          },
 
225
          hover: function() {
 
226
            $module.removeClass(className.hover);
 
227
          },
 
228
          down: function() {
 
229
            $module.removeClass(className.down);
 
230
          },
 
231
        },
 
232
 
 
233
        toggle: {
 
234
          state: function() {
 
235
            var
 
236
              apiRequest = $module.data(metadata.promise)
 
237
            ;
 
238
            if( module.allows('active') && module.is.enabled() ) {
 
239
              module.refresh();
 
240
              if(apiRequest !== undefined) {
 
241
                module.listenTo(apiRequest);
 
242
              }
 
243
              else {
 
244
                module.change.state();
 
245
              }
 
246
            }
 
247
          }
 
248
        },
 
249
 
 
250
        listenTo: function(apiRequest) {
 
251
          module.debug('API request detected, waiting for state signal', apiRequest);
 
252
          if(apiRequest) {
 
253
            if(text.loading) {
 
254
              module.update.text(text.loading);
 
255
            }
 
256
            $.when(apiRequest)
 
257
              .then(function() {
 
258
                if(apiRequest.state() == 'resolved') {
 
259
                  module.debug('API request succeeded');
 
260
                  settings.activateTest   = function(){ return true; };
 
261
                  settings.deactivateTest = function(){ return true; };
 
262
                }
 
263
                else {
 
264
                  module.debug('API request failed');
 
265
                  settings.activateTest   = function(){ return false; };
 
266
                  settings.deactivateTest = function(){ return false; };
 
267
                }
 
268
                module.change.state();
 
269
              })
 
270
            ;
 
271
          }
 
272
          // xhr exists but set to false, beforeSend killed the xhr
 
273
          else {
 
274
            settings.activateTest   = function(){ return false; };
 
275
            settings.deactivateTest = function(){ return false; };
 
276
          }
 
277
        },
 
278
 
 
279
        // checks whether active/inactive state can be given
 
280
        change: {
 
281
 
 
282
          state: function() {
 
283
            module.debug('Determining state change direction');
 
284
            // inactive to active change
 
285
            if( module.is.inactive() ) {
 
286
              module.activate();
 
287
            }
 
288
            else {
 
289
              module.deactivate();
 
290
            }
 
291
            if(settings.sync) {
 
292
              module.sync();
 
293
            }
 
294
            $.proxy(settings.onChange, element)();
 
295
          },
 
296
 
 
297
          text: function() {
 
298
            if( module.is.textEnabled() ) {
 
299
              if( module.is.active() ) {
 
300
                if(text.hover) {
 
301
                  module.verbose('Changing text to hover text', text.hover);
 
302
                  module.update.text(text.hover);
 
303
                }
 
304
                else if(text.disable) {
 
305
                  module.verbose('Changing text to disable text', text.disable);
 
306
                  module.update.text(text.disable);
 
307
                }
 
308
              }
 
309
              else {
 
310
                if(text.hover) {
 
311
                  module.verbose('Changing text to hover text', text.disable);
 
312
                  module.update.text(text.hover);
 
313
                }
 
314
                else if(text.enable){
 
315
                  module.verbose('Changing text to enable text', text.enable);
 
316
                  module.update.text(text.enable);
 
317
                }
 
318
              }
 
319
            }
 
320
          }
 
321
 
 
322
        },
 
323
 
 
324
        activate: function() {
 
325
          if( $.proxy(settings.activateTest, element)() ) {
 
326
            module.debug('Setting state to active');
 
327
            $module
 
328
              .addClass(className.active)
 
329
            ;
 
330
            module.update.text(text.active);
 
331
          }
 
332
          $.proxy(settings.onActivate, element)();
 
333
        },
 
334
 
 
335
        deactivate: function() {
 
336
          if($.proxy(settings.deactivateTest, element)() ) {
 
337
            module.debug('Setting state to inactive');
 
338
            $module
 
339
              .removeClass(className.active)
 
340
            ;
 
341
            module.update.text(text.inactive);
 
342
          }
 
343
          $.proxy(settings.onDeactivate, element)();
 
344
        },
 
345
 
 
346
        sync: function() {
 
347
          module.verbose('Syncing other buttons to current state');
 
348
          if( module.is.active() ) {
 
349
            $allModules
 
350
              .not($module)
 
351
                .state('activate');
 
352
          }
 
353
          else {
 
354
            $allModules
 
355
              .not($module)
 
356
                .state('deactivate')
 
357
            ;
 
358
          }
 
359
        },
 
360
 
 
361
        get: {
 
362
          text: function() {
 
363
            return (settings.selector.text)
 
364
              ? $module.find(settings.selector.text).text()
 
365
              : $module.html()
 
366
            ;
 
367
          },
 
368
          textFor: function(state) {
 
369
            return text[state] || false;
 
370
          }
 
371
        },
 
372
 
 
373
        flash: {
 
374
          text: function(text, duration) {
 
375
            var
 
376
              previousText = module.get.text()
 
377
            ;
 
378
            module.debug('Flashing text message', text, duration);
 
379
            text     = text     || settings.text.flash;
 
380
            duration = duration || settings.flashDuration;
 
381
            module.update.text(text);
 
382
            setTimeout(function(){
 
383
              module.update.text(previousText);
 
384
            }, duration);
 
385
          }
 
386
        },
 
387
 
 
388
        reset: {
 
389
          // on mouseout sets text to previous value
 
390
          text: function() {
 
391
            var
 
392
              activeText   = text.active   || $module.data(metadata.storedText),
 
393
              inactiveText = text.inactive || $module.data(metadata.storedText)
 
394
            ;
 
395
            if( module.is.textEnabled() ) {
 
396
              if( module.is.active() && activeText) {
 
397
                module.verbose('Resetting active text', activeText);
 
398
                module.update.text(activeText);
 
399
              }
 
400
              else if(inactiveText) {
 
401
                module.verbose('Resetting inactive text', activeText);
 
402
                module.update.text(inactiveText);
 
403
              }
 
404
            }
 
405
          }
 
406
        },
 
407
 
 
408
        update: {
 
409
          text: function(text) {
 
410
            var
 
411
              currentText = module.get.text()
 
412
            ;
 
413
            if(text && text !== currentText) {
 
414
              module.debug('Updating text', text);
 
415
              if(settings.selector.text) {
 
416
                $module
 
417
                  .data(metadata.storedText, text)
 
418
                  .find(settings.selector.text)
 
419
                    .text(text)
 
420
                ;
 
421
              }
 
422
              else {
 
423
                $module
 
424
                  .data(metadata.storedText, text)
 
425
                  .html(text)
 
426
                ;
 
427
              }
 
428
            }
 
429
            else {
 
430
              module.debug('Text is already sane, ignoring update', text);
 
431
            }
 
432
          }
 
433
        },
 
434
 
 
435
        setting: function(name, value) {
 
436
          module.debug('Changing setting', name, value);
 
437
          if(value !== undefined) {
 
438
            if( $.isPlainObject(name) ) {
 
439
              $.extend(true, settings, name);
 
440
            }
 
441
            else {
 
442
              settings[name] = value;
 
443
            }
 
444
          }
 
445
          else {
 
446
            return settings[name];
 
447
          }
 
448
        },
 
449
        internal: function(name, value) {
 
450
          module.debug('Changing internal', name, value);
 
451
          if(value !== undefined) {
 
452
            if( $.isPlainObject(name) ) {
 
453
              $.extend(true, module, name);
 
454
            }
 
455
            else {
 
456
              module[name] = value;
 
457
            }
 
458
          }
 
459
          else {
 
460
            return module[name];
 
461
          }
 
462
        },
 
463
        debug: function() {
 
464
          if(settings.debug) {
 
465
            if(settings.performance) {
 
466
              module.performance.log(arguments);
 
467
            }
 
468
            else {
 
469
              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
 
470
              module.debug.apply(console, arguments);
 
471
            }
 
472
          }
 
473
        },
 
474
        verbose: function() {
 
475
          if(settings.verbose && settings.debug) {
 
476
            if(settings.performance) {
 
477
              module.performance.log(arguments);
 
478
            }
 
479
            else {
 
480
              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
 
481
              module.verbose.apply(console, arguments);
 
482
            }
 
483
          }
 
484
        },
 
485
        error: function() {
 
486
          module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
 
487
          module.error.apply(console, arguments);
 
488
        },
 
489
        performance: {
 
490
          log: function(message) {
 
491
            var
 
492
              currentTime,
 
493
              executionTime,
 
494
              previousTime
 
495
            ;
 
496
            if(settings.performance) {
 
497
              currentTime   = new Date().getTime();
 
498
              previousTime  = time || currentTime;
 
499
              executionTime = currentTime - previousTime;
 
500
              time          = currentTime;
 
501
              performance.push({
 
502
                'Element'        : element,
 
503
                'Name'           : message[0],
 
504
                'Arguments'      : [].slice.call(message, 1) || '',
 
505
                'Execution Time' : executionTime
 
506
              });
 
507
            }
 
508
            clearTimeout(module.performance.timer);
 
509
            module.performance.timer = setTimeout(module.performance.display, 100);
 
510
          },
 
511
          display: function() {
 
512
            var
 
513
              title = settings.name + ':',
 
514
              totalTime = 0
 
515
            ;
 
516
            time = false;
 
517
            clearTimeout(module.performance.timer);
 
518
            $.each(performance, function(index, data) {
 
519
              totalTime += data['Execution Time'];
 
520
            });
 
521
            title += ' ' + totalTime + 'ms';
 
522
            if(moduleSelector) {
 
523
              title += ' \'' + moduleSelector + '\'';
 
524
            }
 
525
            if($allModules.size() > 1) {
 
526
              title += ' ' + '(' + $allModules.size() + ')';
 
527
            }
 
528
            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
 
529
              console.groupCollapsed(title);
 
530
              if(console.table) {
 
531
                console.table(performance);
 
532
              }
 
533
              else {
 
534
                $.each(performance, function(index, data) {
 
535
                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
 
536
                });
 
537
              }
 
538
              console.groupEnd();
 
539
            }
 
540
            performance = [];
 
541
          }
 
542
        },
 
543
        invoke: function(query, passedArguments, context) {
 
544
          var
 
545
            object = instance,
 
546
            maxDepth,
 
547
            found,
 
548
            response
 
549
          ;
 
550
          passedArguments = passedArguments || queryArguments;
 
551
          context         = element         || context;
 
552
          if(typeof query == 'string' && object !== undefined) {
 
553
            query    = query.split(/[\. ]/);
 
554
            maxDepth = query.length - 1;
 
555
            $.each(query, function(depth, value) {
 
556
              var camelCaseValue = (depth != maxDepth)
 
557
                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
 
558
                : query
 
559
              ;
 
560
              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
 
561
                object = object[camelCaseValue];
 
562
              }
 
563
              else if( object[camelCaseValue] !== undefined ) {
 
564
                found = object[camelCaseValue];
 
565
                return false;
 
566
              }
 
567
              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
 
568
                object = object[value];
 
569
              }
 
570
              else if( object[value] !== undefined ) {
 
571
                found = object[value];
 
572
                return false;
 
573
              }
 
574
              else {
 
575
                return false;
 
576
              }
 
577
            });
 
578
          }
 
579
          if ( $.isFunction( found ) ) {
 
580
            response = found.apply(context, passedArguments);
 
581
          }
 
582
          else if(found !== undefined) {
 
583
            response = found;
 
584
          }
 
585
          if($.isArray(returnedValue)) {
 
586
            returnedValue.push(response);
 
587
          }
 
588
          else if(returnedValue !== undefined) {
 
589
            returnedValue = [returnedValue, response];
 
590
          }
 
591
          else if(response !== undefined) {
 
592
            returnedValue = response;
 
593
          }
 
594
          return found;
 
595
        }
 
596
      };
 
597
      if(methodInvoked) {
 
598
        if(instance === undefined) {
 
599
          module.initialize();
 
600
        }
 
601
        module.invoke(query);
 
602
      }
 
603
      else {
 
604
        if(instance !== undefined) {
 
605
          module.destroy();
 
606
        }
 
607
        module.initialize();
 
608
      }
 
609
 
 
610
    })
 
611
  ;
 
612
 
 
613
  return (returnedValue !== undefined)
 
614
    ? returnedValue
 
615
    : this
 
616
  ;
 
617
};
 
618
 
 
619
$.fn.state.settings = {
 
620
 
 
621
  // module info
 
622
  name : 'State',
 
623
 
 
624
  // debug output
 
625
  debug      : true,
 
626
 
 
627
  // verbose debug output
 
628
  verbose    : true,
 
629
 
 
630
  // namespace for events
 
631
  namespace  : 'state',
 
632
 
 
633
  // debug data includes performance
 
634
  performance: true,
 
635
 
 
636
  // callback occurs on state change
 
637
  onActivate   : function() {},
 
638
  onDeactivate : function() {},
 
639
  onChange     : function() {},
 
640
 
 
641
  // state test functions
 
642
  activateTest   : function() { return true; },
 
643
  deactivateTest : function() { return true; },
 
644
 
 
645
  // whether to automatically map default states
 
646
  automatic     : true,
 
647
 
 
648
  // activate / deactivate changes all elements instantiated at same time
 
649
  sync          : false,
 
650
 
 
651
  // default flash text duration, used for temporarily changing text of an element
 
652
  flashDuration : 3000,
 
653
 
 
654
  // selector filter
 
655
  filter     : {
 
656
    text   : '.loading, .disabled',
 
657
    active : '.disabled'
 
658
  },
 
659
 
 
660
  context    : false,
 
661
 
 
662
  // error
 
663
  error: {
 
664
    method : 'The method you called is not defined.'
 
665
  },
 
666
 
 
667
  // metadata
 
668
  metadata: {
 
669
    promise    : 'promise',
 
670
    storedText : 'stored-text'
 
671
  },
 
672
 
 
673
  // change class on state
 
674
  className: {
 
675
    focus   : 'focus',
 
676
    hover   : 'hover',
 
677
    down    : 'down',
 
678
    active  : 'active',
 
679
    loading : 'loading'
 
680
  },
 
681
 
 
682
  selector: {
 
683
    // selector for text node
 
684
    text: false
 
685
  },
 
686
 
 
687
  defaults : {
 
688
    input: {
 
689
      hover   : true,
 
690
      focus   : true,
 
691
      down    : true,
 
692
      loading : false,
 
693
      active  : false
 
694
    },
 
695
    button: {
 
696
      hover   : true,
 
697
      focus   : false,
 
698
      down    : true,
 
699
      active  : true,
 
700
      loading : true
 
701
    }
 
702
  },
 
703
 
 
704
  states     : {
 
705
    hover   : true,
 
706
    focus   : true,
 
707
    down    : true,
 
708
    loading : false,
 
709
    active  : false
 
710
  },
 
711
 
 
712
  text     : {
 
713
    flash    : false,
 
714
    hover    : false,
 
715
    active   : false,
 
716
    inactive : false,
 
717
    enable   : false,
 
718
    disable  : false
 
719
  }
 
720
 
 
721
};
 
722
 
 
723
 
 
724
 
 
725
})( jQuery, window , document );