3
* http://github.com/jlukic/semantic-ui/
6
* Copyright 2014 Contributors
7
* Released under the MIT license
8
* http://opensource.org/licenses/MIT
12
;(function ( $, window, document, undefined ) {
14
$.fn.state = function(parameters) {
16
$allModules = $(this),
17
settings = $.extend(true, {}, $.fn.state.settings, parameters),
19
moduleSelector = $allModules.selector || '',
21
time = new Date().getTime(),
25
methodInvoked = (typeof query == 'string'),
26
queryArguments = [].slice.call(arguments, 1),
29
error = settings.error,
30
metadata = settings.metadata,
31
className = settings.className,
32
namespace = settings.namespace,
33
states = settings.states,
36
eventNamespace = '.' + namespace,
37
moduleNamespace = namespace + '-module',
48
instance = $module.data(moduleNamespace),
54
initialize: function() {
55
module.verbose('Initializing module');
57
// allow module to guess desired state based on element
58
if(settings.automatic) {
59
module.add.defaults();
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)
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)
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)
83
.on(moduleSelector, 'mouseenter' + eventNamespace, module.change.text)
84
.on(moduleSelector, 'mouseleave' + eventNamespace, module.reset.text)
85
.on(moduleSelector, 'click' + eventNamespace, module.toggle.state)
89
if( module.allows('hover') ) {
91
.on('mouseenter' + eventNamespace, module.enable.hover)
92
.on('mouseleave' + eventNamespace, module.disable.hover)
95
if( module.allows('down') ) {
97
.on('mousedown' + eventNamespace, module.enable.down)
98
.on('mouseup' + eventNamespace, module.disable.down)
101
if( module.allows('focus') ) {
103
.on('focus' + eventNamespace, module.enable.focus)
104
.on('blur' + eventNamespace, module.disable.focus)
108
.on('mouseenter' + eventNamespace, module.change.text)
109
.on('mouseleave' + eventNamespace, module.reset.text)
110
.on('click' + eventNamespace, module.toggle.state)
113
module.instantiate();
116
instantiate: function() {
117
module.verbose('Storing instance of module', module);
120
.data(moduleNamespace, module)
124
destroy: function() {
125
module.verbose('Destroying previous module', instance);
128
.removeData(moduleNamespace)
132
refresh: function() {
133
module.verbose('Refreshing selector cache');
134
$module = $(element);
138
defaults: function() {
140
userStates = parameters && $.isPlainObject(parameters.states)
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);
156
return $module.hasClass(className.active);
158
loading: function() {
159
return $module.hasClass(className.loading);
161
inactive: function() {
162
return !( $module.hasClass(className.active) );
165
enabled: function() {
166
return !( $module.is(settings.filter.active) );
168
disabled: function() {
169
return ( $module.is(settings.filter.active) );
171
textEnabled: function() {
172
return !( $module.is(settings.filter.text) );
175
// definitions for automatic type detection
177
return $module.is('.button:not(a, .submit)');
180
return $module.is('input');
184
allow: function(state) {
185
module.debug('Now allowing state', state);
186
states[state] = true;
188
disallow: function(state) {
189
module.debug('No longer allowing', state);
190
states[state] = false;
193
allows: function(state) {
194
return states[state] || false;
198
state: function(state) {
199
if(module.allows(state)) {
200
$module.addClass( className[state] );
205
$module.addClass(className.focus);
208
$module.addClass(className.hover);
211
$module.addClass(className.down);
216
state: function(state) {
217
if(module.allows(state)) {
218
$module.removeClass( className[state] );
223
$module.removeClass(className.focus);
226
$module.removeClass(className.hover);
229
$module.removeClass(className.down);
236
apiRequest = $module.data(metadata.promise)
238
if( module.allows('active') && module.is.enabled() ) {
240
if(apiRequest !== undefined) {
241
module.listenTo(apiRequest);
244
module.change.state();
250
listenTo: function(apiRequest) {
251
module.debug('API request detected, waiting for state signal', apiRequest);
254
module.update.text(text.loading);
258
if(apiRequest.state() == 'resolved') {
259
module.debug('API request succeeded');
260
settings.activateTest = function(){ return true; };
261
settings.deactivateTest = function(){ return true; };
264
module.debug('API request failed');
265
settings.activateTest = function(){ return false; };
266
settings.deactivateTest = function(){ return false; };
268
module.change.state();
272
// xhr exists but set to false, beforeSend killed the xhr
274
settings.activateTest = function(){ return false; };
275
settings.deactivateTest = function(){ return false; };
279
// checks whether active/inactive state can be given
283
module.debug('Determining state change direction');
284
// inactive to active change
285
if( module.is.inactive() ) {
294
$.proxy(settings.onChange, element)();
298
if( module.is.textEnabled() ) {
299
if( module.is.active() ) {
301
module.verbose('Changing text to hover text', text.hover);
302
module.update.text(text.hover);
304
else if(text.disable) {
305
module.verbose('Changing text to disable text', text.disable);
306
module.update.text(text.disable);
311
module.verbose('Changing text to hover text', text.disable);
312
module.update.text(text.hover);
314
else if(text.enable){
315
module.verbose('Changing text to enable text', text.enable);
316
module.update.text(text.enable);
324
activate: function() {
325
if( $.proxy(settings.activateTest, element)() ) {
326
module.debug('Setting state to active');
328
.addClass(className.active)
330
module.update.text(text.active);
332
$.proxy(settings.onActivate, element)();
335
deactivate: function() {
336
if($.proxy(settings.deactivateTest, element)() ) {
337
module.debug('Setting state to inactive');
339
.removeClass(className.active)
341
module.update.text(text.inactive);
343
$.proxy(settings.onDeactivate, element)();
347
module.verbose('Syncing other buttons to current state');
348
if( module.is.active() ) {
363
return (settings.selector.text)
364
? $module.find(settings.selector.text).text()
368
textFor: function(state) {
369
return text[state] || false;
374
text: function(text, duration) {
376
previousText = module.get.text()
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);
389
// on mouseout sets text to previous value
392
activeText = text.active || $module.data(metadata.storedText),
393
inactiveText = text.inactive || $module.data(metadata.storedText)
395
if( module.is.textEnabled() ) {
396
if( module.is.active() && activeText) {
397
module.verbose('Resetting active text', activeText);
398
module.update.text(activeText);
400
else if(inactiveText) {
401
module.verbose('Resetting inactive text', activeText);
402
module.update.text(inactiveText);
409
text: function(text) {
411
currentText = module.get.text()
413
if(text && text !== currentText) {
414
module.debug('Updating text', text);
415
if(settings.selector.text) {
417
.data(metadata.storedText, text)
418
.find(settings.selector.text)
424
.data(metadata.storedText, text)
430
module.debug('Text is already sane, ignoring update', text);
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);
442
settings[name] = value;
446
return settings[name];
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);
456
module[name] = value;
465
if(settings.performance) {
466
module.performance.log(arguments);
469
module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
470
module.debug.apply(console, arguments);
474
verbose: function() {
475
if(settings.verbose && settings.debug) {
476
if(settings.performance) {
477
module.performance.log(arguments);
480
module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
481
module.verbose.apply(console, arguments);
486
module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
487
module.error.apply(console, arguments);
490
log: function(message) {
496
if(settings.performance) {
497
currentTime = new Date().getTime();
498
previousTime = time || currentTime;
499
executionTime = currentTime - previousTime;
504
'Arguments' : [].slice.call(message, 1) || '',
505
'Execution Time' : executionTime
508
clearTimeout(module.performance.timer);
509
module.performance.timer = setTimeout(module.performance.display, 100);
511
display: function() {
513
title = settings.name + ':',
517
clearTimeout(module.performance.timer);
518
$.each(performance, function(index, data) {
519
totalTime += data['Execution Time'];
521
title += ' ' + totalTime + 'ms';
523
title += ' \'' + moduleSelector + '\'';
525
if($allModules.size() > 1) {
526
title += ' ' + '(' + $allModules.size() + ')';
528
if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
529
console.groupCollapsed(title);
531
console.table(performance);
534
$.each(performance, function(index, data) {
535
console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
543
invoke: function(query, passedArguments, context) {
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)
560
if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
561
object = object[camelCaseValue];
563
else if( object[camelCaseValue] !== undefined ) {
564
found = object[camelCaseValue];
567
else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
568
object = object[value];
570
else if( object[value] !== undefined ) {
571
found = object[value];
579
if ( $.isFunction( found ) ) {
580
response = found.apply(context, passedArguments);
582
else if(found !== undefined) {
585
if($.isArray(returnedValue)) {
586
returnedValue.push(response);
588
else if(returnedValue !== undefined) {
589
returnedValue = [returnedValue, response];
591
else if(response !== undefined) {
592
returnedValue = response;
598
if(instance === undefined) {
601
module.invoke(query);
604
if(instance !== undefined) {
613
return (returnedValue !== undefined)
619
$.fn.state.settings = {
627
// verbose debug output
630
// namespace for events
633
// debug data includes performance
636
// callback occurs on state change
637
onActivate : function() {},
638
onDeactivate : function() {},
639
onChange : function() {},
641
// state test functions
642
activateTest : function() { return true; },
643
deactivateTest : function() { return true; },
645
// whether to automatically map default states
648
// activate / deactivate changes all elements instantiated at same time
651
// default flash text duration, used for temporarily changing text of an element
652
flashDuration : 3000,
656
text : '.loading, .disabled',
664
method : 'The method you called is not defined.'
670
storedText : 'stored-text'
673
// change class on state
683
// selector for text node
725
})( jQuery, window , document );