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