'+sMsg+'
'; 70 | this.element.scrollTop = this.element.scrollHeight; 71 | } 72 | }; 73 | } ); 74 | -------------------------------------------------------------------------------- /src/demos/widgets/modules/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The app module, responsible for starting/stopping widget modules. 3 | */ 4 | TinyCore.Module.define( 'app', ['mediator', 'events'], function ( _mediator, _events ) 5 | { 6 | 'use strict'; 7 | 8 | // Constants. 9 | var CLASS_DISABLED = 'disabled'; 10 | 11 | // The module. 12 | return { 13 | startLink0 : document.getElementById( 'start0' ), 14 | stopLink0 : document.getElementById( 'stop0' ), 15 | startLink1 : document.getElementById( 'start1' ), 16 | stopLink1 : document.getElementById( 'stop1' ), 17 | /** 18 | * This method is called when the module is started. 19 | * @param {Object} oStartData The start data. 20 | */ 21 | onStart : function ( oStartData ) 22 | { 23 | TinyCore.Module.start( 'activity-log', { element : document.getElementById( 'activity-view' ) } ); 24 | 25 | _events.on( this.startLink0, 'click', this.startWidget ); 26 | _events.on( this.stopLink0, 'click', this.stopWidget ); 27 | _events.on( this.startLink1, 'click', this.startWidget ); 28 | _events.on( this.stopLink1, 'click', this.stopWidget ); 29 | }, 30 | /** 31 | * This method is called when the module is stopped. 32 | */ 33 | onStop : function () 34 | { 35 | _events.off( this.stopLink1, 'click', this.stopWidget ); 36 | _events.off( this.startLink1, 'click', this.startWidget ); 37 | _events.off( this.stopLink0, 'click', this.stopWidget ); 38 | _events.off( this.startLink0, 'click', this.startWidget ); 39 | 40 | TinyCore.Module.stop( 'activity-log' ); 41 | }, 42 | /** 43 | * Starts the widget module. 44 | * @param {Event} eEvent 45 | */ 46 | startWidget : function ( eEvent ) 47 | { 48 | var sModuleName = this.href.split( '#' )[1], 49 | sLinkNum = this.id.substr( -1 ), 50 | oWidget = document.getElementById( sModuleName ); 51 | 52 | eEvent.preventDefault(); 53 | 54 | TinyCore.Module.start( sModuleName, { element : oWidget } ); 55 | 56 | this.className = CLASS_DISABLED; 57 | document.getElementById( 'stop'+sLinkNum ).className = ''; 58 | 59 | _mediator.publish( 'widget:enable', { targetID : sModuleName } ); 60 | }, 61 | /** 62 | * Stops the widget module. 63 | * @param {Event} eEvent 64 | */ 65 | stopWidget : function ( eEvent ) 66 | { 67 | var sModuleName = this.href.split( '#' )[1], 68 | sLinkNum = this.id.substr( -1 ); 69 | 70 | eEvent.preventDefault(); 71 | 72 | _mediator.publish( 'widget:disable', { targetID : sModuleName } ); 73 | 74 | TinyCore.Module.stop( sModuleName ); 75 | 76 | this.className = CLASS_DISABLED; 77 | document.getElementById( 'start'+sLinkNum ).className = ''; 78 | } 79 | }; 80 | } ); 81 | -------------------------------------------------------------------------------- /src/demos/widgets/modules/widget-generic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The "widget-generic" module, mainly responsible for automatically subscribing to topics and adding DOM events listeners when started. 3 | * Also publishes topics for logging information (see the "activity-log" module). 4 | * It's the base module for the "widget-text" and the "widget-select" modules. 5 | * 6 | * Topics subscribed : "widget:enable", "widget:disable" 7 | * Topics published : "log:add" 8 | */ 9 | TinyCore.Module.define( 'widget-generic', ['mediator', 'events'], function ( _mediator, _events ) 10 | { 11 | 'use strict'; 12 | 13 | // Private variables and helpers. 14 | var _oAttachedEvents = {}, 15 | _fpSlice = Array.prototype.slice, 16 | _fpToArray = function ( mixed ) 17 | { 18 | return _fpSlice.call( mixed ); 19 | }; 20 | 21 | // The module. 22 | return { 23 | /** 24 | * Topics to automatically subscribe to. 25 | * @type {Object} 26 | */ 27 | topics : { 28 | 'widget:enable' : function ( oTopic ) 29 | { 30 | if ( oTopic.data.targetID !== this.element.id ) 31 | { 32 | return; 33 | } 34 | 35 | this.logActivity( 'widget-generic', '"'+oTopic.name+'" received (#'+oTopic.data.targetID+')' ); 36 | 37 | _fpToArray( this.element.querySelectorAll( 'input, button, select' ) ).forEach( function ( oInput ) 38 | { 39 | oInput.removeAttribute( 'disabled' ); 40 | } ); 41 | }, 42 | 'widget:disable' : function ( oTopic ) 43 | { 44 | if ( oTopic.data.targetID !== this.element.id ) 45 | { 46 | return; 47 | } 48 | 49 | this.logActivity( 'widget-generic', '"'+oTopic.name+'" received (#'+oTopic.data.targetID+')' ); 50 | 51 | _fpToArray( this.element.querySelectorAll( 'input, button, select' ) ).forEach( function ( oInput ) 52 | { 53 | oInput.setAttribute( 'disabled', 'disabled' ); 54 | } ); 55 | } 56 | }, 57 | /** 58 | * Events to automatically add listeners to. 59 | * @type {Object} 60 | */ 61 | events : { 62 | 'focus input' : function ( eEvent ) 63 | { 64 | this.logActivity( 'widget-generic', 'focus on input' ); 65 | }, 66 | 'focus select' : function ( eEvent ) 67 | { 68 | this.logActivity( 'widget-generic', 'focus on select' ); 69 | } 70 | }, 71 | /** 72 | * This method is called when the module is started. 73 | * @param {Object} oStartData The start data. 74 | */ 75 | onStart : function ( oStartData ) 76 | { 77 | this.logActivity( 'widget-generic', 'onStart' ); 78 | 79 | // "Automatic setup", IN THE CONTEXT OF THE INHERITED MODULE. 80 | this.element = oStartData.element; 81 | this.subscribeTopics(); 82 | this.addEventsListeners(); 83 | }, 84 | /** 85 | * Subscribes to all topics defined in the module. 86 | */ 87 | subscribeTopics : function ( oContext ) 88 | { 89 | this.logActivity( 'widget-generic', 'subscribeTopics' ); 90 | 91 | var self = this; 92 | 93 | TinyCore.Utils.forIn( self.topics, function ( fpHandler, sTopicName ) 94 | { 95 | _mediator.subscribe( sTopicName, fpHandler, self ); 96 | } ); 97 | }, 98 | /** 99 | * Adds events listeners to all events defined in the module. 100 | */ 101 | addEventsListeners : function () 102 | { 103 | this.logActivity( 'widget-generic', 'addEventsListeners' ); 104 | 105 | var self = this; 106 | 107 | TinyCore.Utils.forIn( self.events, function ( fpHandler, sEventAndSelector ) 108 | { 109 | var sParts = sEventAndSelector.split( ' ' ), 110 | sEventName = sParts[0], 111 | sSelector = sParts[1], 112 | oElement = self.element.querySelector( sSelector ), 113 | fpContextualHandler = fpHandler.bind( self ); 114 | 115 | _events.on( oElement, sEventName, fpContextualHandler ); 116 | 117 | _oAttachedEvents[sEventAndSelector] = { 118 | element : oElement, 119 | eventName : sEventName, 120 | handler : fpContextualHandler 121 | }; 122 | } ); 123 | }, 124 | /** 125 | * This method is called when the module is stopped. 126 | */ 127 | onStop : function () 128 | { 129 | this.logActivity( 'widget-generic', 'onStop' ); 130 | 131 | _mediator.unsubscribeAll(); 132 | 133 | TinyCore.Utils.forIn( _oAttachedEvents, function ( oEventData ) 134 | { 135 | _events.off( oEventData.element, oEventData.eventName, oEventData.handler ); 136 | } ); 137 | }, 138 | /** 139 | * Logs some messages. 140 | * @param {String} sSrc 141 | * @param {String} sMsg 142 | */ 143 | logActivity : function ( sSrc, sMsg ) 144 | { 145 | _mediator.publish( 'log:add', { timestamp : +new Date(), src : sSrc, msg : sMsg } ); 146 | } 147 | }; 148 | } ); 149 | -------------------------------------------------------------------------------- /src/demos/widgets/modules/widget-select.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The (inherited) "widget-select" module. 3 | * 4 | * Topics subscribed : "color:add" 5 | * Topics published : "color:preset" 6 | */ 7 | TinyCore.Module.inherit( 'widget-generic', 'widget-select', ['mediator'], function ( _mediator ) 8 | { 9 | // The module. 10 | return { 11 | /** 12 | * Topics to automatically subscribe to. 13 | * @type {Object} 14 | */ 15 | topics : { 16 | 'color:add' : function ( oTopic ) 17 | { 18 | var sNewOptionValue = oTopic.data.text.toLowerCase(), 19 | sNewOption = sNewOptionValue.charAt( 0 ).toUpperCase() + sNewOptionValue.slice( 1 ), 20 | oNewOption = document.createElement( 'option' ); 21 | 22 | this.logActivity( 'widget-select', '"'+oTopic.name+'" received ('+oTopic.data.text+')' ); 23 | 24 | oNewOption.value = sNewOptionValue; 25 | oNewOption.text = sNewOption; 26 | this.select.appendChild( oNewOption ); 27 | } 28 | }, 29 | /** 30 | * Events to automatically add listeners to. 31 | * @type {Object} 32 | */ 33 | events : { 34 | 'change select' : function ( eEvent ) 35 | { 36 | var oSelectedOption = this.select.options[this.select.selectedIndex], 37 | sText = oSelectedOption.value !== '' ? oSelectedOption.text : ''; 38 | 39 | this.logActivity( 'widget-select', 'change on select' ); 40 | 41 | _mediator.publish( 'color:preset', { 42 | value : oSelectedOption.value, 43 | text : sText 44 | } ); 45 | } 46 | }, 47 | /** 48 | * The select. 49 | * @type {DOM Element} 50 | */ 51 | select : document.getElementById( 'select-input' ), 52 | /** 53 | * This method is called when the module is started. 54 | * @param {Object} oStartData The start data. 55 | */ 56 | onStart : function ( oStartData ) 57 | { 58 | this.logActivity( 'widget-select', 'onStart' ); 59 | this._super( oStartData ); 60 | }, 61 | /** 62 | * This method is called when the module is stopped. 63 | */ 64 | onStop : function () 65 | { 66 | this.logActivity( 'widget-select', 'onStop' ); 67 | this._super(); 68 | } 69 | }; 70 | } ); 71 | -------------------------------------------------------------------------------- /src/demos/widgets/modules/widget-text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The (inherited) "widget-text" module. 3 | * 4 | * Topics subscribed : "color:preset" 5 | * Topics published : "color:add" 6 | */ 7 | TinyCore.Module.inherit( 'widget-generic', 'widget-text', ['mediator'], function ( _mediator ) 8 | { 9 | // The module. 10 | return { 11 | /** 12 | * Topics to automatically subscribe to. 13 | * @type {Object} 14 | */ 15 | topics : { 16 | 'color:preset' : function ( oTopic ) 17 | { 18 | this.logActivity( 'widget-text', '"'+oTopic.name+'" received ('+oTopic.data.value+')' ); 19 | this.textInput.value = oTopic.data.text; 20 | } 21 | }, 22 | /** 23 | * Events to automatically add listeners to. 24 | * @type {Object} 25 | */ 26 | events : { 27 | 'click button[type=submit]' : function ( eEvent ) 28 | { 29 | var sValue = this.textInput.value.trim(); 30 | 31 | this.logActivity( 'widget-text', 'click on button[type=submit]' ); 32 | 33 | eEvent.preventDefault(); 34 | 35 | if ( sValue ) 36 | { 37 | _mediator.publish( 'color:add', { text : sValue } ); 38 | sValue = ''; 39 | } 40 | 41 | this.textInput.value = sValue; 42 | } 43 | }, 44 | /** 45 | * The text input. 46 | * @type {DOM Element} 47 | */ 48 | textInput : document.getElementById( 'text-input' ), 49 | /** 50 | * This method is called when the module is started. 51 | * @param {Object} oStartData The start data. 52 | */ 53 | onStart : function ( oStartData ) 54 | { 55 | this.logActivity( 'widget-text', 'onStart' ); 56 | this._super( oStartData ); 57 | }, 58 | /** 59 | * This method is called when the module is stopped. 60 | */ 61 | onStop : function () 62 | { 63 | this.logActivity( 'widget-text', 'onStop' ); 64 | this._super(); 65 | } 66 | }; 67 | } ); 68 | -------------------------------------------------------------------------------- /src/extensions/AMD/README.md: -------------------------------------------------------------------------------- 1 | ## TinyCore.js / Extensions 2 | 3 | ### AMD 4 | 5 | #### Features 6 | 7 | - Wrapper over [require.js](http://requirejs.org). 8 | - Provides async modules loading using the [Asynchronous Module Definition](https://github.com/amdjs/amdjs-api/wiki/AMD) format. 9 | - Less than 1Kb minified + 15Kb for require.js. 10 | 11 | #### TinyCore.AMD API 12 | 13 | ```js 14 | TinyCore.AMD.config( settings ) 15 | TinyCore.AMD.define( moduleName, dependencies, creatorFunction ) 16 | TinyCore.AMD.require( resourcesNames, callback ) 17 | TinyCore.AMD.requireAndStart( modulesData, callback ) 18 | TinyCore.AMD.setErrorHandler( errorHandler ) 19 | ``` 20 | 21 | #### Examples 22 | 23 | An example from "demos/todolist/amd/index.html" : 24 | 25 | **app.js :** 26 | ```js 27 | TinyCore.AMD.config( { 28 | require : { 29 | // see http://requirejs.org/docs/api.html#config 30 | baseUrl : 'modules', 31 | paths : { 32 | 'tools' : '../tools' 33 | } 34 | } 35 | } ); 36 | 37 | TinyCore.AMD.requireAndStart( [ 'todo_form_add', 'todo_list' ], function ( aModulesData ) 38 | { 39 | console.info( aModulesData.length+' module(s) loaded:', aModulesData ); 40 | } ); 41 | ``` 42 | 43 | **todo_form_add.js :** 44 | ```js 45 | TinyCore.AMD.define( 'todo_form_add', ['tools/mediator', 'tools/events'], function ( mediator, events ) 46 | { 47 | return { /* ... */ }; 48 | } ); 49 | ``` 50 | 51 | **todo_list.js :** 52 | ```js 53 | TinyCore.AMD.define( 'todo_list', ['tools/mediator', 'tools/dom', 'tools/events'], function ( mediator, dom, events ) 54 | { 55 | return { /* ... */ }; 56 | } ); 57 | ``` 58 | 59 | ### AMD with DOM boot 60 | 61 | #### Features 62 | 63 | - Supports a declarative approach : simply by adding the proper "data-" attribute in the markup, modules can be attached to any DOM element. Modules can then be loaded and started from the declarations found in the DOM, on demand. 64 | - Lazy loading based on strategies : by adding the proper "data-" attribute, strategies can be declared for deferring the loading of modules. 65 | - 3 types of strategies are supported : event-based (when a given event occurs on the element where the module is attached), time-based (after xxx milliseconds), distance-based (when the distance between the mouse pointer and the DOM element is less than yyy pixels). 66 | - Loading strategies can be combined (e.g.: event+timer). 67 | - Around 3Kb minified + 15Kb for require.js. 68 | 69 | #### Extended TinyCore.AMD API 70 | 71 | ```js 72 | // existing 73 | TinyCore.AMD.config( settings ) 74 | TinyCore.AMD.define( moduleName, dependencies, creatorFunction ) 75 | TinyCore.AMD.require( resourcesNames, callback ) 76 | TinyCore.AMD.requireAndStart( modulesData, callback ) 77 | TinyCore.AMD.setErrorHandler( errorHandler ) 78 | 79 | // extended 80 | TinyCore.AMD.domBoot( rootNode, callback ) 81 | ``` 82 | 83 | #### Examples 84 | 85 | See the demos in the "demos/todolist/amd" folder : 86 | 87 | - "demos/todolist/amd/index-domboot.html" : declarative approach using "data-" attributes in the markup, without deferred loading strategies. 88 | - "demos/todolist/amd/index-deferred" : same as before, but with several deferred loading strategies. 89 | 90 | An example from "demos/todolist/amd/index-deferred.html" : 91 | 92 | **app.js :** 93 | ```js 94 | TinyCore.AMD.config( { 95 | require : { 96 | // see http://requirejs.org/docs/api.html#config 97 | baseUrl : 'modules', 98 | paths : { 99 | 'tools' : '../tools' 100 | } 101 | } 102 | domBoot : { 103 | // node names to ignore when scanning the DOM (must be in capital letters) 104 | nodesIgnored : { H1 : true, LI : true, A : true } 105 | } 106 | } ); 107 | 108 | TinyCore.AMD.domBoot( function ( aModulesData ) 109 | { 110 | console.info( 'DOM boot ok. '+aModulesData.length+' module(s) loaded:', aModulesData ); 111 | } ); 112 | ``` 113 | 114 | **app.html :** 115 | ```html 116 | (...) 117 | 118 |