├── ├── .gitignore ├── CHANGELOG ├── Gruntfile.js ├── MIT-LICENSE ├── README.md ├── bower.json ├── build ├── TinyCore.js ├── TinyCore.min.js ├── extensions │ ├── AMD │ │ ├── TinyCore.AMD+domBoot.min.js │ │ ├── TinyCore.AMD.min.js │ │ └── require-2.1.4.min.js │ ├── Inheritance │ │ └── TinyCore.Module.Inheritance.min.js │ ├── Instances │ │ └── TinyCore.Module.Instances.min.js │ └── Jasmine │ │ └── TinyCore.Toolbox.Jasmine.min.js ├── output.txt └── tools │ └── mediator │ └── TinyCore.Toolbox.Mediator.min.js ├── misc └── sublimetext │ └── mod.sublime-snippet ├── package.json ├── src ├── Intro.js ├── Outro.js ├── TinyCore.Error.js ├── TinyCore.Module.js ├── TinyCore.Toolbox.js ├── TinyCore.Utils.js ├── demos │ ├── __tools__ │ │ ├── dom.js │ │ ├── events.js │ │ └── storage.js │ ├── chat │ │ ├── css │ │ │ └── chat.css │ │ ├── index.html │ │ └── modules │ │ │ ├── app.js │ │ │ ├── chat_client.js │ │ │ └── hub.js │ ├── index.html │ ├── todolist │ │ ├── _css │ │ │ └── todo.css │ │ ├── _img │ │ │ ├── remove-red.png │ │ │ ├── remove.png │ │ │ ├── reset-red.png │ │ │ └── reset.png │ │ ├── amd │ │ │ ├── index-deferred.html │ │ │ ├── index-domboot.html │ │ │ ├── index.html │ │ │ └── modules │ │ │ │ ├── todo_form_add.js │ │ │ │ ├── todo_form_search.js │ │ │ │ ├── todo_list.js │ │ │ │ ├── todo_storage.js │ │ │ │ └── todo_summary.js │ │ └── localfiles │ │ │ ├── index.html │ │ │ └── modules │ │ │ ├── todo_form_add.js │ │ │ ├── todo_form_search.js │ │ │ ├── todo_list.js │ │ │ ├── todo_storage.js │ │ │ └── todo_summary.js │ └── widgets │ │ ├── css │ │ └── widgets.css │ │ ├── index.html │ │ └── modules │ │ ├── activity-log.js │ │ ├── app.js │ │ ├── widget-generic.js │ │ ├── widget-select.js │ │ └── widget-text.js ├── extensions │ ├── AMD │ │ ├── README.md │ │ ├── TinyCore.AMD.domBoot.js │ │ ├── TinyCore.AMD.js │ │ └── require-2.1.4.min.js │ ├── Inheritance │ │ ├── README.md │ │ └── TinyCore.Module.Inheritance.js │ ├── Instances │ │ ├── README.md │ │ └── TinyCore.Module.Instances.js │ ├── Jasmine │ │ ├── README.md │ │ └── TinyCore.Toolbox.Jasmine.js │ └── README.md └── tools │ └── mediator │ ├── README.md │ └── TinyCore.Toolbox.Mediator.js └── test ├── jasmine ├── TinyCore.Spec.js ├── TinyCore.SpecHelper.js ├── TinyCore.SpecRunner.html ├── extensions │ ├── AMD │ │ ├── RequireMock.js │ │ ├── TinyCore.AMD.Spec.js │ │ ├── TinyCore.AMD.SpecRunner.html │ │ ├── TinyCore.AMD.domBoot.Spec.js │ │ └── TinyCore.AMD.domBoot.SpecRunner.html │ ├── Inheritance+Instances │ │ ├── TinyCore.Module.Inheritance+Instances.Spec.js │ │ └── TinyCore.Module.Inheritance+Instances.SpecRunner.html │ ├── Inheritance │ │ ├── TinyCore.Module.Inheritance.Spec.js │ │ └── TinyCore.Module.Inheritance.SpecRunner.html │ ├── Jasmine │ │ ├── TinyCore.Toolbox.Jasmine.Spec.js │ │ └── TinyCore.Toolbox.Jasmine.SpecRunner.html │ └── instances │ │ ├── TinyCore.Module.Instances.Spec.js │ │ └── TinyCore.Module.Instances.SpecRunner.html ├── lib │ └── jasmine-1.3.1 │ │ ├── MIT.LICENSE │ │ ├── jasmine-html.js │ │ ├── jasmine.css │ │ └── jasmine.js └── tools │ └── mediator │ ├── TinyCore.Toolbox.Mediator.Spec.js │ └── TinyCore.Toolbox.Mediator.SpecRunner.html └── karma ├── core.conf.js ├── ext.amd.conf.js ├── ext.amd.domBoot.conf.js ├── ext.inheritance+instances.conf.js ├── ext.inheritance.conf.js ├── ext.instances.conf.js ├── ext.jasmine.conf.js ├── shared.conf.js └── tools.mediator.conf.js / : -------------------------------------------------------------------------------- 1 | {"version":3,"file":"build/TinyCore.min.js","sources":["build/TinyCore.js"],"names":["oEnv","_null_","_true_","_false_","_oObjectProto","Object","prototype","_hasOwnProp","hasOwnProperty","_toString","toString","TinyCore","version","debugMode","Module","Toolbox","Error","Utils","Array","forEach","fn","scope","i","len","this","length","call","Function","bind","oThis","TypeError","aArgs","slice","arguments","fToBind","fNOP","fBound","apply","concat","String","trim","replace","isClass","mixed","sClassName","isFunction","isObject","isArray","forIn","oObject","fpCallback","sProperty","extend","args","nArgsCount","nIndex","oDest","fpCopy","val","key","tryCatchDecorator","oContext","fpFunc","sErrMsg","__decorated__","fpDecoratedFunc","eError","log","message","createModuleObject","fpCreator","_oTools","_nToolID","request","sToolName","oToolData","fpFactory","register","sMsg","console","error","report","_oErrorHandler","_oModulesData","define","sModuleName","aToolsNames","oInstances","start","oStartData","oInstanceData","getInstance","oInstance","instantiate","bIsStarted","onStart","stop","bAndDestroy","onStop","onDestroy","oModuleData","nToolIndex","aTools","unshift","__tools__","instanceProp","sPropName","getModules","sInstanceName"],"mappings":";;CAKG,SAAWA,GAEb,YAEA,IAAIC,GAAS,KACZC,GAAS,EACTC,GAAU,EACVC,EAAgBC,OAAOC,UACvBC,EAAcH,EAAcI,eAC5BC,EAAYL,EAAcM,SAMvBC,GAKHC,QAAU,QAMVC,UAAYV,EAKZW,OAASb,EAKTc,QAAUd,EAKVe,MAAQf,EAKRgB,MAAQhB,GAETgB,EAAQN,EAASM,MACjBF,EAAUJ,EAASI,QACnBC,EAAQL,EAASK,KAMXE,OAAMZ,UAAUa,UAErBD,MAAMZ,UAAUa,QAAU,SAAUC,EAAIC,GAEvC,GAAIC,GAAGC,CACP,KAAMD,EAAI,EAAGC,EAAMC,KAAKC,OAAYF,EAAJD,IAAWA,EAErCA,IAAKE,OAETJ,EAAGM,KAAML,EAAOG,KAAMF,GAAKA,EAAGE,QAM5BG,SAASrB,UAAUsB,OAExBD,SAASrB,UAAUsB,KAAO,SAAUC,GAEnC,GAAqB,kBAATL,MAGX,KAAM,IAAIM,WAAW,uEAGtB,IAAIC,GAAQb,MAAMZ,UAAU0B,MAAMN,KAAMO,UAAW,GAClDC,EAAUV,KACVW,EAAO,aACPC,EAAS,WAER,MAAOF,GAAQG,MAAOb,eAAgBW,IAAQN,EAAQL,KAAOK,EAC5DE,EAAMO,OAAQpB,MAAMZ,UAAU0B,MAAMN,KAAMO,aAM7C,OAHAE,GAAK7B,UAAYkB,KAAKlB,UACtB8B,EAAO9B,UAAY,GAAI6B,GAEhBC,IAIHG,OAAOjC,UAAUkC,OAEtBD,OAAOjC,UAAUkC,KAAO,WAEvB,MAAOhB,MAAKiB,QAAS,aAAc,MAQrCxB,GAOCyB,QAAU,SAAWC,EAAOC,GAE3B,MAAOnC,GAAUiB,KAAMiB,KAAY,WAAWC,EAAW,KAQ1DC,WAAa,SAAWF,GAEvB,MAAO1B,GAAMyB,QAASC,EAAO,aAQ9BG,SAAW,SAAWH,GAErB,MAAO1B,GAAMyB,QAASC,EAAO,WAQ9BI,QAAU,SAAWJ,GAEpB,MAAO1B,GAAMyB,QAASC,EAAO,UAQ9BK,MAAQ,SAAWC,EAASC,GAE3B,GAAMD,GAAYhC,EAAM6B,SAAUG,GAKlC,IAAM,GAAIE,KAAaF,GAEjB1C,EAAYmB,KAAMuB,EAASE,IAE/BD,EAAYD,EAAQE,GAAYA,IAanCC,OAAS,WAWR,IATA,GAAIC,GAAOpB,UACVqB,EAAaD,EAAK5B,OAClB8B,EAAS,EACTC,EAAQH,EAAK,OACbI,EAAS,SAAWC,EAAKC,GAExBH,EAAMG,GAAO1C,EAAM6B,SAAUY,GAAQzC,EAAMmC,OAAQI,EAAMG,GAAMD,GAAQA,GAG1DJ,EAAPC,EAAmBA,IAE1BtC,EAAM+B,MAAOK,EAAKE,GAASE,EAG5B,OAAOD,IAWRI,kBAAoB,SAAWC,EAAUC,EAAQC,GAEhD,GAAKD,EAAOE,cAEX,MAAOF,EAGR,IAAIG,GAAkB,WAErB,IAEC,MAAOH,GAAOzB,MAAOwB,EAAU5B,WAEhC,MAAQiC,GAEPvD,EAASK,MAAMmD,IAAKJ,EAAUG,EAAOE,UAMvC,OAFAH,GAAgBD,cAAgB9D,EAEzB+D,GAQRI,mBAAqB,SAAWC,EAAWvC,GAE1C,MAAOuC,GAAUjC,MAAOpC,EAAQ8B,IAUlC,IAAIwC,MAKHC,EAAW,EAMZzD,IAMC0D,QAAU,SAAWC,GAEpB,GAAIC,GAAYJ,EAAQG,EACxB,OAAOC,IAAaA,EAAUC,WAAaD,EAAUC,YAAaJ,IAAcvE,GAQjF4E,SAAW,SAAWH,EAAWE,GAEhC,MAAKL,GAAQG,KAAezD,EAAM4B,WAAY+B,GAEtCzE,GAGRoE,EAAQG,IACPE,UAAYA,GAGN1E,KAUTc,GAKCmD,IAAM,SAAWW,GAEX9E,EAAK+E,SAAW/E,EAAK+E,QAAQC,OAEjChF,EAAK+E,QAAQC,MAAOF,IAOtBG,OAAS,SAAWH,GAEnB,GAAKnE,EAASE,UAEb,KAAM,IAAIG,GAAO8D,EAIjBI,gBAAef,IAAKW,IAgBvB,IAAIK,KAMJrE,SAQCsE,OAAS,SAAWC,EAAaC,EAAahB,GAE7C,MAAKa,GAAcE,KAAiBpE,EAAM4B,WAAYyB,GAE9CnE,GAGRgF,EAAcE,IACbf,UAAYA,EACZiB,cACAD,YAAcA,GAGRpF,IASRsF,MAAQ,SAAWH,EAAaI,GAE/B,GAAIC,GAAgB5E,OAAO6E,YAAaN,EAgBxC,OAdMK,KAGLA,EAAgBP,EAAcE,GAAaE,WAAWF,IACrDO,UAAY9E,OAAO+E,YAAaR,KAI5BK,EAAcI,aAEnBJ,EAAcE,UAAUG,QAASN,GACjCC,EAAcI,WAAa5F,GAGrBwF,EAAcI,YAStBE,KAAO,SAAWX,EAAaY,GAE9B,GAAIP,GAAgB5E,OAAO6E,YAAaN,EAExC,OAAMK,IAAkBA,EAAcE,WAKjCF,EAAcI,aAEb7E,EAAM4B,WAAY6C,EAAcE,UAAUM,SAE9CR,EAAcE,UAAUM,SAEzBR,EAAcI,WAAa3F,GAGvB8F,GAEChF,EAAM4B,WAAY6C,EAAcE,UAAUO,YAE9CT,EAAcE,UAAUO,kBAElBhB,GAAcE,GACdnF,IAGAwF,EAAcI,YAtBd3F,GA8BT0F,YAAc,SAAWR,GAExB,GAGCX,GAEAkB,EALGQ,EAAcjB,EAAcE,GAC/BC,EAAcc,EAAYd,YAC1Be,EAAaf,EAAY7D,OAEzB6E,IAQD,KALMF,GAELpF,EAAMiE,OAAQ,eAAeI,EAAY,qBAGlCgB,KAEP3B,EAAYY,EAAYe,GACxBC,EAAOC,QAASxF,EAAQ0D,QAASC,GAKlC,IAFAkB,EAAY3E,EAAMoD,mBAAoB+B,EAAY9B,UAAWgC,GAExD3F,EAASE,UAIb,IAFA+E,EAAUY,UAAYZ,EAAUY,cAChCH,EAAaf,EAAY7D,OACjB4E,KAEPT,EAAUY,UAAUlB,EAAYe,IAAeC,EAAOD,OAMvDpF,GAAM+B,MAAO4C,EAAW,SAAWa,EAAcC,GAE3CzF,EAAM4B,WAAY4D,KAEtBb,EAAUc,GAAazF,EAAM2C,kBAAmBgC,EAAWa,EAAc,oBAAoBpB,EAAY,uBAAuBqB,EAAU,SAK7I,OAAOd,IAQRe,WAAa,WAEZ,MAAOxB,IAWRQ,YAAc,SAAWN,EAAauB,GAErC,GAAIR,GAAcjB,EAAcE,EAUhC,OATMe,IAELpF,EAAMiE,OAAQ,eAAeI,EAAY,qBAEZ,mBAAlBuB,KAGXA,EAAgBvB,GAEVe,EAAYb,WAAWqB,KAKhC5G,EAAKW,SAAWA,EAEXX,EAAKoF,QAETpF,EAAKoF,OAAQ,WAAYzE,IAEvBa"} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .bower.json 3 | npm-debug.log 4 | /node_modules 5 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | May 10, 2014 - v1.0.2 2 | 3 | * Added bower.json and updated package.json (devDependencies, ...) 4 | * Added .gitignore 5 | * Build : renamed Footer.js and Header.js to Outro.js and Intro.js. 6 | * Grunt : changed banner position with (uglify task). 7 | * Doc : fixed a typo. 8 | 9 | November 30, 2013 - v1.0.1 10 | 11 | * Build : reorganized the files, added Footer.js and Header.js to optimize the final build file and minification. 12 | * Doc : added links in README to the extensions documentation and a README file in src/extensions. 13 | * Utils : renamed Utils.forEach to Utils.forIn (consistency). 14 | * Grunt : added the "clean" task to remove temporary build files. 15 | * Tests : removed duplicated "inheritance+instances" tests files. 16 | 17 | November 13, 2013 - v1.0.0 18 | 19 | * Massive refactoring/code/files reorganization, API changes. 20 | * Removed the sandbox in favor of a toolbox, allowing module's "tools" to be passed to the creator function. 21 | * Added a declarative approach for loading modules from the DOM using "data-" attributes. 22 | * Added strategies support for lazy loading of the modules declared in the DOM. 23 | * Added package.json and Gruntfile.js. 24 | * Added a SublimeText snippet for creating a new module (see the "misc" folder). 25 | * Doc : massive update. 26 | * Demos : todolist (for toolbox/mediator/AMD usage), chat (multiple instances), widgets (inheritance). 27 | 28 | March 13, 2013 29 | 30 | * Extension / AMD 0.2.0 : Added a check on the optional callback function passed to register and registerAndStart. 31 | * Extension / AMD 0.2.0 : Replaced onError by a proper setErrorHandler method. 32 | * Extension / ExtAPI 0.1.1 : Starts a module even if register returns false. 33 | 34 | March 01, 2013 - v0.4.0 35 | 36 | * Core : improved consistency by adding TinyCore.Module to regroup all modules-related methods. 37 | * Extension / ExtAPI : split in two extensions : ExtError and ExtModule, refactored accordingly. 38 | * Extension / AMD : refactored accordingly. 39 | * UT : refactored accordingly. 40 | * Demos : refactored accordingly, added an index file with links to all demos. 41 | * Doc : refactored and updated accordingly (added benefits). 42 | 43 | February 23, 2013 - v0.3.1 44 | 45 | * Core : fixed a bug in _fpTryCatchDecorator where the result of the original function call was not returned. 46 | * Demos : added the classic "todo list" demo. 47 | * Demos : added more API usage in the "extapi" demo. 48 | 49 | February 17, 2013 - v0.3.0 50 | 51 | * Core / ExtAPI v0.2.0 : moved automatic topics subscription to the ExtAPI extension. 52 | * UT : added missing tests for the automatic topics subscription. 53 | * Demos : update accordingly. 54 | * Doc : update accordingly. 55 | 56 | February 14, 2013 - v0.2.0 57 | 58 | * Core / ExtAPI v0.1.1 : added various DRY improvements that helped reducing the amount of code. 59 | * Core : added the automatic topics subscription if the "topics" property is present in the module. 60 | * Core : register is now checking if the creator is really a function. 61 | * Sandbox : renamed "register" to "build". 62 | * UT : refactored the tests accordingly. 63 | * Demos : refactored to show how automatic topics subscription works. 64 | * Doc : updated README according to the changes, adding extensions documentation. 65 | 66 | February 12, 2013 - v0.1.1 67 | 68 | * Sandbox : solve small naming inconsistencies in the sandbox pub/sub system (we are now using "topics" instead of "events", thanks to joseprio). 69 | * Sandbox : added an optional context argument to the subscribe method. 70 | * Core : remove deep methods decoration because when subscribing, if the handler is a method of an object defined as a property of the module itself, the context of execution cannot be set correctly. 71 | * Sandbox : subscribers handlers are now also wrapped into a try-catch statement. 72 | 73 | February 10, 2013 - v0.1.0 74 | 75 | * Initial release. 76 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function ( grunt ) 2 | { 3 | // Project configuration. 4 | grunt.initConfig( 5 | { 6 | pkg: grunt.file.readJSON( 'package.json' ), 7 | concat : { 8 | core: 9 | { 10 | src: [ 11 | 'src/Intro.js', 12 | 'src/TinyCore.Utils.js', 13 | 'src/TinyCore.Toolbox.js', 14 | 'src/TinyCore.Error.js', 15 | 'src/TinyCore.Module.js', 16 | 'src/Outro.js' 17 | ], 18 | dest: 'build/TinyCore.js' 19 | }, 20 | amd: 21 | { 22 | files: 23 | { 24 | 'build/extensions/AMD/require-2.1.4.min.js' : ['src/extensions/AMD/require-2.1.4.min.js'], 25 | 'build/extensions/AMD/TinyCore.AMD+domBoot.js' : [ 26 | 'src/extensions/AMD/TinyCore.AMD.js', 27 | 'src/extensions/AMD/TinyCore.AMD.domBoot.js' 28 | ] 29 | } 30 | } 31 | }, 32 | jshint: 33 | { 34 | core: 35 | { 36 | options: 37 | { 38 | // W055: A constructor name should start with an uppercase letter. 39 | // Found in MDN's Function.prototype.bind shim. 40 | '-W055': true 41 | }, 42 | src: [ 'build/TinyCore.js' ] 43 | }, 44 | mediator: 45 | { 46 | src: ['src/tools/mediator/TinyCore.Toolbox.Mediator.js'] 47 | }, 48 | amd: 49 | { 50 | src: ['src/extensions/amd/TinyCore.AMD.js', 'build/extensions/amd/TinyCore.AMD.domBoot.js'] 51 | }, 52 | inheritance: 53 | { 54 | src: ['src/extensions/Inheritance/TinyCore.Module.Inheritance.js'] 55 | }, 56 | instances: 57 | { 58 | src: ['src/extensions/Instances/TinyCore.Module.Instances.js'] 59 | }, 60 | jasmine: 61 | { 62 | src: ['src/extensions/Jasmine/TinyCore.Toolbox.Jasmine.js'] 63 | } 64 | }, 65 | uglify: 66 | { 67 | options: 68 | { 69 | preserveComments: 'some', 70 | report: 'gzip' 71 | }, 72 | core: 73 | { 74 | options : { 75 | banner: '/*! TinyCore v<%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd") %>) | (c) 2013 Marc Mignonsin | <%= pkg.license %> license */\n' 76 | }, 77 | files: 78 | { 79 | 'build/TinyCore.min.js' : ['build/TinyCore.js'] 80 | } 81 | }, 82 | mediator: 83 | { 84 | files: 85 | { 86 | 'build/tools/mediator/TinyCore.Toolbox.Mediator.min.js' : ['src/tools/mediator/TinyCore.Toolbox.Mediator.js'] 87 | } 88 | }, 89 | amd: 90 | { 91 | files: 92 | { 93 | 'build/extensions/AMD/TinyCore.AMD.min.js' : ['src/extensions/amd/TinyCore.AMD.js'], 94 | 'build/extensions/AMD/TinyCore.AMD+domBoot.min.js' : ['build/extensions/amd/TinyCore.AMD+domBoot.js'] 95 | } 96 | }, 97 | inheritance: 98 | { 99 | files: 100 | { 101 | 'build/extensions/Inheritance/TinyCore.Module.Inheritance.min.js' : ['src/extensions/Inheritance/TinyCore.Module.Inheritance.js'] 102 | } 103 | }, 104 | instances: 105 | { 106 | files: 107 | { 108 | 'build/extensions/Instances/TinyCore.Module.Instances.min.js' : ['src/extensions/Instances/TinyCore.Module.Instances.js'] 109 | } 110 | }, 111 | jasmine: 112 | { 113 | files: 114 | { 115 | 'build/extensions/Jasmine/TinyCore.Toolbox.Jasmine.min.js' : ['src/extensions/Jasmine/TinyCore.Toolbox.Jasmine.js'] 116 | } 117 | } 118 | }, 119 | clean : { 120 | amd : ['build/extensions/AMD/TinyCore.AMD+domBoot.js'] 121 | }, 122 | karma: 123 | { 124 | core: { 125 | configFile: 'test/karma/core.conf.js' 126 | }, 127 | mediator: { 128 | configFile: 'test/karma/tools.mediator.conf.js' 129 | }, 130 | amd: { 131 | configFile: 'test/karma/ext.amd.conf.js' 132 | }, 133 | domBoot: { 134 | configFile: 'test/karma/ext.amd.domBoot.conf.js' 135 | }, 136 | inheritance: { 137 | configFile: 'test/karma/ext.inheritance.conf.js' 138 | }, 139 | instances: { 140 | configFile: 'test/karma/ext.instances.conf.js' 141 | }, 142 | inherinstances: { 143 | configFile: 'test/karma/ext.inheritance+instances.conf.js' 144 | }, 145 | jasmine: { 146 | configFile: 'test/karma/ext.jasmine.conf.js' 147 | } 148 | } 149 | } ); 150 | 151 | grunt.loadNpmTasks( 'grunt-contrib-concat' ); 152 | grunt.loadNpmTasks( 'grunt-contrib-jshint' ); 153 | grunt.loadNpmTasks( 'grunt-contrib-uglify' ); 154 | grunt.loadNpmTasks( 'grunt-contrib-clean' ); 155 | grunt.loadNpmTasks( 'grunt-karma' ); 156 | 157 | // Default task(s). 158 | grunt.registerTask( 'build-core', [ 'concat:core', 'jshint:core', 'uglify:core' ] ); 159 | grunt.registerTask( 'build-mediator', [ 'jshint:mediator', 'uglify:mediator' ] ); 160 | grunt.registerTask( 'build-amd', [ 'jshint:amd', 'concat:amd', 'uglify:amd', 'clean:amd' ] ); 161 | grunt.registerTask( 'build-inheritance', [ 'jshint:inheritance', 'uglify:inheritance' ] ); 162 | grunt.registerTask( 'build-instances', [ 'jshint:instances', 'uglify:instances' ] ); 163 | grunt.registerTask( 'build-jasmine', [ 'jshint:jasmine', 'uglify:jasmine' ] ); 164 | grunt.registerTask( 'build-all', [ 'build-core', 'build-mediator', 'build-amd', 'build-inheritance', 'build-instances', 'build-jasmine' ] ); 165 | 166 | grunt.registerTask( 'karma-all', [ 'karma:core', 'karma:mediator', 'karma:amd', 'karma:domBoot', 'karma:inheritance', 'karma:instances', 'karma:inherinstances', 'karma:jasmine' ] ); 167 | grunt.registerTask( 'build-all-plus-karma', [ 'karma-all', 'build-all' ] ); 168 | 169 | }; 170 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Marc Mignonsin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TinyCore.js", 3 | "description": "A JavaScript library providing a modular and decoupled architecture that helps you create scalable and easy-to-maintain Web applications.", 4 | "version": "1.0.2", 5 | "main": "build/TinyCore.js", 6 | "license": "MIT", 7 | "ignore": [ 8 | "node_modules" 9 | ], 10 | "keywords": [ 11 | "js","architecture","module","scalable","spa" 12 | ], 13 | "authors": [ 14 | "Marc Mignonsin" 15 | ], 16 | "homepage": "https://github.com/mawrkus/tinycore", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/mawrkus/TinyCore.git" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /build/TinyCore.min.js: -------------------------------------------------------------------------------- 1 | /*! TinyCore v1.0.2 (2014-05-10) | (c) 2013 Marc Mignonsin | MIT license */ 2 | !function(a){"use strict";var b=null,c=!0,d=!1,e=Object.prototype,f=e.hasOwnProperty,g=e.toString,h={version:"1.0.2",debugMode:d,Module:b,Toolbox:b,Error:b,Utils:b};Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c,d;for(c=0,d=this.length;d>c;++c)c in this&&a.call(b,this[c],c,this)}),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),String.prototype.trim||(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")});var i={isClass:function(a,b){return g.call(a)==="[object "+b+"]"},isFunction:function(a){return i.isClass(a,"Function")},isObject:function(a){return i.isClass(a,"Object")},isArray:function(a){return i.isClass(a,"Array")},forIn:function(a,b){if(a&&i.isObject(a))for(var c in a)f.call(a,c)&&b(a[c],c)},extend:function(){for(var a=arguments,b=a.length,c=1,d=a[0]||{},e=function(a,b){d[b]=i.isObject(a)?i.extend(d[b],a):a};b>c;c++)i.forIn(a[c],e);return d},tryCatchDecorator:function(a,b,d){if(b.__decorated__)return b;var e=function(){try{return b.apply(a,arguments)}catch(c){h.Error.log(d+c.message)}};return e.__decorated__=c,e},createModuleObject:function(a,c){return a.apply(b,c)}};h.Utils=i;var j={},k=-1;h.Toolbox={request:function(a){var c=j[a];return c&&c.fpFactory&&c.fpFactory(++k)||b},register:function(a,b){return j[a]||!i.isFunction(b)?d:(j[a]={fpFactory:b},c)}},h.Error={log:function(b){a.console&&a.console.error&&a.console.error(b)},report:function(a){if(h.debugMode)throw new Error(a);this.log(a)}};var l={};h.Module={define:function(a,b,e){return l[a]||!i.isFunction(e)?d:(l[a]={fpCreator:e,oInstances:{},aToolsNames:b},c)},start:function(a,b){var d=this.getInstance(a);return d||(d=l[a].oInstances[a]={oInstance:this.instantiate(a)}),d.bIsStarted||(d.oInstance.onStart(b),d.bIsStarted=c),d.bIsStarted},stop:function(a,b){var e=this.getInstance(a);return e&&e.oInstance?(e.bIsStarted&&(i.isFunction(e.oInstance.onStop)&&e.oInstance.onStop(),e.bIsStarted=d),b?(i.isFunction(e.oInstance.onDestroy)&&e.oInstance.onDestroy(),delete l[a],c):!e.bIsStarted):d},instantiate:function(a){var b,c,d=l[a],e=d.aToolsNames,f=e.length,g=[];for(d||Error.report('The module "'+a+'" is not defined!');f--;)b=e[f],g.unshift(h.Toolbox.request(b));if(c=i.createModuleObject(d.fpCreator,g),h.debugMode)for(c.__tools__=c.__tools__||{},f=e.length;f--;)c.__tools__[e[f]]=g[f];else i.forIn(c,function(b,d){i.isFunction(b)&&(c[d]=i.tryCatchDecorator(c,b,'Error in module "'+a+'" executing method "'+d+'": '))});return c},getModules:function(){return l},getInstance:function(a,b){var c=l[a];return c||h.Error.report('The module "'+a+'" is not defined!'),"undefined"==typeof b&&(b=a),c.oInstances[b]}},a.TinyCore=h,a.define&&a.define.amd&&a.define("TinyCore",h),a.module&&a.module.exports&&(a.module.exports=h)}(this); -------------------------------------------------------------------------------- /build/extensions/AMD/TinyCore.AMD+domBoot.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";var b=a.TinyCore,c=b.Utils,d=b.Module;if(!a.require||!a.define)throw new Error("Cannot add AMD extension to TinyCore: require.js seems to be missing!");var e={baseUrl:"modules"},f={require:c.extend({},e)},g={config:function(b){return"undefined"==typeof b?f:(c.extend(f,b),a.require.config(f.require),void 0)},setErrorHandler:function(b){a.require.onError=b},define:function(b,c,e){a.define(b,c,function(){for(var a=[],f=c.length;f--;)a.unshift(c[f].split("/").pop());d.define(b,a,e)})},require:function(b,c){a.require(b,c)},requireAndStart:function(a,b){var e=[];c.isArray(a)||(a=[a]),a.forEach(function(b,c){"string"==typeof b&&(a[c]=b={name:b,startData:{}}),e.push(b.name)}),g.require(e,function(){a.forEach(function(a){d.start(a.name,a.startData)}),b&&b(a)})}};g.setErrorHandler(function(a){b.Error.log('Error loading module(s) "'+a.requireModules+'": '+a.message)}),b.AMD=g}(this),function(a){"use strict";var b=a.TinyCore,c=b.Utils,d=b.AMD,e=a.JSON,f=document,g=[],h={nodesIgnored:{SCRIPT:!0,IFRAME:!0}},i="data-tc-modules",j=";",k=new RegExp("([\\w-]+)(\\s*:\\s*({[^"+j+"]*}))?"),l="data-tc-defer",m=";",n={},o={},p=null,q=function(){return f.attachEvent?function(a,b,c){a.attachEvent("on"+b,c)}:function(a,b,c){a.addEventListener(b,c,!1)}}(),r=function(){return f.detachEvent?function(a,b,c){a.detachEvent("on"+b,c)}:function(a,b,c){a.removeEventListener(b,c,!1)}}(),s=function(a,b){var c=a.getAttribute(b);return c=c?c.trim():""},t=function(a){var b;for(o.nodesIgnored[a.nodeName]!==!0&&(b=s(a,i),b&&u(a,b)),a=a.firstChild;a;)1===a.nodeType&&t(a),a=a.nextSibling},u=function(a,b){var c,d,e,f,h,i=b.split(j),o=[];i.forEach(function(b){var c,d,e=b.match(k)||[],f=e[1]&&e[1].trim();f&&(c=e[3]&&e[3].trim(),d={name:f},d.startData=c?p(c):{},d.startData.element=a,o.push(d))}),c=s(a,l),c?(d=c.split(m),d.forEach(function(b){b&&(e=b.split(":"),f=e[0]&&e[0].trim(),h=e[1]&&e[1].trim(),n[f]=n[f]||{},n[f][h]=n[f][h]||[],n[f][h].push({node:a,modulesData:o,typeVal:h}))})):g=g.concat(o)},v=function(a){var b=function(b,c){var d=c.node,e=function(){r(d,b,e),h(c.modulesData,a)};q(d,b,e)},e=function(b,c){setTimeout(function(){h(c,a)},b)},g=function(b){var c=function(d){var e=d.pageX||d.clientX+f.body.scrollLeft,g=d.pageY||d.clientY+f.body.scrollTop,i=[];b.forEach(function(b,c){var d=+b.typeVal,f=b.node.getBoundingClientRect(),j=f.left-d,k=f.right+d,l=f.top-d,m=f.bottom+d;e>=j&&k>=e&&g>=l&&m>=g&&(i.push(c),h(b.modulesData,a))}),i.forEach(function(a){b.splice(a,1)}),b.length||r(f,"mousemove",c)};q(f,"mousemove",c)},h=function(a,b){var c=[];a.forEach(function(a){var b=a.name;n._loaded[b]||(n._loaded[b]=!0,c.push(a))}),c.length&&d.requireAndStart(c,b)},i=[];n._loaded={},c.forIn(n.event,function(a,c){a.forEach(function(a){b(c,a)})}),delete n.event,c.forIn(n.time,function(a,b){var c=[];a.forEach(function(a){c=c.concat(a.modulesData)}),e(b,c)}),delete n.time,c.forIn(n.distance,function(a){i=i.concat(a)}),i.length&&g(i),delete n.distance},w={domBoot:function(){var a,h;c.isFunction(arguments[0])?(a=f.body,h=arguments[0]):(a=arguments[0]||f.body,h=arguments[1]),p=function(a){return e.parse(a)},b.debugMode||(p=c.tryCatchDecorator(null,p,"Error while booting from DOM! ")),o=d.config().domBoot,g=[],t(a),g.length&&d.requireAndStart(g,h),v(h)}};c.extend(d.config(),{domBoot:h}),c.extend(d,w)}(this); -------------------------------------------------------------------------------- /build/extensions/AMD/TinyCore.AMD.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";var b=a.TinyCore,c=b.Utils,d=b.Module;if(!a.require||!a.define)throw new Error("Cannot add AMD extension to TinyCore: require.js seems to be missing!");var e={baseUrl:"modules"},f={require:c.extend({},e)},g={config:function(b){return"undefined"==typeof b?f:(c.extend(f,b),a.require.config(f.require),void 0)},setErrorHandler:function(b){a.require.onError=b},define:function(b,c,e){a.define(b,c,function(){for(var a=[],f=c.length;f--;)a.unshift(c[f].split("/").pop());d.define(b,a,e)})},require:function(b,c){a.require(b,c)},requireAndStart:function(a,b){var e=[];c.isArray(a)||(a=[a]),a.forEach(function(b,c){"string"==typeof b&&(a[c]=b={name:b,startData:{}}),e.push(b.name)}),g.require(e,function(){a.forEach(function(a){d.start(a.name,a.startData)}),b&&b(a)})}};g.setErrorHandler(function(a){b.Error.log('Error loading module(s) "'+a.requireModules+'": '+a.message)}),b.AMD=g}(this); -------------------------------------------------------------------------------- /build/extensions/Inheritance/TinyCore.Module.Inheritance.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";var b=a.TinyCore,c=b.Utils,d=b.Module,e={inherit:function(a,b,e,f){var g=d.getModules();return a!==b&&g[a]&&!g[b]&&c.isFunction(f)?(g[b]={fpCreator:f,oInstances:{},aToolsNames:e,sSuper:a},!0):!1},instantiate:function(a){return function(c){var e,f=d.getModules(),g=f[c],h=[],i=c,j={};g||b.Error.report('The module "'+c+'" is not defined!');do h.push({name:i,creator:g.fpCreator}),i=g.sSuper,g=f[i];while(g);e=h.length-1;do h[e].creator.prototype=j,j=a.call(d,h[e].name);while(e--);return j}}(d.instantiate)},f={createModuleObject:function(a,b){var d=a.apply(null,b),e=function(){},f=a.prototype;return c.forIn(d,function(a,b){var e=f[b];f[b]=c.isObject(e)?c.isObject(d[b])?c.extend(f[b],d[b]):d[b]:c.isFunction(e)?function(){var a,c=this._super;return this._super=e,a=d[b].apply(this,arguments),this._super=c,a}:d[b]}),e.prototype=f,new e}};c.extend(c,f),c.extend(d,e)}(this); -------------------------------------------------------------------------------- /build/extensions/Instances/TinyCore.Module.Instances.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";var b=a.TinyCore,c=b.Utils,d=b.Module,e={startInstance:function(a,b,c){var e=d.getInstance(a,b);return e||(e=d.getModules()[a].oInstances[b]={oInstance:d.instantiate(a)}),e.bIsStarted||(e.oInstance.onStart(c,b),e.bIsStarted=!0),e.bIsStarted},stopInstance:function(a,b,e){var f=d.getInstance(a,b);return f&&f.oInstance?(f.bIsStarted&&(c.isFunction(f.oInstance.onStop)&&f.oInstance.onStop(),f.bIsStarted=!1),e?(c.isFunction(f.oInstance.onDestroy)&&f.oInstance.onDestroy(),delete d.getModules()[a].oInstances[b],!0):!f.bIsStarted):!1},start:function(a){return function(b,e){var f=d.getModules()[b],g=!1,h=!0;return f&&c.forIn(f.oInstances,function(a,c){g=!0,d.startInstance(b,c,e)||(h=!1)}),g?h:a.apply(d,arguments)}}(d.start),stop:function(a,b){var e=d.getModules(),f=!0;return c.forIn(e[a].oInstances,function(c,e){d.stopInstance(a,e,b)||(f=!1)}),b&&f&&delete e[a],f}};c.extend(d,e)}(this); -------------------------------------------------------------------------------- /build/extensions/Jasmine/TinyCore.Toolbox.Jasmine.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";var b=a.TinyCore,c=b.Utils,d=b.Toolbox,e=a.jasmine,f=a.spyOn;if(!e||!c.isFunction(f))throw new Error("Cannot add Jasmine extension to TinyCore: Jasmine seems to be missing!");var g={},h=function(a){return a?(c.forIn(a,function(b,d){c.isFunction(b)&&!b.isSpy&&f(a,d)}),a):null},i={request:function(a){return function(b){var c=g[b];return c?h(c):(c=a.apply(d,arguments),g[b]=h(c),c)}}(d.request)};c.extend(d,i)}(this); -------------------------------------------------------------------------------- /build/output.txt: -------------------------------------------------------------------------------- 1 | Running "concat:core" (concat) task 2 | File "build/TinyCore.js" created. 3 | 4 | Running "jshint:core" (jshint) task 5 | >> 1 file lint free. 6 | 7 | Running "uglify:core" (uglify) task 8 | File "build/TinyCore.min.js" created. 9 | Original: 13085 bytes. 10 | Minified: 3129 bytes. 11 | Gzipped: 729 bytes. 12 | 13 | Running "jshint:mediator" (jshint) task 14 | >> 1 file lint free. 15 | 16 | Running "uglify:mediator" (uglify) task 17 | File "build/tools/mediator/TinyCore.Toolbox.Mediator.min.js" created. 18 | Original: 3680 bytes. 19 | Minified: 784 bytes. 20 | Gzipped: 214 bytes. 21 | 22 | Running "jshint:amd" (jshint) task 23 | >> 1 file lint free. 24 | 25 | Running "concat:amd" (concat) task 26 | File "build/extensions/AMD/require-2.1.4.min.js" created. 27 | File "build/extensions/AMD/TinyCore.AMD+domBoot.js" created. 28 | 29 | Running "uglify:amd" (uglify) task 30 | File "build/extensions/AMD/TinyCore.AMD.min.js" created. 31 | Original: 3619 bytes. 32 | Minified: 911 bytes. 33 | Gzipped: 252 bytes. 34 | File "build/extensions/AMD/TinyCore.AMD+domBoot.min.js" created. 35 | Original: 14021 bytes. 36 | Minified: 3328 bytes. 37 | Gzipped: 809 bytes. 38 | 39 | Running "clean:amd" (clean) task 40 | Cleaning build/extensions/AMD/TinyCore.AMD+domBoot.js...OK 41 | 42 | Running "jshint:inheritance" (jshint) task 43 | >> 1 file lint free. 44 | 45 | Running "uglify:inheritance" (uglify) task 46 | File "build/extensions/Inheritance/TinyCore.Module.Inheritance.min.js" created. 47 | Original: 3926 bytes. 48 | Minified: 901 bytes. 49 | Gzipped: 252 bytes. 50 | 51 | Running "jshint:instances" (jshint) task 52 | >> 1 file lint free. 53 | 54 | Running "uglify:instances" (uglify) task 55 | File "build/extensions/Instances/TinyCore.Module.Instances.min.js" created. 56 | Original: 4303 bytes. 57 | Minified: 915 bytes. 58 | Gzipped: 222 bytes. 59 | 60 | Running "jshint:jasmine" (jshint) task 61 | >> 1 file lint free. 62 | 63 | Running "uglify:jasmine" (uglify) task 64 | File "build/extensions/Jasmine/TinyCore.Toolbox.Jasmine.min.js" created. 65 | Original: 1717 bytes. 66 | Minified: 442 bytes. 67 | Gzipped: 143 bytes. 68 | 69 | Done, without errors. 70 | [Finished in 3.2s] 71 | -------------------------------------------------------------------------------- /build/tools/mediator/TinyCore.Toolbox.Mediator.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"use strict";var b=a.TinyCore,c=b.Utils,d=function(a){this.nSubscriberID=a};d.prototype=function(){var d={},e={};return{subscribe:function(a,f,g){var h=c.isArray(a)?a:[a],i=this.nSubscriberID;h.forEach(function(a){d[a]=d[a]||{},d[a][i]||(d[a][i]=b.debugMode?f.bind(g):c.tryCatchDecorator(g,f,'Error publishing topic "'+a+'": '),e[i]=e[i]||[],e[i].push(a))})},publish:function(b,e,f){var g=f?e:e&&c.extend({},e);c.forIn(d[b],function(c){a.setTimeout(function(){c({name:b,data:g})},0)})},unsubscribe:function(a){for(var b,e=c.isArray(a)?a:[a],f=e.length,g=this.nSubscriberID;f--;)b=d[e[f]],b&&b[g]&&delete b[g]},unsubscribeAll:function(){e[this.nSubscriberID]&&this.unsubscribe(e[this.nSubscriberID])}}}(),b.Toolbox.register("mediator",function(a){return new d(a)})}(this); -------------------------------------------------------------------------------- /misc/sublimetext/mod.sublime-snippet: -------------------------------------------------------------------------------- 1 | 2 | 37 | mod 38 | 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TinyCore.js", 3 | "version": "1.0.2", 4 | "description": "A JavaScript library providing a modular and decoupled architecture that helps you create scalable and easy-to-maintain Web applications.", 5 | "keywords": [ 6 | "js", 7 | "architecture", 8 | "module", 9 | "scalable", 10 | "spa" 11 | ], 12 | "author": "Marc Mignonsin", 13 | "homepage": "https://github.com/mawrkus/tinycore", 14 | "license": "MIT", 15 | "bugs": "https://github.com/mawrkus/TinyCore/issues", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/mawrkus/TinyCore.git" 19 | }, 20 | "optionalDependencies": { 21 | "requirejs": "~2.1.4" 22 | }, 23 | "devDependencies": { 24 | "grunt": "~0.4.4", 25 | "grunt-cli": "^0.1.13", 26 | "grunt-contrib-clean": "~0.5.0", 27 | "grunt-contrib-concat": "~0.4.0", 28 | "grunt-contrib-jshint": "~0.10.0", 29 | "grunt-contrib-uglify": "^0.4.0", 30 | "grunt-karma": "^0.8.3", 31 | "karma": "^0.12.15", 32 | "karma-chrome-launcher": "^0.1.3", 33 | "karma-firefox-launcher": "^0.1.3", 34 | "karma-jasmine": "^0.1.5", 35 | "karma-opera-launcher": "^0.1.0", 36 | "karma-phantomjs-launcher": "^0.1.4", 37 | "karma-requirejs": "^0.2.1", 38 | "karma-safari-launcher": "^0.1.1", 39 | "karma-script-launcher": "^0.1.0" 40 | }, 41 | "engines": { 42 | "node": ">=0.10" 43 | }, 44 | "scripts": { 45 | "test": "node node_modules/karma/bin/karma start test/karma/core.conf.js --single-run --browsers PhantomJS" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Intro.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyCore.js 3 | * A tiny yet extensible JS modular architecture. 4 | * @author mawrkus (web@sparring-partner.be) 5 | */ 6 | ;( function ( oEnv ) 7 | { 8 | 'use strict'; 9 | 10 | var _null_ = null, 11 | _true_ = true, 12 | _false_ = false, 13 | _oObjectProto = Object.prototype, 14 | _hasOwnProp = _oObjectProto.hasOwnProperty, 15 | _toString = _oObjectProto.toString; 16 | 17 | /** 18 | * The core 19 | * @type {Object} 20 | */ 21 | var TinyCore = { 22 | /** 23 | * Current version 24 | * @type {String} 25 | */ 26 | version : '1.0.2', 27 | /** 28 | * Debug mode : if true, error in modules methods and topics subscribers will not be caught, 29 | * if false, errors will be caught and logged using the error handler. 30 | * @type {Boolean} 31 | */ 32 | debugMode : _false_, 33 | /** 34 | * The modules manager. 35 | * @type {Object} 36 | */ 37 | Module : _null_, 38 | /** 39 | * The tools factory. 40 | * @type {Object} 41 | */ 42 | Toolbox : _null_, 43 | /** 44 | * The error handler. 45 | * @type {Object} 46 | */ 47 | Error : _null_, 48 | /** 49 | * Utilities functions. 50 | * @type {Object} 51 | */ 52 | Utils : _null_ 53 | }; 54 | -------------------------------------------------------------------------------- /src/Outro.js: -------------------------------------------------------------------------------- 1 | // Add TinyCore to the environment. 2 | oEnv.TinyCore = TinyCore; 3 | 4 | if ( oEnv.define && oEnv.define.amd ) 5 | { 6 | oEnv.define( 'TinyCore', TinyCore ); 7 | } 8 | 9 | if ( oEnv.module && oEnv.module.exports ) 10 | { 11 | oEnv.module.exports = TinyCore; 12 | } 13 | } ( this ) ); 14 | -------------------------------------------------------------------------------- /src/TinyCore.Error.js: -------------------------------------------------------------------------------- 1 | /* --------- Error handling --------- */ 2 | 3 | /** 4 | * * The error handler. 5 | * @type {Object} 6 | */ 7 | TinyCore.Error = { 8 | /** 9 | * * Logs an error message. 10 | * @param {String} sMsg 11 | */ 12 | log : function ( sMsg ) 13 | { 14 | if ( oEnv.console && oEnv.console.error ) 15 | { 16 | oEnv.console.error( sMsg ); 17 | } 18 | }, 19 | /** 20 | * Throws an exception if in debug mode, log the error essage if not. 21 | * @param {String} sMsg 22 | */ 23 | report : function ( sMsg ) 24 | { 25 | if ( TinyCore.debugMode ) 26 | { 27 | throw new Error( sMsg ); 28 | } 29 | else 30 | { 31 | this.log( sMsg ); 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /src/TinyCore.Module.js: -------------------------------------------------------------------------------- 1 | /* --------- Modules management --------- */ 2 | 3 | /** 4 | * The modules data, where each key is a module name and the associated value an object holding the module data. 5 | * @type {Object} 6 | * @type {Array} oModuleData.aDependencies An array holding the IDs of the module's dependencies 7 | * @type {Function} oModuleData.fpCreator The function that creates a new module's instance 8 | * @type {Object} oModuleData.oInstances The module's instances data, where each key is an instance name and the associated value the instance data 9 | * @type {Object} oInstanceData.oInstance A particular module instance 10 | * @type {Boolean} oInstanceData.bIsStarted Whether the module instance is started or not 11 | */ 12 | var _oModulesData = {}; 13 | 14 | /** 15 | * The modules manager. 16 | * @type {Object} 17 | */ 18 | TinyCore.Module = { 19 | /** 20 | * Defines a new module. 21 | * @param {String} sModuleName The module name 22 | * @param {Array} aToolsNames An array of tools names, that will be requested to the Toolbox and passed as arguments of fpCreator 23 | * @param {Function} fpCreator The function that creates and returns a module instance 24 | * @return {Boolean} Whether the module was successfuly or not. 25 | */ 26 | define : function ( sModuleName, aToolsNames, fpCreator ) 27 | { 28 | if ( _oModulesData[sModuleName] || !_oUtils.isFunction( fpCreator ) ) 29 | { 30 | return _false_; 31 | } 32 | 33 | _oModulesData[sModuleName] = { 34 | fpCreator : fpCreator, 35 | oInstances : {}, 36 | aToolsNames : aToolsNames 37 | }; 38 | 39 | return _true_; 40 | }, 41 | /** 42 | * Starts a module by creating a new module instance and calling its "onStart" method with oStartData passed as parameter. 43 | * @param {String} sModuleName The module name 44 | * @param {Object} oStartData Optional, the data to pass to the module "onStart" method 45 | * @return {Boolean} Whether the module has been started or not 46 | * @throws {Error} If the module has not been defined 47 | */ 48 | start : function ( sModuleName, oStartData ) 49 | { 50 | var oInstanceData = this.getInstance( sModuleName ); 51 | 52 | if ( !oInstanceData ) 53 | { 54 | // We use the module name as the default instance name. 55 | oInstanceData = _oModulesData[sModuleName].oInstances[sModuleName] = { 56 | oInstance : this.instantiate( sModuleName ) 57 | }; 58 | } 59 | 60 | if ( !oInstanceData.bIsStarted ) 61 | { 62 | oInstanceData.oInstance.onStart( oStartData ); // onStart must be defined in the module. 63 | oInstanceData.bIsStarted = _true_; 64 | } 65 | 66 | return oInstanceData.bIsStarted; 67 | }, 68 | /** 69 | * Stops a module instance by calling its "onStop" method (if it exists) and by unsubscribing from all the subscribed topics. 70 | * @param {String} sModuleName The module name 71 | * @param {Boolean} Whether the module should also be destroyed or not 72 | * @return {Boolean} Whether the module has been stopped or not 73 | * @throws {Error} If the module has not been defined 74 | */ 75 | stop : function ( sModuleName, bAndDestroy ) 76 | { 77 | var oInstanceData = this.getInstance( sModuleName ); 78 | 79 | if ( !oInstanceData || !oInstanceData.oInstance ) 80 | { 81 | return _false_; 82 | } 83 | 84 | if ( oInstanceData.bIsStarted ) 85 | { 86 | if ( _oUtils.isFunction( oInstanceData.oInstance.onStop ) ) 87 | { 88 | oInstanceData.oInstance.onStop(); 89 | } 90 | oInstanceData.bIsStarted = _false_; 91 | } 92 | 93 | if ( bAndDestroy ) 94 | { 95 | if ( _oUtils.isFunction( oInstanceData.oInstance.onDestroy ) ) 96 | { 97 | oInstanceData.oInstance.onDestroy(); 98 | } 99 | delete _oModulesData[sModuleName]; 100 | return _true_; 101 | } 102 | 103 | return !oInstanceData.bIsStarted; 104 | }, 105 | /** 106 | * Instantiates a module. 107 | * @param {String} sModuleName The module name 108 | * @return {Object} The module instance 109 | * @throws {Error} If the module has not been defined 110 | */ 111 | instantiate : function ( sModuleName ) 112 | { 113 | var oModuleData = _oModulesData[sModuleName], 114 | aToolsNames = oModuleData.aToolsNames, 115 | nToolIndex = aToolsNames.length, 116 | sToolName, 117 | aTools = [], 118 | oInstance; 119 | 120 | if ( !oModuleData ) 121 | { 122 | Error.report( 'The module "'+sModuleName+'" is not defined!' ); 123 | } 124 | 125 | while ( nToolIndex-- ) 126 | { 127 | sToolName = aToolsNames[nToolIndex]; 128 | aTools.unshift( TinyCore.Toolbox.request( sToolName ) ); 129 | } 130 | 131 | oInstance = _oUtils.createModuleObject( oModuleData.fpCreator, aTools ); 132 | 133 | if ( TinyCore.debugMode ) 134 | { 135 | oInstance.__tools__ = oInstance.__tools__ || {}; 136 | nToolIndex = aToolsNames.length; 137 | while ( nToolIndex-- ) 138 | { 139 | oInstance.__tools__[aToolsNames[nToolIndex]] = aTools[nToolIndex]; 140 | } 141 | } 142 | else 143 | { 144 | // Catch errors by wrapping all the instance's methods into a try-catch statement. 145 | _oUtils.forIn( oInstance, function ( instanceProp, sPropName ) 146 | { 147 | if ( _oUtils.isFunction( instanceProp ) ) 148 | { 149 | oInstance[sPropName] = _oUtils.tryCatchDecorator( oInstance, instanceProp, 'Error in module "'+sModuleName+'" executing method "'+sPropName+'": ' ); 150 | } 151 | } ); 152 | } 153 | 154 | return oInstance; 155 | }, 156 | /** 157 | * Returns the data related to all the modules defined, useful for creating an extension to TinyCore. 158 | * @return {Object} oModuleData The modules data ; each property is itself an object containing the following properties : 159 | * @return {Function} oModuleData.fpCreator The function invoked to create a new module's instance 160 | * @return {Object} oModuleData.oInstances The instances of the module 161 | */ 162 | getModules : function () 163 | { 164 | return _oModulesData; 165 | }, 166 | /** 167 | * Returns a specified module's instance data. 168 | * @param {String} sModuleName 169 | * @param {String} sInstanceName Optional 170 | * @return {Object} oInstanceData The instance data 171 | * @return {Object} oInstanceData.oInstance The instance of the module 172 | * @return {Boolean} oInstanceData.bIsStarted Whether the module is started or not 173 | * @throws {Error} If the module has not been defined 174 | */ 175 | getInstance : function ( sModuleName, sInstanceName ) 176 | { 177 | var oModuleData = _oModulesData[sModuleName]; 178 | if ( !oModuleData ) 179 | { 180 | TinyCore.Error.report( 'The module "'+sModuleName+'" is not defined!' ); 181 | } 182 | if ( typeof sInstanceName === 'undefined' ) 183 | { 184 | // Return the default module instance. 185 | sInstanceName = sModuleName; 186 | } 187 | return oModuleData.oInstances[sInstanceName]; 188 | } 189 | }; 190 | -------------------------------------------------------------------------------- /src/TinyCore.Toolbox.js: -------------------------------------------------------------------------------- 1 | /* --------- Tools factory --------- */ 2 | 3 | /** 4 | * The available tools, each property holds a factory function that can create the tool requested. 5 | * @type {Object} 6 | */ 7 | var _oTools = {}, 8 | /** 9 | * The current tool ID. 10 | * @type {Number} 11 | */ 12 | _nToolID = -1; 13 | 14 | /** 15 | * The tools factory. 16 | * @type {Object} 17 | */ 18 | TinyCore.Toolbox = { 19 | /** 20 | * Returns the tool requested. 21 | * @param {String} sToolName 22 | * @return {Mixed} The tool requested or null 23 | */ 24 | request : function ( sToolName ) 25 | { 26 | var oToolData = _oTools[sToolName]; 27 | return oToolData && oToolData.fpFactory && oToolData.fpFactory( ++_nToolID ) || _null_; 28 | }, 29 | /** 30 | * Register a new tool's factory function. 31 | * @param {String} sToolName 32 | * @param {Function} fpFactory 33 | * @return {Boolean} Whether the registration was successful or not 34 | */ 35 | register : function ( sToolName, fpFactory ) 36 | { 37 | if ( _oTools[sToolName] || !_oUtils.isFunction( fpFactory ) ) 38 | { 39 | return _false_; 40 | } 41 | 42 | _oTools[sToolName] = { 43 | fpFactory : fpFactory 44 | }; 45 | 46 | return _true_; 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /src/TinyCore.Utils.js: -------------------------------------------------------------------------------- 1 | /* --------- Utilities --------- */ 2 | 3 | /* ES5 shims, from MDN. */ 4 | 5 | if ( !Array.prototype.forEach ) 6 | { 7 | Array.prototype.forEach = function( fn, scope ) 8 | { 9 | var i, len; 10 | for ( i = 0, len = this.length; i < len; ++i ) 11 | { 12 | if ( i in this ) 13 | { 14 | fn.call( scope, this[ i ], i, this ); 15 | } 16 | } 17 | }; 18 | } 19 | 20 | if ( !Function.prototype.bind ) 21 | { 22 | Function.prototype.bind = function( oThis ) 23 | { 24 | if ( typeof this !== "function" ) 25 | { 26 | // closest thing possible to the ECMAScript 5 internal IsCallable function 27 | throw new TypeError( "Function.prototype.bind - what is trying to be bound is not callable" ); 28 | } 29 | 30 | var aArgs = Array.prototype.slice.call( arguments, 1 ), 31 | fToBind = this, 32 | fNOP = function( ) {}, 33 | fBound = function( ) 34 | { 35 | return fToBind.apply( this instanceof fNOP && oThis ? this : oThis, 36 | aArgs.concat( Array.prototype.slice.call( arguments ) ) ); 37 | }; 38 | 39 | fNOP.prototype = this.prototype; 40 | fBound.prototype = new fNOP(); 41 | 42 | return fBound; 43 | }; 44 | } 45 | 46 | if ( !String.prototype.trim ) 47 | { 48 | String.prototype.trim = function( ) 49 | { 50 | return this.replace( /^\s+|\s+$/g, '' ); 51 | }; 52 | } 53 | 54 | /** 55 | * Utilities functions. 56 | * @type {Object} 57 | */ 58 | var _oUtils = { 59 | /** 60 | * Determines the class of an object. 61 | * @type {Function} 62 | * @param {Mixed} mixed 63 | * @return {Boolean} 64 | */ 65 | isClass : function ( mixed, sClassName ) 66 | { 67 | return _toString.call( mixed ) === '[object '+sClassName+']'; 68 | }, 69 | /** 70 | * Determine if the parameter is a function. 71 | * @type {Function} 72 | * @param {Mixed} mixed 73 | * @return {Boolean} 74 | */ 75 | isFunction : function ( mixed ) 76 | { 77 | return _oUtils.isClass( mixed, 'Function' ); 78 | }, 79 | /** 80 | * Determine if the parameter is an object. 81 | * @type {Function} 82 | * @param {Mixed} mixed 83 | * @return {Boolean} 84 | */ 85 | isObject : function ( mixed ) 86 | { 87 | return _oUtils.isClass( mixed, 'Object' ); 88 | }, 89 | /** 90 | * Determine if the parameter is an array. 91 | * @type {Function} 92 | * @param {Mixed} mixed 93 | * @return {Boolean} 94 | */ 95 | isArray : function ( mixed ) 96 | { 97 | return _oUtils.isClass( mixed, 'Array' ); 98 | }, 99 | /** 100 | * Runs through all the properties of an object, applying a callback function on each of them. 101 | * @type {Function} 102 | * @param {Object} oObject 103 | * @param {Function} fpCallback 104 | */ 105 | forIn : function ( oObject, fpCallback ) 106 | { 107 | if ( !oObject || !_oUtils.isObject( oObject ) ) 108 | { 109 | return; 110 | } 111 | 112 | for ( var sProperty in oObject ) 113 | { 114 | if ( _hasOwnProp.call( oObject, sProperty ) ) 115 | { 116 | fpCallback( oObject[sProperty], sProperty ); 117 | } 118 | } 119 | }, 120 | /** 121 | * Merges deeply several objects. 122 | * @type {Function} 123 | * @param {Object} oDest The destination object 124 | * @param {Object} oObj1 The 1st object to merge 125 | * @param {Object} oObj2 The 2nd object to merge 126 | * ... 127 | * @return {Object} 128 | */ 129 | extend : function () 130 | { 131 | var args = arguments, 132 | nArgsCount = args.length, 133 | nIndex = 1, 134 | oDest = args[0] || {}, 135 | fpCopy = function ( val, key ) 136 | { 137 | oDest[key] = _oUtils.isObject( val ) ? _oUtils.extend( oDest[key], val ) : val; 138 | }; 139 | 140 | for ( ; nIndex 2 | 3 | 4 | 5 | TinyCore /// chat 6 | 7 | 8 | 9 | 10 |
11 |

A chat (multiple module instances)

12 |
13 | 14 | 15 | 16 |
17 |
18 |

19 | /// exit 20 |

21 |
22 |
23 |

Conversation started on the at

24 |
25 | 26 |
27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/demos/chat/modules/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The app module, responsible for creating and destroying chat clients. 3 | * Controls the hub lifecycle and communicates with it to check if user access is permitted. 4 | * 5 | * Topics subscribed : "hub:access:granted", "hub:access:refused", "user:exit" 6 | * Topics published : "hub:access:request" 7 | */ 8 | TinyCore.Module.define( 'app', ['mediator', 'events'], function ( _mediator, _events ) 9 | { 10 | 'use strict'; 11 | 12 | var _oNewUserForm; 13 | 14 | // The module. 15 | return { 16 | /** 17 | * This method is called when the module is started. 18 | * @param {Object} oStartData The start data. 19 | */ 20 | onStart : function ( oStartData ) 21 | { 22 | _oNewUserForm = document.getElementById( 'new-user-form' ); 23 | 24 | _mediator.subscribe( 'hub:access:granted', function ( oTopic ) 25 | { 26 | var sUsername = oTopic.data.username; 27 | TinyCore.Module.startInstance( 'chat_client', sUsername, { username : sUsername, containerID : 'chat-windows' } ); 28 | }, this ); 29 | 30 | _mediator.subscribe( 'hub:access:refused', function ( oTopic ) 31 | { 32 | var sErrMsg = oTopic.data.message; 33 | alert( sErrMsg ); 34 | }, this ); 35 | 36 | _mediator.subscribe( 'user:exit', function ( oTopic ) 37 | { 38 | var sUsername = oTopic.data.username; 39 | TinyCore.Module.stopInstance( 'chat_client', sUsername, true ); 40 | }, this ); 41 | 42 | TinyCore.Module.start( 'hub' ); 43 | 44 | _events.on( _oNewUserForm, 'submit', this.requestAccess ); 45 | }, 46 | /** 47 | * This method is called when the module is stopped. 48 | */ 49 | onStop : function () 50 | { 51 | _events.off( _oNewUserForm, 'submit', this.requestAccess ); 52 | 53 | TinyCore.Module.stop( 'hub' ); 54 | 55 | _mediator.unsubscribeAll(); 56 | }, 57 | /** 58 | * Requests access to the chat. 59 | */ 60 | requestAccess : function ( eEvent ) 61 | { 62 | var oUsernameInput = document.getElementById( 'username' ), 63 | sUsername = oUsernameInput.value; 64 | 65 | oUsernameInput.value = ''; 66 | 67 | eEvent.preventDefault(); 68 | 69 | _mediator.publish( 'hub:access:request', { username : sUsername } ); 70 | } 71 | }; 72 | } ); 73 | -------------------------------------------------------------------------------- /src/demos/chat/modules/chat_client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The chat client module, allowing the user to type messages and be notified of the other useres activity. 3 | * 4 | * Topics subscribed : "hub:user:activity", "hub:user:message" 5 | * Topics published : "user:enter", "user:writing", "user:message", "user:exit" 6 | */ 7 | TinyCore.Module.define( 'chat_client', ['mediator', 'dom', 'events'], function ( _mediator, _dom, _events ) 8 | { 9 | 'use strict'; 10 | 11 | // Private variables and helpers. 12 | var _fpFormat2Digits = function ( aParts, sSeparator) 13 | { 14 | var nIndex = aParts.length; 15 | 16 | while ( nIndex-- ) 17 | { 18 | if ( aParts[nIndex] < 10 ) aParts[nIndex] = '0' + aParts[nIndex]; 19 | } 20 | 21 | return aParts.join( sSeparator ); 22 | }, 23 | _fpFormatTime = function ( nTimestamp ) 24 | { 25 | var oDate = new Date( nTimestamp ); 26 | return _fpFormat2Digits( [ oDate.getHours(), oDate.getMinutes(), oDate.getSeconds() ], ':' ); 27 | }, 28 | _fpFormatDate = function ( nTimestamp ) 29 | { 30 | var oDate = new Date( nTimestamp ); 31 | return _fpFormat2Digits( [ oDate.getDate(), oDate.getMonth()+1, oDate.getFullYear() ], '/' ); 32 | }, 33 | _nTimeoutID = -1; 34 | 35 | // Constants. 36 | var KEYCODE_ENTER = 13, 37 | TIMEOUT_NOTIFICATION = 1500; 38 | 39 | // The module. 40 | var _oModule = { 41 | /** 42 | * User name. 43 | * @type {String} 44 | */ 45 | username : '', 46 | /** 47 | * Chat form. 48 | * @type {DOM Element} 49 | */ 50 | chatForm : null, 51 | /** 52 | * Text input. 53 | * @type {DOM Element} 54 | */ 55 | textInput : null, 56 | /** 57 | * Messages view. 58 | * @type {DOM Element} 59 | */ 60 | msgsView : null, 61 | /** 62 | * Activity view. 63 | * @type {DOM Element} 64 | */ 65 | activityView : null, 66 | /** 67 | * Exit button. 68 | * @type {DOM Element} 69 | */ 70 | exitButton : null, 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 | var oContainer; 78 | 79 | _oModule.username = _oModule.username || oStartData.username; 80 | 81 | if ( !_oModule.chatForm ) 82 | { 83 | oContainer = _dom.getById( oStartData.containerID ); 84 | _oModule.createChatWindow( oContainer ); 85 | } 86 | 87 | _mediator.publish( 'user:enter', { username : _oModule.username } ); 88 | 89 | _mediator.subscribe( 'hub:user:activity', function ( oTopic ) 90 | { 91 | var oUserData = oTopic.data; 92 | if ( oUserData.username !== _oModule.username ) 93 | { 94 | _oModule.displayUsersActivity( oUserData ); 95 | } 96 | }, _oModule ); 97 | 98 | _mediator.subscribe( 'hub:user:message', function ( oTopic ) 99 | { 100 | var oUserData = oTopic.data; 101 | if ( oUserData.username !== _oModule.username ) 102 | { 103 | _oModule.displayUserMessage( oUserData ); 104 | } 105 | }, _oModule ); 106 | 107 | _events.on( _oModule.textInput, 'keyup', _oModule.onKeyUp ); 108 | _events.on( _oModule.chatForm, 'submit', _oModule.onSubmitMsg ); 109 | _events.on( _oModule.exitButton, 'click', _oModule.onExitClicked ); 110 | }, 111 | /** 112 | * This method is called when the module is stopped. 113 | */ 114 | onStop : function () 115 | { 116 | if ( _nTimeoutID > -1 ) 117 | { 118 | clearTimeout( _nTimeoutID ); 119 | } 120 | _mediator.unsubscribeAll(); 121 | _events.off( _oModule.exitButton, 'click', _oModule.onExitClicked ); 122 | _events.off( _oModule.chatForm, 'submit', _oModule.onSubmitMsg ); 123 | _events.off( _oModule.textInput, 'keyup', _oModule.onKeyUp ); 124 | }, 125 | /** 126 | * This method is called when the module is destroyed. 127 | */ 128 | onDestroy : function () 129 | { 130 | _oModule.destroyChatWindow(); 131 | }, 132 | /** 133 | * Creates and adds a new chat window to the DOM. 134 | * Initializes a few module properties related to DOM. 135 | * @param {DOM Element} oContainer 136 | */ 137 | createChatWindow : function ( oContainer ) 138 | { 139 | var oChatWindow = _dom.getById( 'chat-window-tpl' ).cloneNode( true ); 140 | 141 | oChatWindow.id = _oModule.username+'-chat'; 142 | _dom.removeClass( oChatWindow, 'tpl' ); 143 | _dom.append( oContainer, oChatWindow ); 144 | _dom.getByClass( 'username', oChatWindow )[0].innerHTML = _oModule.username; 145 | _dom.getByClass( 'start-date', oChatWindow )[0].innerHTML = _fpFormatDate( +new Date() ); 146 | _dom.getByClass( 'start-time', oChatWindow )[0].innerHTML = _fpFormatTime( +new Date() ); 147 | 148 | _oModule.textInput = _dom.getByClass( 'user-text', oChatWindow )[0]; 149 | _oModule.activityView = _dom.getByClass( 'activity', oChatWindow )[0]; 150 | _oModule.msgsView = _dom.getByClass( 'messages', oChatWindow )[0]; 151 | _oModule.exitButton = _dom.getByClass( 'exit', oChatWindow )[0]; 152 | 153 | _oModule.chatForm = oChatWindow; 154 | }, 155 | /** 156 | * Destroys a chat window by removing it from the DOM. 157 | */ 158 | destroyChatWindow : function () 159 | { 160 | _dom.remove( _oModule.chatForm ); 161 | 162 | _oModule.chatForm = null; 163 | _oModule.textInput = null; 164 | _oModule.activityView = null; 165 | _oModule.msgsView = null; 166 | _oModule.exitButton = null; 167 | }, 168 | /** 169 | * Handles the keyup event. 170 | * @param {Event} 171 | */ 172 | onKeyUp : function ( eEvent ) 173 | { 174 | var nKeyCode = eEvent.keyCode || eEvent.which, 175 | oUserData; 176 | 177 | if ( nKeyCode !== KEYCODE_ENTER ) 178 | { 179 | oUserData = { 180 | username : _oModule.username, 181 | timestamp : +new Date() 182 | }; 183 | 184 | _mediator.publish( 'user:writing', oUserData ); 185 | } 186 | }, 187 | /** 188 | * Handles the submit event on the chat form (new message). 189 | * @param {Event} 190 | */ 191 | onSubmitMsg : function ( eEvent ) 192 | { 193 | var oUserData = { 194 | username : _oModule.username, 195 | message : _oModule.textInput.value, 196 | timestamp : +new Date() 197 | }; 198 | 199 | eEvent.preventDefault(); 200 | 201 | _oModule.textInput.value = ''; 202 | _oModule.displayUserMessage( oUserData ); 203 | 204 | _mediator.publish( 'user:message', oUserData ); 205 | }, 206 | /** 207 | * Displays a user message in the messages view. 208 | * @param {Object} oUserData 209 | */ 210 | displayUserMessage : function ( oUserData ) 211 | { 212 | var p = document.createElement( 'p' ); 213 | 214 | p.innerHTML = ''+oUserData.username + ''+_fpFormatTime( oUserData.timestamp )+''; 215 | _oModule.msgsView.appendChild( p ); 216 | 217 | p = document.createElement( 'p' ); 218 | p.innerHTML = oUserData.message; 219 | p.className = 'usermsg'; 220 | _oModule.msgsView.appendChild( p ); 221 | 222 | _oModule.msgsView.scrollTop = _oModule.msgsView.scrollHeight; 223 | }, 224 | /** 225 | * Displays a user activity in the activity view. 226 | * @param {Object} oUserData 227 | */ 228 | displayUsersActivity : function ( oUserData ) 229 | { 230 | _oModule.activityView.innerHTML = oUserData.message; 231 | 232 | if ( _nTimeoutID > -1 ) 233 | { 234 | clearTimeout( _nTimeoutID ); 235 | } 236 | 237 | _dom.addClass( _oModule.activityView, 'active' ); 238 | 239 | _nTimeoutID = setTimeout( function () 240 | { 241 | _oModule.activityView.innerHTML = ''; 242 | _dom.removeClass( _oModule.activityView, 'active' ); 243 | _nTimeoutID = -1; 244 | }, TIMEOUT_NOTIFICATION ); 245 | }, 246 | /** 247 | * Handles the click event on the exit button. 248 | * @param {Event} 249 | */ 250 | onExitClicked : function ( eEvent ) 251 | { 252 | eEvent.preventDefault(); 253 | _mediator.publish( 'user:exit', { username : _oModule.username } ); 254 | } 255 | }; 256 | 257 | return _oModule; 258 | } ); 259 | -------------------------------------------------------------------------------- /src/demos/chat/modules/hub.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The hub module, responsible for broadcasting users activity and messages to all chat clients. 3 | * Also checks for new user access. 4 | * 5 | * Topics subscribed : "hub:access:request", "user:enter", "user:writing", "user:exit", "user:message" 6 | * Topics published : "hub:access:granted", "hub:access:refused", "hub:user:activity", "hub:user:message" 7 | */ 8 | 9 | TinyCore.Module.define( 'hub', ['mediator'], function ( _mediator ) 10 | { 11 | 'use strict'; 12 | 13 | // Private variables and helpers. 14 | var _oUsersData = {}, 15 | _msg = function ( sMsg, sVar ) 16 | { 17 | return sMsg.replace( '%1', sVar ); 18 | }; 19 | 20 | // Constants. 21 | var MSG_USER_ENTER = '%1 has joined the chat!', 22 | MSG_USER_WRITING = '%1 is writing a message...', 23 | MSG_USER_EXIT = '%1 has left the chat.', 24 | MSG_ERR_INVALID_USERNAME = 'The username "%1" is invalid!', 25 | MSG_ERR_USER_ALREADY_ENTERED = 'The username "%1" is already taken!'; 26 | 27 | // The module. 28 | return { 29 | /** 30 | * This method is called when the module is started. 31 | * @param {Object} oStartData The start data. 32 | */ 33 | onStart : function ( oStartData ) 34 | { 35 | _mediator.subscribe( 'hub:access:request', function ( oTopic ) 36 | { 37 | var oUserData = oTopic.data; 38 | 39 | if ( _oUsersData[oUserData.username] ) 40 | { 41 | _mediator.publish( 'hub:access:refused', { username : oUserData.username, message : _msg( MSG_ERR_USER_ALREADY_ENTERED, oUserData.username ) } ); 42 | } 43 | else if ( oUserData.username === '' ) 44 | { 45 | _mediator.publish( 'hub:access:refused', { username : oUserData.username, message : _msg( MSG_ERR_INVALID_USERNAME, oUserData.username ) } ); 46 | } 47 | else 48 | { 49 | _oUsersData[oUserData.username] = { 50 | entry : +new Date() 51 | }; 52 | 53 | _mediator.publish( 'hub:access:granted', { username : oUserData.username } ); 54 | } 55 | } ); 56 | 57 | _mediator.subscribe( 'user:enter', function ( oTopic ) 58 | { 59 | var oUserData = oTopic.data; 60 | this.publishTopic( 'activity', oUserData.username, _msg( MSG_USER_ENTER, oUserData.username ) ); 61 | }, this ); 62 | 63 | _mediator.subscribe( 'user:writing', function ( oTopic ) 64 | { 65 | var oUserData = oTopic.data; 66 | this.publishTopic( 'activity', oUserData.username, _msg( MSG_USER_WRITING, oUserData.username ) ); 67 | }, this ); 68 | 69 | _mediator.subscribe( 'user:exit', function ( oTopic ) 70 | { 71 | var oUserData = oTopic.data; 72 | delete _oUsersData[oUserData.username]; 73 | this.publishTopic( 'activity', oUserData.username, _msg( MSG_USER_EXIT, oUserData.username ) ); 74 | }, this ); 75 | 76 | _mediator.subscribe( 'user:message', function ( oTopic ) 77 | { 78 | var oUserData = oTopic.data; 79 | this.publishTopic( 'message', oUserData.username, oUserData.message ); 80 | }, this ); 81 | }, 82 | /** 83 | * Helper, publishes a timetsamped topic. 84 | * @param {sType} The topic type : "activity" or "message" 85 | * @param {sUsername} 86 | * @param {sMsg} 87 | */ 88 | publishTopic : function ( sType, sUsername, sMsg ) 89 | { 90 | _mediator.publish( 'hub:user:'+sType, { timestamp : +new Date(), username : sUsername, message : sMsg } ); 91 | }, 92 | /** 93 | * This method is called when the module is stopped. 94 | */ 95 | onStop : function () 96 | { 97 | _mediator.unsubscribeAll(); 98 | } 99 | }; 100 | } ); 101 | -------------------------------------------------------------------------------- /src/demos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TinyCore /// demos 6 | 7 | 8 |
9 |

TinyCore.js : demos

10 |
    11 |
  1. 12 | Todo list, local files 13 |
  2. 14 |
  3. 15 | Todo list, AMD 16 |
  4. 17 |
  5. 18 | Todo list, AMD & DOM boot 19 |
  6. 20 |
  7. 21 | Todo list, AMD, DOM boot with deferred loading 22 |
  8. 23 |
  9. 24 | Chat, multiple module instances 25 |
  10. 26 |
  11. 27 | Widgets : inheritance, automatic topics & events subscriptions 28 |
  12. 29 |
30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /src/demos/todolist/_css/todo.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Georgia, serif; 3 | font-size: 13px; 4 | color:#333; 5 | } 6 | 7 | h1 { 8 | margin: 0 0 40px; 9 | } 10 | 11 | #container { 12 | width: 600px; 13 | margin: 10px auto; 14 | position: relative; 15 | padding:20px 40px; 16 | border: 1px solid #777; 17 | -moz-box-shadow: 0 0 1px #999; 18 | -ms-box-shadow: 0 0 1px #999; 19 | -webkit-box-shadow: 0 0 1px #999; 20 | box-shadow: 0 0 1px #999; 21 | position: relative; 22 | } 23 | 24 | /* Form */ 25 | .todo-form { 26 | float: left; 27 | margin-bottom: 15px; 28 | } 29 | .todo-input { 30 | border:1px solid #aaa; 31 | padding:6px 12px; 32 | width:258px; 33 | box-shadow:inset 0 0 1px #999; 34 | } 35 | .todo-input:focus { 36 | border:1px solid #666; 37 | } 38 | #search-form { 39 | float: right; 40 | position: relative; 41 | } 42 | #search-input { 43 | width:131px; 44 | } 45 | #search-reset { 46 | width:131px; 47 | position: absolute; 48 | top:8px; 49 | right:7px; 50 | display: block; 51 | width: 16px; 52 | height: 16px; 53 | background: url(../_img/reset.png) no-repeat center center; 54 | } 55 | #search-reset:hover 56 | { 57 | background-image: url(../_img/reset-red.png); 58 | } 59 | 60 | /* Summary */ 61 | #summary { 62 | float: right; 63 | position: relative; 64 | top: 18px; 65 | right: 6px; 66 | } 67 | #summary .clear { 68 | text-decoration: none; 69 | color:#FF3300; 70 | } 71 | #summary .clear:hover { 72 | text-decoration: underline; 73 | } 74 | 75 | /* List */ 76 | #todo-list { 77 | width: 100%; 78 | margin:0; 79 | padding:0; 80 | clear: both; 81 | } 82 | #todo-list li { 83 | display: block; 84 | padding:4px; 85 | height: 16px; 86 | } 87 | #list-header { 88 | border-bottom:1px solid #333; 89 | margin-bottom:4px; 90 | } 91 | #list-header .label { 92 | font-size: 14px; 93 | font-weight: bold; 94 | } 95 | #todo-list .todo-item:hover { 96 | background-color: #F2F2F2; 97 | } 98 | .todo-check, .todo-check-all { 99 | margin:0 8px 0 2px; 100 | cursor: pointer; 101 | } 102 | .done .todo-name { 103 | text-decoration: line-through; 104 | color:#999; 105 | } 106 | .todo-remove { 107 | width:12px; 108 | height:100%; 109 | background: url(../_img/remove.png) no-repeat center center; 110 | display: none; 111 | float:right; 112 | padding-right: 4px; 113 | } 114 | .todo-remove:hover { 115 | background-image: url(../_img/remove-red.png); 116 | } 117 | #todo-list .todo-item:hover .todo-remove { 118 | display: block; 119 | } 120 | .hidden { 121 | display: none !important; 122 | } 123 | -------------------------------------------------------------------------------- /src/demos/todolist/_img/remove-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mawrkus/tinycore/5f18144d6b47ed0fa883dc49b22bae145d5a89ef/src/demos/todolist/_img/remove-red.png -------------------------------------------------------------------------------- /src/demos/todolist/_img/remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mawrkus/tinycore/5f18144d6b47ed0fa883dc49b22bae145d5a89ef/src/demos/todolist/_img/remove.png -------------------------------------------------------------------------------- /src/demos/todolist/_img/reset-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mawrkus/tinycore/5f18144d6b47ed0fa883dc49b22bae145d5a89ef/src/demos/todolist/_img/reset-red.png -------------------------------------------------------------------------------- /src/demos/todolist/_img/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mawrkus/tinycore/5f18144d6b47ed0fa883dc49b22bae145d5a89ef/src/demos/todolist/_img/reset.png -------------------------------------------------------------------------------- /src/demos/todolist/amd/index-deferred.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TinyCore /// todo list 6 | 7 | 8 | 9 | 10 |
11 |

A todo list (AMD deferred load)

12 |
13 | 14 |
15 |
16 | 17 | 18 |
19 | 20 | / completed - clear completed - clear all 21 | 22 | 28 |
29 | 30 | 31 | 32 | 33 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/demos/todolist/amd/index-domboot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TinyCore /// todo list 6 | 7 | 8 | 9 | 10 |
11 |

A todo list (AMD & DOM boot)

12 |
13 | 14 |
15 |
16 | 17 | 18 |
19 | 20 | / completed - clear completed - clear all 21 | 22 | 28 |
29 | 30 | 31 | 32 | 33 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/demos/todolist/amd/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TinyCore /// todo list 6 | 7 | 8 | 9 | 10 |
11 |

A todo list (AMD)

12 |
13 | 14 |
15 |
16 | 17 | 18 |
19 | 20 | / completed - clear completed - clear all 21 | 22 | 28 |
29 | 30 | 31 | 32 | 33 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/demos/todolist/amd/modules/todo_form_add.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The add module, responsible for publishing a topic with the new todo(s) data, when the user submits the form. 3 | * 4 | * Topics subscribed : "storage:sync" 5 | * Topics published : "todo:add" 6 | */ 7 | TinyCore.AMD.define( 'todo_form_add', ['mediator', 'tools/dom', 'tools/events'], function ( _mediator, _dom, _events ) 8 | { 9 | 'use strict'; 10 | 11 | // Private variables. 12 | var _nCurrentTodoID = 0, 13 | _oForm = null, 14 | _oInput = null; 15 | 16 | // The module. 17 | return { 18 | /** 19 | * This method will be called when the module is started. 20 | * @param {Object} oStartData The start data. 21 | */ 22 | onStart : function ( oStartData ) 23 | { 24 | _mediator.subscribe( 'storage:sync', function ( oTopic ) 25 | { 26 | // Get the last todo ID from the storage module. 27 | _nCurrentTodoID = oTopic.data.lastid + 1; 28 | } ); 29 | 30 | _oForm = _dom.getById( 'todo-form-add' ); 31 | _oInput = _dom.getById( 'add-input' ); 32 | 33 | _oInput.value = ''; 34 | _events.focus( _oInput ); 35 | 36 | _events.on( _oForm, 'submit', this.onFormSubmit ); 37 | }, 38 | 39 | /** 40 | * This method will be called when the module is stopped. 41 | */ 42 | onStop : function () 43 | { 44 | _events.off( _oForm, 'submit', this.onFormSubmit ); 45 | _oInput = _oForm = null; 46 | }, 47 | 48 | /** 49 | * This method will be called when the form is submitted. 50 | * @param {Event} eEvent The submit event object 51 | */ 52 | onFormSubmit : function ( eEvent ) 53 | { 54 | var aNames = _oInput.value.split( ',' ), 55 | nNamesCount = aNames.length, 56 | nIndex = 0, 57 | sCurrentName = ''; 58 | 59 | for ( ; nIndex'+sTodoName+''; 154 | 155 | _dom.html( oLI, sLIHTML ); 156 | _dom.setData( oLI, 'todo-id', sTodoID ); 157 | _dom.addClass( oLI, 'todo-item' + sClass ); 158 | 159 | _dom.append( _oList, oLI ); 160 | }, 161 | 162 | /** 163 | * Toggles a todo (mark it as done or not and vice-versa, yes). 164 | * @param {DOM Element} oListItem 165 | * @param {Boolean} bDone Optional, whether to mark them as done or not 166 | */ 167 | toggleTodo : function ( oListItem, bDone ) 168 | { 169 | var sTodoID = _dom.getData( oListItem, 'todo-id' ), 170 | oTodoName = _dom.getById( 'todo-name-'+sTodoID ), 171 | sTodoName = _dom.html( oTodoName ), 172 | oCheckBox = _dom.getById( 'todo-check-'+sTodoID ); 173 | 174 | bDone = typeof bDone === 'undefined' ? !_dom.hasClass( oListItem, 'done' ) : bDone; 175 | 176 | oCheckBox.checked = bDone; 177 | _dom.toggleClass( oListItem, 'done', bDone ); 178 | 179 | // Let's broadcast the news... 180 | _mediator.publish( 'todo:update', { name : sTodoName, id : sTodoID, done : bDone } ); 181 | }, 182 | 183 | /** 184 | * Removes a todo. 185 | * @param {DOM Element} oListItem 186 | */ 187 | removeTodo : function ( oListItem ) 188 | { 189 | var sTodoID = _dom.getData( oListItem, 'todo-id' ), 190 | sTodoName = _dom.html( _dom.getById( 'todo-name-'+sTodoID ) ); 191 | 192 | _dom.remove( oListItem ); 193 | 194 | // Let's broadcast the news... 195 | _mediator.publish( 'todo:remove', { name : sTodoName, id : sTodoID, done : _dom.hasClass( oListItem, 'done' ) } ); 196 | }, 197 | 198 | /** 199 | * Performs an action on each todo, depending on the result of a filtering function. 200 | * @param {Function} fpAction The function that will performs the action 201 | * @param {Function} fpFilter Optional, a function receiving the current todo list item element and returning a boolean value that indicates if the action should be executed on that todo or not 202 | */ 203 | forEachTodo : function ( fpAction, fpFilter ) 204 | { 205 | var aListItems = _dom.getByClass( 'todo-item' ), 206 | nCount = aListItems.length, 207 | oCurrentListItem = null; 208 | 209 | fpFilter = fpFilter || function ( oCurrentListItem ) { return true; }; 210 | 211 | while ( nCount-- ) 212 | { 213 | oCurrentListItem = aListItems[nCount]; 214 | 215 | if ( fpFilter( oCurrentListItem ) ) 216 | { 217 | fpAction( oCurrentListItem ); 218 | } 219 | } 220 | } 221 | }; 222 | } ); 223 | -------------------------------------------------------------------------------- /src/demos/todolist/amd/modules/todo_storage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The (optional) storage module, responsible for storing/restoring the todos. 3 | * The method chosen is defined by the storage_sandbox. 4 | * Note that since it does not need to manipulate the DOM, the sandbox type is different from the other modules. 5 | * 6 | * Topics subscribed : "todo:add", "todo:update", "todo:remove" 7 | * Topics published : "storage:sync" 8 | */ 9 | TinyCore.AMD.define( 'todo_storage', ['mediator', 'tools/storage'], function ( _mediator, _storage ) 10 | { 11 | 'use strict'; 12 | 13 | // Private variables and constants. 14 | var _oTodos = null; 15 | 16 | // Constants. 17 | var STORAGE_NAME = 'todos'; 18 | 19 | // The module. 20 | return { 21 | /** 22 | * This method will be called when the module is started. 23 | * @param {Object} oStartData The start data. 24 | */ 25 | onStart : function ( oStartData ) 26 | { 27 | _oTodos = _storage.get( STORAGE_NAME ); 28 | 29 | if ( _oTodos ) 30 | { 31 | // This is done to initialize properly the current todo ID when synching the storage (see todo_form.js). 32 | _oTodos.lastid = this.getLastID(); 33 | } 34 | else 35 | { 36 | _oTodos = { 37 | total :0, 38 | done : 0, 39 | list : {}, 40 | lastid : -1 41 | }; 42 | } 43 | 44 | _mediator.publish( 'storage:sync', _oTodos ); // Let's tell the world! 45 | 46 | _mediator.subscribe( 'todo:add', this.addTodo ); 47 | _mediator.subscribe( 'todo:update', this.updateTodo ); 48 | _mediator.subscribe( 'todo:remove', this.removeTodo ); 49 | }, 50 | 51 | getLastID : function () 52 | { 53 | var sTodoID = '', 54 | nResult = -1; 55 | 56 | for ( sTodoID in _oTodos.list ) {} 57 | 58 | if ( sTodoID ) 59 | { 60 | nResult = parseInt( sTodoID, 10 ); 61 | } 62 | 63 | return nResult; 64 | }, 65 | 66 | /** 67 | * Adds a todo. 68 | * @param {Object} oTopic The topic published 69 | */ 70 | addTodo : function ( oTopic ) 71 | { 72 | var oData = oTopic.data; 73 | 74 | if ( _oTodos.list[oData.id] ) 75 | { 76 | TinyCore.Error.report( 'Add error : todo with ID="'+oData.id+'" already exist in storage ("'+oData.name+'")!', oData ); 77 | } 78 | 79 | _oTodos.list[oData.id] = { name : oData.name, done : false }; 80 | _oTodos.total++; 81 | 82 | _storage.set( STORAGE_NAME, _oTodos ); 83 | }, 84 | 85 | /** 86 | * Updates a todo. 87 | * @param {Object} oTopic The topic published 88 | */ 89 | updateTodo : function ( oTopic ) 90 | { 91 | var oData = oTopic.data; 92 | 93 | if ( !_oTodos.list[oData.id] ) 94 | { 95 | TinyCore.Error.report( 'Update error : todo with ID="'+oData.id+'" does not exist in storage ("'+oData.name+'")!', oData ); 96 | } 97 | 98 | _oTodos.list[oData.id] = { name : oData.name, done : oData.done }; 99 | _oTodos.done += oData.done ? +1 : -1; 100 | 101 | _storage.set( STORAGE_NAME, _oTodos ); 102 | }, 103 | 104 | /** 105 | * Removes a todo. 106 | * @param {Object} oTopic The topic published 107 | */ 108 | removeTodo : function ( oTopic ) 109 | { 110 | var oData = oTopic.data; 111 | 112 | if ( !_oTodos.list[oData.id] ) 113 | { 114 | TinyCore.Error.report( 'Remove error : todo with ID="'+oData.id+'" does not exist in storage!', oData ); 115 | } 116 | 117 | delete _oTodos.list[oData.id]; 118 | _oTodos.total--; 119 | 120 | if ( oData.done ) 121 | { 122 | _oTodos.done--; 123 | } 124 | 125 | _storage.set( STORAGE_NAME, _oTodos ); 126 | }, 127 | 128 | /** 129 | * This method will be called when the module is stopped. 130 | */ 131 | onStop : function () 132 | { 133 | _oTodos = null; 134 | } 135 | }; 136 | } ); 137 | -------------------------------------------------------------------------------- /src/demos/todolist/amd/modules/todo_summary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The summary module, responsible for displaying the updated count of completed vs remaining todos. 3 | * Also responsible for providing a way to clear the todo list when clicking on one of the "clear" links. 4 | * 5 | * Topics subscribed : "todo:add", "todo:update", "todo:remove", "storage:sync" 6 | * Topics published : "list:clear", "list:clean" 7 | */ 8 | TinyCore.AMD.define( 'todo_summary', ['mediator', 'tools/events'], function ( _mediator, _events ) 9 | { 10 | 'use strict'; 11 | 12 | // Private variable. 13 | var _oClearAll = null, 14 | _oClearCompleted = null; 15 | 16 | // The module. 17 | return { 18 | /** 19 | * This method will be called when the module is started. 20 | * @param {Object} oStartData The start data. 21 | */ 22 | onStart : function ( oStartData ) 23 | { 24 | var self = this, 25 | oTotal = document.getElementById( 'count-total' ), 26 | oDone = document.getElementById( 'count-done' ); 27 | 28 | this.setCount( oTotal, 0 ); 29 | this.setCount( oDone, 0 ); 30 | 31 | _mediator.subscribe( 'todo:add', function ( oTopic ) 32 | { 33 | console.info(oTopic.name, oTopic.data); 34 | self.incCount( oTotal, +1 ); 35 | } ); 36 | 37 | _mediator.subscribe( 'todo:update', function ( oTopic ) 38 | { 39 | console.info(oTopic.name, oTopic.data); 40 | self.incCount( oDone, oTopic.data.done ? +1 : -1 ); 41 | } ); 42 | 43 | _mediator.subscribe( 'todo:remove', function ( oTopic ) 44 | { 45 | console.info(oTopic.name, oTopic.data); 46 | self.incCount( oTotal, -1 ); 47 | 48 | if ( oTopic.data.done ) 49 | { 50 | self.incCount( oDone, -1 ); 51 | } 52 | } ); 53 | 54 | _mediator.subscribe( 'storage:sync', function ( oTopic ) 55 | { 56 | console.info(oTopic.name, oTopic.data); 57 | var oData = oTopic.data; 58 | self.setCount( oTotal, oData.total ); 59 | self.setCount( oDone, oData.done ); 60 | } ); 61 | 62 | _oClearAll = document.getElementById( 'clear-list' ); 63 | _oClearCompleted = document.getElementById( 'clear-completed' ); 64 | 65 | _events.on( _oClearAll, 'click', this.onClearListClick ); 66 | _events.on( _oClearCompleted, 'click', this.onClearCompletedClick ); 67 | }, 68 | 69 | /** 70 | * This method will be called when the module is stopped. 71 | */ 72 | onStop : function () 73 | { 74 | _events.off( _oClearCompleted, 'click', this.onClearCompletedClick ); 75 | _events.off( _oClearAll, 'click', this.onClearListClick ); 76 | 77 | _oClearCompleted = _oClearAll = null; 78 | }, 79 | 80 | /** 81 | * Sets a given count 82 | * @param {DOM Element} oElement The DOM element that holds the count to set 83 | * @param {Number} nNewCount The new count 84 | */ 85 | setCount : function ( oElement, nNewCount ) 86 | { 87 | oElement.innerHTML = nNewCount; 88 | }, 89 | 90 | /** 91 | * Increment a given count 92 | * @param {DOM Element} oElement The DOM element that holds the count to increment 93 | * @param {Number} nInc The count increment 94 | */ 95 | incCount : function ( oElement, nInc ) 96 | { 97 | var nCurrentCount = parseInt( oElement.innerHTML, 10 ); 98 | this.setCount( oElement, nCurrentCount + nInc ); 99 | }, 100 | 101 | /** 102 | * This method will be called when the "clear list" link is clicked. 103 | * @param {Event} eEvent The click event object 104 | */ 105 | onClearListClick : function ( eEvent ) 106 | { 107 | eEvent.preventDefault(); 108 | _mediator.publish( 'list:clear' ); // Let's request some cleanup... 109 | }, 110 | 111 | /** 112 | * This method will be called when the "clear completed" link is clicked. 113 | * @param {Event} eEvent The click event object 114 | */ 115 | onClearCompletedClick : function ( eEvent ) 116 | { 117 | eEvent.preventDefault(); 118 | _mediator.publish( 'list:clean' ); // Let's request some cleanup... 119 | } 120 | }; 121 | } ); 122 | -------------------------------------------------------------------------------- /src/demos/todolist/localfiles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TinyCore /// todo list 6 | 7 | 8 | 9 | 10 |
11 |

A todo list (local files)

12 |
13 | 14 |
15 |
16 | 17 | 18 |
19 | 20 | / completed - clear completed - clear all 21 | 22 |
    23 |
  • 24 | 25 | Name 26 |
  • 27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/demos/todolist/localfiles/modules/todo_form_add.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The add module, responsible for publishing a topic with the new todo(s) data, when the user submits the form. 3 | * 4 | * Topics subscribed : "storage:sync" 5 | * Topics published : "todo:add" 6 | */ 7 | TinyCore.Module.define( 'todo_form_add', ['mediator', 'dom', 'events'], function ( _mediator, _dom, _events ) 8 | { 9 | 'use strict'; 10 | 11 | // Private variables. 12 | var _nCurrentTodoID = 0, 13 | _oForm = null, 14 | _oInput = null; 15 | 16 | // The module. 17 | return { 18 | /** 19 | * This method will be called when the module is started. 20 | * @param {Object} oStartData The start data. 21 | */ 22 | onStart : function ( oStartData ) 23 | { 24 | _mediator.subscribe( 'storage:sync', function ( oTopic ) 25 | { 26 | // Get the last todo ID from the storage module. 27 | _nCurrentTodoID = oTopic.data.lastid + 1; 28 | } ); 29 | 30 | _oForm = _dom.getById( 'todo-form-add' ); 31 | _oInput = _dom.getById( 'add-input' ); 32 | 33 | _oInput.value = ''; 34 | _events.focus( _oInput ); 35 | 36 | _events.on( _oForm, 'submit', this.onFormSubmit ); 37 | }, 38 | 39 | /** 40 | * This method will be called when the module is stopped. 41 | */ 42 | onStop : function () 43 | { 44 | _events.off( _oForm, 'submit', this.onFormSubmit ); 45 | _oInput = _oForm = null; 46 | }, 47 | 48 | /** 49 | * This method will be called when the form is submitted. 50 | * @param {Event} eEvent The submit event object 51 | */ 52 | onFormSubmit : function ( eEvent ) 53 | { 54 | var aNames = _oInput.value.split( ',' ), 55 | nNamesCount = aNames.length, 56 | nIndex = 0, 57 | sCurrentName = ''; 58 | 59 | for ( ; nIndex'+sTodoName+''; 154 | 155 | _dom.html( oLI, sLIHTML ); 156 | _dom.setData( oLI, 'todo-id', sTodoID ); 157 | _dom.addClass( oLI, 'todo-item' + sClass ); 158 | 159 | _dom.append( _oList, oLI ); 160 | }, 161 | 162 | /** 163 | * Toggles a todo (mark it as done or not and vice-versa, yes). 164 | * @param {DOM Element} oListItem 165 | * @param {Boolean} bDone Optional, whether to mark them as done or not 166 | */ 167 | toggleTodo : function ( oListItem, bDone ) 168 | { 169 | var sTodoID = _dom.getData( oListItem, 'todo-id' ), 170 | oTodoName = _dom.getById( 'todo-name-'+sTodoID ), 171 | sTodoName = _dom.html( oTodoName ), 172 | oCheckBox = _dom.getById( 'todo-check-'+sTodoID ); 173 | 174 | bDone = typeof bDone === 'undefined' ? !_dom.hasClass( oListItem, 'done' ) : bDone; 175 | 176 | oCheckBox.checked = bDone; 177 | _dom.toggleClass( oListItem, 'done', bDone ); 178 | 179 | // Let's broadcast the news... 180 | _mediator.publish( 'todo:update', { name : sTodoName, id : sTodoID, done : bDone } ); 181 | }, 182 | 183 | /** 184 | * Removes a todo. 185 | * @param {DOM Element} oListItem 186 | */ 187 | removeTodo : function ( oListItem ) 188 | { 189 | var sTodoID = _dom.getData( oListItem, 'todo-id' ), 190 | sTodoName = _dom.html( _dom.getById( 'todo-name-'+sTodoID ) ); 191 | 192 | _dom.remove( oListItem ); 193 | 194 | // Let's broadcast the news... 195 | _mediator.publish( 'todo:remove', { name : sTodoName, id : sTodoID, done : _dom.hasClass( oListItem, 'done' ) } ); 196 | }, 197 | 198 | /** 199 | * Performs an action on each todo, depending on the result of a filtering function. 200 | * @param {Function} fpAction The function that will performs the action 201 | * @param {Function} fpFilter Optional, a function receiving the current todo list item element and returning a boolean value that indicates if the action should be executed on that todo or not 202 | */ 203 | forEachTodo : function ( fpAction, fpFilter ) 204 | { 205 | var aListItems = _dom.getByClass( 'todo-item' ), 206 | nCount = aListItems.length, 207 | oCurrentListItem = null; 208 | 209 | fpFilter = fpFilter || function ( oCurrentListItem ) { return true; }; 210 | 211 | while ( nCount-- ) 212 | { 213 | oCurrentListItem = aListItems[nCount]; 214 | 215 | if ( fpFilter( oCurrentListItem ) ) 216 | { 217 | fpAction( oCurrentListItem ); 218 | } 219 | } 220 | } 221 | }; 222 | } ); 223 | -------------------------------------------------------------------------------- /src/demos/todolist/localfiles/modules/todo_storage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The (optional) storage module, responsible for storing/restoring the todos. 3 | * The method chosen is defined by the storage_sandbox. 4 | * Note that since it does not need to manipulate the DOM, the sandbox type is different from the other modules. 5 | * 6 | * Topics subscribed : "todo:add", "todo:update", "todo:remove" 7 | * Topics published : "storage:sync" 8 | */ 9 | TinyCore.Module.define( 'todo_storage', ['mediator', 'storage'], function ( _mediator, _storage ) 10 | { 11 | 'use strict'; 12 | 13 | // Private variables and constants. 14 | var _oTodos = null; 15 | 16 | // Constants. 17 | var STORAGE_NAME = 'todos'; 18 | 19 | // The module. 20 | return { 21 | /** 22 | * This method will be called when the module is started. 23 | * @param {Object} oStartData The start data. 24 | */ 25 | onStart : function ( oStartData ) 26 | { 27 | _oTodos = _storage.get( STORAGE_NAME ); 28 | 29 | if ( _oTodos ) 30 | { 31 | // This is done to initialize properly the current todo ID when synching the storage (see todo_form.js). 32 | _oTodos.lastid = this.getLastID(); 33 | } 34 | else 35 | { 36 | _oTodos = { 37 | total :0, 38 | done : 0, 39 | list : {}, 40 | lastid : -1 41 | }; 42 | } 43 | 44 | _mediator.publish( 'storage:sync', _oTodos ); // Let's tell the world! 45 | 46 | _mediator.subscribe( 'todo:add', this.addTodo ); 47 | _mediator.subscribe( 'todo:update', this.updateTodo ); 48 | _mediator.subscribe( 'todo:remove', this.removeTodo ); 49 | }, 50 | 51 | getLastID : function () 52 | { 53 | var sTodoID = '', 54 | nResult = -1; 55 | 56 | for ( sTodoID in _oTodos.list ) {} 57 | 58 | if ( sTodoID ) 59 | { 60 | nResult = parseInt( sTodoID, 10 ); 61 | } 62 | 63 | return nResult; 64 | }, 65 | 66 | /** 67 | * Adds a todo. 68 | * @param {Object} oTopic The topic published 69 | */ 70 | addTodo : function ( oTopic ) 71 | { 72 | var oData = oTopic.data; 73 | 74 | if ( _oTodos.list[oData.id] ) 75 | { 76 | TinyCore.Error.report( 'Add error : todo with ID="'+oData.id+'" already exist in storage ("'+oData.name+'")!', oData ); 77 | } 78 | 79 | _oTodos.list[oData.id] = { name : oData.name, done : false }; 80 | _oTodos.total++; 81 | 82 | _storage.set( STORAGE_NAME, _oTodos ); 83 | }, 84 | 85 | /** 86 | * Updates a todo. 87 | * @param {Object} oTopic The topic published 88 | */ 89 | updateTodo : function ( oTopic ) 90 | { 91 | var oData = oTopic.data; 92 | 93 | if ( !_oTodos.list[oData.id] ) 94 | { 95 | TinyCore.Error.report( 'Update error : todo with ID="'+oData.id+'" does not exist in storage ("'+oData.name+'")!', oData ); 96 | } 97 | 98 | _oTodos.list[oData.id] = { name : oData.name, done : oData.done }; 99 | _oTodos.done += oData.done ? +1 : -1; 100 | 101 | _storage.set( STORAGE_NAME, _oTodos ); 102 | }, 103 | 104 | /** 105 | * Removes a todo. 106 | * @param {Object} oTopic The topic published 107 | */ 108 | removeTodo : function ( oTopic ) 109 | { 110 | var oData = oTopic.data; 111 | 112 | if ( !_oTodos.list[oData.id] ) 113 | { 114 | TinyCore.Error.report( 'Remove error : todo with ID="'+oData.id+'" does not exist in storage!', oData ); 115 | } 116 | 117 | delete _oTodos.list[oData.id]; 118 | _oTodos.total--; 119 | 120 | if ( oData.done ) 121 | { 122 | _oTodos.done--; 123 | } 124 | 125 | _storage.set( STORAGE_NAME, _oTodos ); 126 | }, 127 | 128 | /** 129 | * This method will be called when the module is stopped. 130 | */ 131 | onStop : function () 132 | { 133 | _oTodos = null; 134 | } 135 | }; 136 | } ); 137 | -------------------------------------------------------------------------------- /src/demos/todolist/localfiles/modules/todo_summary.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The summary module, responsible for displaying the updated count of completed vs remaining todos. 3 | * Also responsible for providing a way to clear the todo list when clicking on one of the "clear" links. 4 | * 5 | * Topics subscribed : "todo:add", "todo:update", "todo:remove", "storage:sync" 6 | * Topics published : "list:clear", "list:clean" 7 | */ 8 | TinyCore.Module.define( 'todo_summary', ['mediator', 'events'], function ( _mediator, _events ) 9 | { 10 | 'use strict'; 11 | 12 | // Private variable. 13 | var _oClearAll = null, 14 | _oClearCompleted = null; 15 | 16 | // The module. 17 | return { 18 | /** 19 | * This method will be called when the module is started. 20 | * @param {Object} oStartData The start data. 21 | */ 22 | onStart : function ( oStartData ) 23 | { 24 | var self = this, 25 | oTotal = document.getElementById( 'count-total' ), 26 | oDone = document.getElementById( 'count-done' ); 27 | 28 | this.setCount( oTotal, 0 ); 29 | this.setCount( oDone, 0 ); 30 | 31 | _mediator.subscribe( 'todo:add', function ( oTopic ) 32 | { 33 | console.info(oTopic.name, oTopic.data); 34 | self.incCount( oTotal, +1 ); 35 | } ); 36 | 37 | _mediator.subscribe( 'todo:update', function ( oTopic ) 38 | { 39 | console.info(oTopic.name, oTopic.data); 40 | self.incCount( oDone, oTopic.data.done ? +1 : -1 ); 41 | } ); 42 | 43 | _mediator.subscribe( 'todo:remove', function ( oTopic ) 44 | { 45 | console.info(oTopic.name, oTopic.data); 46 | self.incCount( oTotal, -1 ); 47 | 48 | if ( oTopic.data.done ) 49 | { 50 | self.incCount( oDone, -1 ); 51 | } 52 | } ); 53 | 54 | _mediator.subscribe( 'storage:sync', function ( oTopic ) 55 | { 56 | console.info(oTopic.name, oTopic.data); 57 | var oData = oTopic.data; 58 | self.setCount( oTotal, oData.total ); 59 | self.setCount( oDone, oData.done ); 60 | } ); 61 | 62 | _oClearAll = document.getElementById( 'clear-list' ); 63 | _oClearCompleted = document.getElementById( 'clear-completed' ); 64 | 65 | _events.on( _oClearAll, 'click', this.onClearListClick ); 66 | _events.on( _oClearCompleted, 'click', this.onClearCompletedClick ); 67 | }, 68 | 69 | /** 70 | * This method will be called when the module is stopped. 71 | */ 72 | onStop : function () 73 | { 74 | _events.off( _oClearCompleted, 'click', this.onClearCompletedClick ); 75 | _events.off( _oClearAll, 'click', this.onClearListClick ); 76 | 77 | _oClearCompleted = _oClearAll = null; 78 | }, 79 | 80 | /** 81 | * Sets a given count 82 | * @param {DOM Element} oElement The DOM element that holds the count to set 83 | * @param {Number} nNewCount The new count 84 | */ 85 | setCount : function ( oElement, nNewCount ) 86 | { 87 | oElement.innerHTML = nNewCount; 88 | }, 89 | 90 | /** 91 | * Increment a given count 92 | * @param {DOM Element} oElement The DOM element that holds the count to increment 93 | * @param {Number} nInc The count increment 94 | */ 95 | incCount : function ( oElement, nInc ) 96 | { 97 | var nCurrentCount = parseInt( oElement.innerHTML, 10 ); 98 | this.setCount( oElement, nCurrentCount + nInc ); 99 | }, 100 | 101 | /** 102 | * This method will be called when the "clear list" link is clicked. 103 | * @param {Event} eEvent The click event object 104 | */ 105 | onClearListClick : function ( eEvent ) 106 | { 107 | eEvent.preventDefault(); 108 | _mediator.publish( 'list:clear' ); // Let's request some cleanup... 109 | }, 110 | 111 | /** 112 | * This method will be called when the "clear completed" link is clicked. 113 | * @param {Event} eEvent The click event object 114 | */ 115 | onClearCompletedClick : function ( eEvent ) 116 | { 117 | eEvent.preventDefault(); 118 | _mediator.publish( 'list:clean' ); // Let's request some cleanup... 119 | } 120 | }; 121 | } ); 122 | -------------------------------------------------------------------------------- /src/demos/widgets/css/widgets.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Georgia, serif; 3 | font-size: 13px; 4 | color:#333; 5 | } 6 | 7 | h1 { 8 | margin: 0 0 20px; 9 | } 10 | h2 { 11 | margin: 0 0 4px; 12 | } 13 | 14 | #container { 15 | width: 800px; 16 | margin: 10px auto; 17 | padding:20px 40px; 18 | border: 1px solid #777; 19 | -moz-box-shadow: 0 0 1px #999; 20 | -ms-box-shadow: 0 0 1px #999; 21 | -webkit-box-shadow: 0 0 1px #999; 22 | box-shadow: 0 0 1px #999; 23 | } 24 | 25 | .form-input { 26 | border:1px solid #AAA; 27 | padding:6px 12px; 28 | box-shadow:inset 0 0 1px #999; 29 | } 30 | .form-input:focus { 31 | border:1px solid #0B5FA2; 32 | } 33 | .form-select { 34 | padding:4px 2px; 35 | } 36 | .button { 37 | background-color: #0B70BE; 38 | color: #FFF; 39 | border: 0; 40 | padding:6px 12px; 41 | } 42 | .button:disabled { 43 | background-color: #CCC; 44 | color: #6D6D6D; 45 | } 46 | 47 | a { 48 | text-decoration: none; 49 | color: #0B70BE; 50 | } 51 | a:hover { 52 | text-decoration: underline; 53 | } 54 | .disabled { 55 | color: #555; 56 | } 57 | .disabled:hover { 58 | text-decoration: none; 59 | } 60 | 61 | .widget { 62 | margin: 10px 0 30px; 63 | float: left; 64 | width: 50%; 65 | } 66 | 67 | .activity-container { 68 | clear: both; 69 | } 70 | #activity-view { 71 | height:300px; 72 | overflow: auto; 73 | border-top: 1px solid #777; 74 | border-bottom: 1px solid #777; 75 | padding: 4px 4px 4px 8px; 76 | color: #444; 77 | font-size: 12px; 78 | } 79 | .activity { 80 | margin: 2px 0 0; 81 | } 82 | -------------------------------------------------------------------------------- /src/demos/widgets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TinyCore /// widgets 6 | 7 | 8 | 9 | 10 |
11 |

Inheritance, automatic topics & events subscriptions

12 | 13 |
14 |

15 | [Start] | [Stop] 16 |

17 |

18 | 19 | 20 |

21 |
22 | 23 |
24 |

25 | [Start] | [Stop] 26 |

27 | 33 |
34 | 35 |
36 |

Widgets activity

37 |
38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/demos/widgets/modules/activity-log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The "activity-log" module, responsible for logging the widgets activity. 3 | * 4 | * Topics subscribed : "activity:log" 5 | */ 6 | TinyCore.Module.define( 'activity-log', ['mediator'], function ( _mediator ) 7 | { 8 | 'use strict'; 9 | 10 | // Private variables and helpers. 11 | var _fpFormatTime = function ( nTimestamp ) 12 | { 13 | var oDate = new Date( nTimestamp ), 14 | aParts = [ oDate.getHours(), oDate.getMinutes(), oDate.getSeconds() ], 15 | nIndex = aParts.length; 16 | 17 | while ( nIndex-- ) 18 | { 19 | if ( aParts[nIndex] < 10 ) aParts[nIndex] = '0' + aParts[nIndex]; 20 | } 21 | 22 | return aParts.join( ':' ); 23 | }; 24 | 25 | // The module. 26 | return { 27 | /** 28 | * The logs container 29 | * @type {DOM Element} 30 | */ 31 | element : null, 32 | /** 33 | * This method is called when the module is started. 34 | * @param {Object} oStartData The start data. 35 | */ 36 | onStart : function ( oStartData ) 37 | { 38 | this.element = oStartData.element; 39 | 40 | _mediator.subscribe( 'log:add', function ( oTopic ) 41 | { 42 | this.log( oTopic.data ); 43 | }, this ); 44 | }, 45 | /** 46 | * This method is called when the module is stopped. 47 | */ 48 | onStop : function () 49 | { 50 | }, 51 | /** 52 | * Logs messages. 53 | * @param {Object} oLogData 54 | * @param {Number} oLogData.timestamp 55 | * @param {String} oLogData.src 56 | * @param {String} oLogData.msg 57 | */ 58 | log : function ( oLogData ) 59 | { 60 | var aParts = [], 61 | sMsg; 62 | 63 | aParts.push( '['+_fpFormatTime( oLogData.timestamp )+'] ' ); 64 | aParts.push( ''+oLogData.src+'> ' ); 65 | aParts.push( oLogData.msg ); 66 | 67 | sMsg = aParts.join( '' ); 68 | 69 | this.element.innerHTML += '

'+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 |
119 |

A todo list (AMD deferred load)

120 |
121 | 122 |
123 |
124 | 125 | 126 |
127 | 128 | / completed - clear completed - clear all 129 | 130 |
    131 |
  • 132 | 133 | Name 134 |
  • 135 |
136 |
137 | 138 | (...) 139 | ``` 140 | 141 | When calling `TinyCore.AMD.domBoot`, the DOM is scanned for finding modules declarations. The following modules are found and are started as follows : 142 | 143 | - "todo_storage", loaded after 5s 144 | - "todo_form_add", loaded when the user will click on the "#add-input" element 145 | - "todo_form_search", loaded when the mouse pointer is closer than 50px or after 10s 146 | - "todo_summary" and "todo_list", loaded after 1s 147 | 148 | Without any deferred strategies declared, modules are loaded directly. 149 | -------------------------------------------------------------------------------- /src/extensions/AMD/TinyCore.AMD.js: -------------------------------------------------------------------------------- 1 | /** 2 | * AMD extension for TinyCore.js 3 | * Load modules asynchronously using require.js and starts them 4 | * @author mawrkus (web@sparring-partner.be) 5 | * @requires TinyCore.js 6 | * @requires require.js version >= 2.1.4 7 | */ 8 | ( function ( oEnv ) 9 | { 10 | 'use strict'; 11 | 12 | var TinyCore = oEnv.TinyCore, 13 | Utils = TinyCore.Utils, 14 | Module = TinyCore.Module; 15 | 16 | if ( !oEnv.require || !oEnv.define ) 17 | { 18 | throw new Error( 'Cannot add AMD extension to TinyCore: require.js seems to be missing!' ); 19 | } 20 | 21 | /* Private. */ 22 | 23 | /** 24 | * The default configuration for require.js. 25 | * @type {Object} 26 | */ 27 | var DEFAULT_REQUIRE_CONFIG = { 28 | baseUrl : 'modules' 29 | }, 30 | /** 31 | * The current configuration. 32 | * @type {Object} 33 | */ 34 | _oConfig = { 35 | require : Utils.extend( {}, DEFAULT_REQUIRE_CONFIG ) 36 | }; 37 | 38 | /** 39 | * The AMD extension. 40 | * @type {Object} 41 | */ 42 | var _oAMDExt = { 43 | /** 44 | * Configures the extension, both require.js and the DOM boot loader. 45 | * @param {Object} oSettings Optional, the settings, see http://requirejs.org/docs/api.html#config for require.js 46 | * @return {Object} The current configuration, if no parameter has been provided 47 | */ 48 | config : function ( oSettings ) 49 | { 50 | if ( typeof oSettings === 'undefined' ) 51 | { 52 | return _oConfig; 53 | } 54 | Utils.extend( _oConfig, oSettings ); 55 | oEnv.require.config( _oConfig.require ); 56 | }, 57 | /** 58 | * Sets the global error handler. 59 | * @param {Function} fpErrorHandler 60 | */ 61 | setErrorHandler : function ( fpErrorHandler ) 62 | { 63 | oEnv.require.onError = fpErrorHandler; 64 | }, 65 | /** 66 | * Defines a module. 67 | * @param {String} sModuleName 68 | * @param {Array} aDependencies 69 | * @param {Function} fpCreator 70 | */ 71 | define : function ( sModuleName, aDependencies, fpCreator ) 72 | { 73 | oEnv.define( sModuleName, aDependencies, function () 74 | { 75 | var aDepsBaseNames = [], 76 | nIndex = aDependencies.length; 77 | 78 | while ( nIndex-- ) 79 | { 80 | aDepsBaseNames.unshift( aDependencies[nIndex].split( '/' ).pop() ); 81 | } 82 | 83 | Module.define( sModuleName, aDepsBaseNames, fpCreator ); 84 | } ); 85 | }, 86 | /** 87 | * Loads asynchronously modules/scripts. 88 | * @param {Array} aResourcesNames 89 | * @param {Function} fpCallback The function to call when all modules are loaded, it receives the array of modules names 90 | */ 91 | require : function ( aResourcesNames, fpCallback ) 92 | { 93 | oEnv.require( aResourcesNames, fpCallback ); 94 | }, 95 | /** 96 | * Loads asynchronously modules and starts them. 97 | * @param {Array|String|Object} aModulesData E.g. [ { name : 'm1', startData:{} }, { name : 'm2', startData:{} }, ... ] 98 | * @param {Function} fpCallback Optional, the function to call when all modules are loaded and started, it receives the array of modules names 99 | */ 100 | requireAndStart : function ( aModulesData, fpCallback ) 101 | { 102 | var aModulesNames = []; 103 | 104 | if ( !Utils.isArray( aModulesData ) ) 105 | { 106 | aModulesData = [aModulesData]; 107 | } 108 | 109 | aModulesData.forEach( function ( data, nIndex ) 110 | { 111 | if ( typeof data === 'string' ) 112 | { 113 | aModulesData[nIndex] = data = { name : data, startData : {} }; 114 | } 115 | aModulesNames.push( data.name ); 116 | } ); 117 | 118 | _oAMDExt.require( aModulesNames, function () 119 | { 120 | aModulesData.forEach( function ( oModuleData ) 121 | { 122 | Module.start( oModuleData.name, oModuleData.startData ); 123 | } ); 124 | if ( fpCallback ) 125 | { 126 | fpCallback( aModulesData ); 127 | } 128 | } ); 129 | } 130 | }; 131 | 132 | // Install the default global error handler 133 | _oAMDExt.setErrorHandler( function ( eError ) 134 | { 135 | TinyCore.Error.log( 'Error loading module(s) "'+eError.requireModules+'": '+eError.message ); 136 | } ); 137 | 138 | // Define TinyCore a little bit more. 139 | TinyCore.AMD = _oAMDExt; 140 | 141 | } ( this ) ); 142 | -------------------------------------------------------------------------------- /src/extensions/Inheritance/README.md: -------------------------------------------------------------------------------- 1 | ## TinyCore.js / Extensions 2 | 3 | ### Module inheritance 4 | 5 | #### Features 6 | 7 | - Extends TinyCore's API with a new method : `TinyCore.Module.inherit`, that allows to reuse an existing module definition. 8 | - Works internally using [John Resig's Simple JavaScript Inheritance](http://ejohn.org/blog/simple-javascript-inheritance/) (prototypal inheritance/mixin). 9 | - Access to overriden methods is provided. 10 | - Less than 1Kb minified. 11 | 12 | #### Extended TinyCore.Module API 13 | 14 | ```js 15 | // existing 16 | TinyCore.Module.define( moduleName, toolsNames, creatorFunction ) 17 | TinyCore.Module.start( moduleName, startData ) 18 | TinyCore.Module.stop( moduleName, stopAndDestroy ) 19 | TinyCore.Module.instantiate( moduleName ) 20 | TinyCore.Module.getInstance( moduleName, instanceName ) 21 | TinyCore.Module.getModules() 22 | 23 | // extended 24 | TinyCore.Module.inherit( superModuleName, moduleName, toolsNames, creatorFunction ) 25 | ``` 26 | 27 | #### Example 28 | 29 | Let's define a base "person" module : 30 | 31 | ```js 32 | TinyCore.Module.define( 'person', [], function () 33 | { 34 | return { 35 | name : '', 36 | isLiving : null, 37 | onStart : function ( oStartData ) 38 | { 39 | this.name = oStartData.name; 40 | this.isLiving = true; 41 | }, 42 | onStop : function () 43 | { 44 | }, 45 | onDestroy : function () 46 | { 47 | } 48 | }; 49 | } ); 50 | ``` 51 | 52 | We can inherit from this module and create a "warrior" module : 53 | 54 | ```js 55 | TinyCore.Module.inherit( 'person', 'warrior', [], function () 56 | { 57 | return { 58 | isFighting : null, 59 | onStart : function ( oStartData ) 60 | { 61 | this._super( oStartData ); 62 | this.isFighting = true; 63 | }, 64 | onStop : function () 65 | { 66 | this.isFighting = false; 67 | this._super(); 68 | }, 69 | onDestroy : function () 70 | { 71 | this._super(); 72 | } 73 | }; 74 | } ); 75 | ``` 76 | 77 | Note that the "warrior" module has access to all the properties of the "person" module (`name`, `isLiving`). 78 | Also, the access to the overriden methods is provided via the `_super` function. 79 | 80 | Let's finally define a "samurai" module, inheriting from the "warrior" module : 81 | 82 | ```js 83 | TinyCore.Module.inherit( 'warrior', 'samurai', [], function () 84 | { 85 | return { 86 | followsBushido : null, 87 | onStart : function ( oStartData ) 88 | { 89 | this._super( oStartData ); 90 | this.followsBushido = true; 91 | }, 92 | onStop : function () 93 | { 94 | this.followsBushido = false; 95 | this._super(); 96 | }, 97 | onDestroy : function () 98 | { 99 | this._super(); 100 | } 101 | }; 102 | } ); 103 | ``` 104 | 105 | This final module can then be started/stopped/destroyed as usual : 106 | 107 | ```js 108 | TinyCore.Module.start( 'samurai', { name : 'Takeda Shingen' } ); 109 | 110 | TinyCore.Module.stop( 'samurai', true ); 111 | ``` 112 | -------------------------------------------------------------------------------- /src/extensions/Inheritance/TinyCore.Module.Inheritance.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Inheritance extension for TinyCore.js 3 | * @author mawrkus (web@sparring-partner.be) 4 | * @requires TinyCore.js 5 | */ 6 | ;( function ( oEnv ) 7 | { 8 | 'use strict'; 9 | 10 | var TinyCore = oEnv.TinyCore, 11 | Utils = TinyCore.Utils, 12 | Module = TinyCore.Module; 13 | 14 | /** 15 | * The inheritance extension. 16 | * @type {Object} 17 | */ 18 | var _oInheritanceExt = { 19 | /** 20 | * Defines a module that inherits from another. 21 | * @param {String} sSuperModuleName The name of the module to inherit from 22 | * @param {String} sModuleName The name of the module being defined 23 | * @param {Array} aToolsNames An array of tools names, that will be requested to the Toolbox and passed as arguments of fpCreator 24 | * @param {Function} fpCreator The function that creates and returns a module instance 25 | * @return {Boolean} Whether the module was successfuly or not 26 | */ 27 | inherit : function ( sSuperModuleName, sModuleName, aToolsNames, fpCreator ) 28 | { 29 | var oModulesData = Module.getModules(); 30 | 31 | if ( sSuperModuleName === sModuleName || 32 | !oModulesData[sSuperModuleName] || oModulesData[sModuleName] || 33 | !Utils.isFunction( fpCreator ) ) 34 | { 35 | return false; 36 | } 37 | 38 | oModulesData[sModuleName] = { 39 | fpCreator : fpCreator, 40 | oInstances : {}, 41 | aToolsNames : aToolsNames, 42 | sSuper : sSuperModuleName 43 | }; 44 | 45 | return true; 46 | }, 47 | /** 48 | * Instantiates a module, taking into account that it might be inherited from another. 49 | * Uses prototypal inheritance. 50 | * @param {String} sModuleName The module name 51 | * @return {Object} The module instance 52 | * @throws {Error} If the module has not been defined 53 | */ 54 | instantiate : ( function ( fpOriginalInstantiate ) 55 | { 56 | return function ( sModuleName ) 57 | { 58 | var oModulesData = Module.getModules(), 59 | oModuleData = oModulesData[sModuleName], 60 | aSuperChain = [], 61 | sCurrentName = sModuleName, 62 | nChainIndex, 63 | oInstance = {}, 64 | oSuperInstance; 65 | 66 | if ( !oModuleData ) 67 | { 68 | TinyCore.Error.report( 'The module "'+sModuleName+'" is not defined!' ); 69 | } 70 | 71 | do 72 | { 73 | aSuperChain.push( { name : sCurrentName, creator : oModuleData.fpCreator } ); 74 | sCurrentName = oModuleData.sSuper; 75 | oModuleData = oModulesData[sCurrentName]; 76 | } 77 | while ( oModuleData ); 78 | 79 | nChainIndex = aSuperChain.length - 1; 80 | 81 | do 82 | { 83 | aSuperChain[nChainIndex].creator.prototype = oInstance; 84 | oInstance = fpOriginalInstantiate.call( Module, aSuperChain[nChainIndex].name ); 85 | } 86 | while ( nChainIndex-- ); 87 | 88 | return oInstance; 89 | }; 90 | } ( Module.instantiate ) ) 91 | }; 92 | 93 | /** 94 | * The createModuleObject utilities extension. 95 | * @type {Object} 96 | */ 97 | var _oUtilsExt = { 98 | /** 99 | * Creates a new module object and sets properly its prototype. 100 | * @param {Function} fpCreator 101 | * @param {Array} aArgs 102 | * @return {Object} 103 | */ 104 | createModuleObject : function ( fpCreator, aArgs ) 105 | { 106 | var oModule = fpCreator.apply( null, aArgs ), 107 | F = function () {}, 108 | oPrototype = fpCreator.prototype; 109 | 110 | Utils.forIn( oModule, function ( prop, sPropName ) 111 | { 112 | var protoProp = oPrototype[sPropName]; 113 | 114 | if ( Utils.isObject( protoProp ) ) 115 | { 116 | // Allow a child property to enrich/replace a parent property. 117 | oPrototype[sPropName] = Utils.isObject( oModule[sPropName] ) ? 118 | Utils.extend( oPrototype[sPropName], oModule[sPropName] ) : 119 | oModule[sPropName]; 120 | } 121 | else if ( Utils.isFunction( protoProp ) ) 122 | { 123 | // Give access to the parent method. 124 | oPrototype[sPropName] = function () 125 | { 126 | var tmp = this._super, 127 | result; 128 | 129 | this._super = protoProp; 130 | result = oModule[sPropName].apply( this, arguments ); 131 | this._super = tmp; 132 | 133 | return result; 134 | }; 135 | } 136 | else 137 | { 138 | oPrototype[sPropName] = oModule[sPropName]; 139 | } 140 | } ); 141 | 142 | F.prototype = oPrototype; 143 | 144 | return new F(); 145 | } 146 | }; 147 | 148 | // Redefine TinyCore. 149 | Utils.extend( Utils, _oUtilsExt ); 150 | Utils.extend( Module, _oInheritanceExt ); 151 | 152 | } ( this ) ); 153 | -------------------------------------------------------------------------------- /src/extensions/Instances/README.md: -------------------------------------------------------------------------------- 1 | ## TinyCore.js / Extensions 2 | 3 | ### Multiple module instances 4 | 5 | #### Features 6 | 7 | - Extends TinyCore's API with 2 new methods : `TinyCore.Module.startInstance` and `TinyCore.Module.stopInstance`. These methods can be used to start/stop multiple instances of the same module. 8 | - Can be combined with the "Inheritance" extension (see the "extensions/Inheritance" folder) 9 | - Less than 1Kb minified. 10 | 11 | #### Extended TinyCore.Module API 12 | 13 | ```js 14 | // existing 15 | TinyCore.Module.define( moduleName, toolsNames, creatorFunction ) 16 | TinyCore.Module.start( moduleName, startData ) 17 | TinyCore.Module.stop( moduleName, stopAndDestroy ) 18 | TinyCore.Module.instantiate( moduleName ) 19 | TinyCore.Module.getInstance( moduleName, instanceName ) 20 | TinyCore.Module.getModules() 21 | 22 | // extended 23 | TinyCore.Module.startInstance( moduleName, instanceName, startData ) 24 | TinyCore.Module.stopInstance( moduleName, instanceName, stopAndDestroy ) 25 | ``` 26 | 27 | #### Example 28 | 29 | If we define an "elevator" module : 30 | 31 | ```js 32 | TinyCore.Module.define( 'elevator', [], function () 33 | { 34 | return { 35 | floor : null, 36 | /** 37 | * This (mandatory) function will be called when an instance is started. 38 | * @param {Object} oStartData The data passed when starting the instance. 39 | */ 40 | onStart : function ( oStartData ) 41 | { 42 | this.floor = oStartData.floor; 43 | }, 44 | /** 45 | * This (optional) function will be called when an instance is stopped. 46 | */ 47 | onStop : function () 48 | { 49 | // Cleanup (in order to be properly restarted?) 50 | this.floor = null; 51 | }, 52 | /** 53 | * This (optional) function will be called when an instance is destroyed. 54 | */ 55 | onDestroy : function () 56 | { 57 | // Complete cleanup! 58 | delete this.floor; 59 | } 60 | }; 61 | } ); 62 | ``` 63 | 64 | We can then start/stop 3 of them : 65 | 66 | ```js 67 | TinyCore.Module.startInstance( 'elevator', 'left-elevator', { currentFloor : +1 } ); 68 | TinyCore.Module.startInstance( 'elevator', 'center-elevator', { currentFloor : 0 } ); 69 | TinyCore.Module.startInstance( 'elevator', 'right-elevator', { currentFloor : -1 } ); 70 | 71 | TinyCore.Module.stopInstance( 'elevator', 'left-elevator' ); 72 | TinyCore.Module.stopInstance( 'elevator', 'right-elevator' ); 73 | TinyCore.Module.stopInstance( 'elevator', 'center-elevator', true ); // and destroy it 74 | 75 | // restart 76 | TinyCore.Module.startInstance( 'elevator', 'left-elevator', { currentFloor : 0 } ); 77 | ``` 78 | -------------------------------------------------------------------------------- /src/extensions/Instances/TinyCore.Module.Instances.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Multi-instances extension for TinyCore.js 3 | * @author mawrkus (web@sparring-partner.be) 4 | * @requires TinyCore.js 5 | */ 6 | ;( function ( oEnv ) 7 | { 8 | 'use strict'; 9 | 10 | var TinyCore = oEnv.TinyCore, 11 | Utils = TinyCore.Utils, 12 | Module = TinyCore.Module; 13 | 14 | /** 15 | * The multi-instances extension. 16 | * @type {Object} 17 | */ 18 | var _oInstancesExt = { 19 | /** 20 | * Starts a new module instance and calls its "onStart" method with oStartData passed as parameter. 21 | * @param {String} sModuleName The module name 22 | * @param {String} sInstanceName The instance name 23 | * @param {Object} oStartData Data to be passed to the module "onStart" method 24 | * @return {Boolean} Whether the instance has been started or not 25 | * @throws {Error} If the module has not been defined 26 | */ 27 | startInstance : function ( sModuleName, sInstanceName, oStartData ) 28 | { 29 | var oInstanceData = Module.getInstance( sModuleName, sInstanceName ); 30 | 31 | if ( !oInstanceData ) 32 | { 33 | oInstanceData = Module.getModules()[sModuleName].oInstances[sInstanceName] = { 34 | oInstance : Module.instantiate( sModuleName ) 35 | }; 36 | } 37 | 38 | if ( !oInstanceData.bIsStarted ) 39 | { 40 | oInstanceData.oInstance.onStart( oStartData, sInstanceName ); // onStart must be defined in the module. 41 | oInstanceData.bIsStarted = true; 42 | } 43 | 44 | return oInstanceData.bIsStarted; 45 | }, 46 | /** 47 | * Stops a module instance by calling its "onStop" method if it exists and unsubscribing from all subscribed topics. 48 | * @param {String} sModuleName The module name 49 | * @param {String} sInstanceName The instance name 50 | * @param {Boolean} Whether the instance should also be destroyed or not 51 | * @return {Boolean} Whether the instance has been stopped or not 52 | * @throws {Error} If the module has not been defined 53 | */ 54 | stopInstance : function ( sModuleName, sInstanceName, bAndDestroy ) 55 | { 56 | var oInstanceData = Module.getInstance( sModuleName, sInstanceName ); 57 | 58 | if ( !oInstanceData || !oInstanceData.oInstance ) 59 | { 60 | return false; 61 | } 62 | 63 | if ( oInstanceData.bIsStarted ) 64 | { 65 | if ( Utils.isFunction( oInstanceData.oInstance.onStop ) ) 66 | { 67 | oInstanceData.oInstance.onStop(); 68 | } 69 | oInstanceData.bIsStarted = false; 70 | } 71 | 72 | if ( bAndDestroy ) 73 | { 74 | if ( Utils.isFunction( oInstanceData.oInstance.onDestroy ) ) 75 | { 76 | oInstanceData.oInstance.onDestroy(); 77 | } 78 | delete Module.getModules()[sModuleName].oInstances[sInstanceName]; 79 | return true; 80 | } 81 | 82 | return !oInstanceData.bIsStarted; 83 | }, 84 | /** 85 | * Redefinition of the original method to start all instances. 86 | * @param {String} sModuleName The module name 87 | * @param {Object} oStartData Optional, the data to pass to each instance's "onStart" method 88 | * @return {Boolean} Whether all instances have been started or not 89 | * @throws {Error} If the module has not been defined 90 | */ 91 | start : ( function ( fpOriginalStart ) 92 | { 93 | return function ( sModuleName, oStartData ) 94 | { 95 | var oModuleData = Module.getModules()[sModuleName], 96 | bHasInstances = false, 97 | bAllStarted = true; 98 | 99 | if ( oModuleData ) 100 | { 101 | Utils.forIn( oModuleData.oInstances, function ( oInstance, sInstanceName ) 102 | { 103 | bHasInstances = true; 104 | if ( !Module.startInstance( sModuleName, sInstanceName, oStartData ) ) 105 | { 106 | bAllStarted = false; 107 | } 108 | } ); 109 | } 110 | 111 | if ( !bHasInstances ) 112 | { 113 | return fpOriginalStart.apply( Module, arguments ); 114 | } 115 | 116 | return bAllStarted; 117 | }; 118 | } ( Module.start ) ), 119 | /** 120 | * Redefinition of the original method to stop all instances. 121 | * @param {String} sModuleName The module name 122 | * @return {Boolean} Whether the module should also be destroyed or not 123 | * @return {Boolean} Whether all instances have been stopped or not 124 | * @throws {Error} If the module has not been defined 125 | */ 126 | stop : function ( sModuleName, bAndDestroy ) 127 | { 128 | var oModules = Module.getModules(), 129 | bAllStopped = true; 130 | 131 | Utils.forIn( oModules[sModuleName].oInstances, function ( oInstance, sInstanceName ) 132 | { 133 | if( !Module.stopInstance( sModuleName, sInstanceName, bAndDestroy ) ) 134 | { 135 | bAllStopped = false; 136 | } 137 | } ); 138 | 139 | if ( bAndDestroy && bAllStopped ) 140 | { 141 | delete oModules[sModuleName]; 142 | } 143 | 144 | return bAllStopped; 145 | } 146 | }; 147 | 148 | // Define TinyCore a little bit more. 149 | Utils.extend( Module, _oInstancesExt ); 150 | 151 | } ( this ) ); 152 | -------------------------------------------------------------------------------- /src/extensions/Jasmine/README.md: -------------------------------------------------------------------------------- 1 | ## TinyCore.js / Extensions 2 | 3 | ### Jasmine testing, fake tools 4 | 5 | #### Features 6 | 7 | - This extension works with the [Jasmine framework](http://pivotal.github.io/jasmine/). It decorates the original `TinyCore.Toolbox.request` method, so that, whenever a module tool is requested, all its methods are automatically stubbed using the Jasmine's `spyOn` function. The same "fake" tool will always be returned each time `TinyCore.Toolbox.request` is called. 8 | - Less than 500 bytes minified. 9 | 10 | #### Example 11 | 12 | We register 2 tools, `dom` and `db` : 13 | 14 | ```js 15 | TinyCore.Toolbox.register( 'dom', function () 16 | { 17 | var doc = window.document; 18 | 19 | return { 20 | byClass : function ( sClass ) 21 | { 22 | return doc.querySelectorAll( '.'+sClass ); 23 | }, 24 | byTagName : function ( sTagName ) 25 | { 26 | return doc.getElementsByTagName( sTagName ); 27 | } 28 | }; 29 | } ); 30 | 31 | TinyCore.Toolbox.register( 'db', function () 32 | { 33 | return { 34 | find : function ( oQuery ) { return {}; } 35 | }; 36 | } ); 37 | ``` 38 | 39 | We define the "user" module, that uses both tools : 40 | 41 | ```js 42 | TinyCore.Module.define( 'user', [ 'dom', 'db' ], function ( DOM, DB ) 43 | { 44 | return { 45 | onStart : function ( oStartData ) 46 | { 47 | var userData = DB.find( { user : oStartData.user } ), 48 | container = DOM.byClass( '.current-user' ); 49 | } 50 | }; 51 | } ); 52 | ``` 53 | 54 | In our Jasmine tests, all the methods are already stubbed, allowing us to focus only on verifying the expectations : 55 | 56 | ```js 57 | describe( 'TinyCore.Toolbox.request', function () 58 | { 59 | it( 'should provide unique tools, with all methods stubbed, to the creator function of the module', function () 60 | { 61 | var oUserModule = TinyCore.Module.instantiate( 'user' ), 62 | DB = TinyCore.Toolbox.request( 'db' ), 63 | DOM = TinyCore.Toolbox.request( 'dom' ), 64 | oStartData = { user : 'blake' }; 65 | 66 | oUserModule.onStart( oStartData ); 67 | 68 | expect( DB.find.calls.length ).toBe( 1 ); 69 | expect( DB.find ).toHaveBeenCalledWith( oStartData ); 70 | 71 | expect( DOM.byClass.calls.length ).toBe( 1 ); 72 | expect( DOM.byClass ).toHaveBeenCalledWith( '.current-user' ); 73 | } ); 74 | } ); 75 | ``` 76 | -------------------------------------------------------------------------------- /src/extensions/Jasmine/TinyCore.Toolbox.Jasmine.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Jasmine testing extension for the Toolbox of TinyCore.js 3 | * @author mawrkus (web@sparring-partner.be) 4 | * @requires TinyCore.js 5 | */ 6 | ;( function ( oEnv ) 7 | { 8 | 'use strict'; 9 | 10 | var TinyCore = oEnv.TinyCore, 11 | Utils = TinyCore.Utils, 12 | Toolbox = TinyCore.Toolbox, 13 | jasmine = oEnv.jasmine, 14 | spyOn = oEnv.spyOn; 15 | 16 | if ( !jasmine || !Utils.isFunction( spyOn ) ) 17 | { 18 | throw new Error( 'Cannot add Jasmine extension to TinyCore: Jasmine seems to be missing!' ); 19 | } 20 | 21 | /** 22 | * Already stubbed tools. 23 | * @type {Object} 24 | */ 25 | var _oStubbedTools = {}, 26 | /** 27 | * Spies on all the methods of the given object. 28 | * @param {Object} oObject 29 | * @return {Object} 30 | */ 31 | _fpCreateStub = function ( oObject ) 32 | { 33 | if ( !oObject ) 34 | { 35 | return null; 36 | } 37 | 38 | Utils.forIn( oObject, function ( prop, propName ) 39 | { 40 | if ( Utils.isFunction( prop ) && !prop.isSpy ) 41 | { 42 | spyOn( oObject, propName ); 43 | } 44 | } ); 45 | 46 | return oObject; 47 | }; 48 | 49 | /** 50 | * The Jasmine extension. 51 | * @type {Object} 52 | */ 53 | var _oJasmineExt = { 54 | /** 55 | * Returns the tool requested with all its methods stubbed. 56 | * @param {String} sToolName 57 | * @return {Mixed} The tool requested or null 58 | */ 59 | request : ( function ( fpOriginalRequest ) 60 | { 61 | return function ( sToolName ) 62 | { 63 | var oTool = _oStubbedTools[sToolName]; 64 | if ( oTool ) 65 | { 66 | // Spies are torn down at the end of every spec. 67 | return _fpCreateStub( oTool ); 68 | } 69 | 70 | oTool = fpOriginalRequest.apply( Toolbox, arguments ); 71 | 72 | // We always return the same stubbed version. 73 | _oStubbedTools[sToolName] = _fpCreateStub( oTool ); 74 | 75 | return oTool; 76 | }; 77 | } ( Toolbox.request ) ) 78 | }; 79 | 80 | // Redefine TinyCore . 81 | Utils.extend( Toolbox, _oJasmineExt ); 82 | 83 | } ( this ) ); 84 | -------------------------------------------------------------------------------- /src/extensions/README.md: -------------------------------------------------------------------------------- 1 | ## TinyCore.js extensions 2 | 3 | ### AMD 4 | 5 | A wrapper over [require.js](http://requirejs.org) that provides async modules loading using the [Asynchronous Module Definition](https://github.com/amdjs/amdjs-api/wiki/AMD) format. 6 | 7 | ### Inheritance 8 | 9 | Reuse existing modules definitions, having access to the overriden methods. 10 | 11 | ### Instances 12 | 13 | Start/stop multiple instances of the same module. 14 | 15 | ### Jasmine 16 | 17 | Easier testing with the [Jasmine framework](http://pivotal.github.io/jasmine/). 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/tools/mediator/README.md: -------------------------------------------------------------------------------- 1 | ## TinyCore.js / Module tools 2 | 3 | ### The Mediator 4 | 5 | #### Features 6 | 7 | - The [mediator](http://addyosmani.com/largescalejavascript/#mediatorpattern) tool implements a simple publish/subscribe system, for inter-modules communication. 8 | - Less than 1Kb minified. 9 | 10 | #### API 11 | 12 | ```js 13 | var Mediator = { 14 | subscribe : function ( topics, handler, context ) {}, 15 | publish : function ( topic, data ) {}, 16 | unsubscribe : function ( topics ) {}, 17 | unsubscribeAll : function () {} 18 | }; 19 | ``` 20 | 21 | #### Example 22 | 23 | The Mediator can be requested explicitly via `TinyCore.Toolbox.request` : 24 | 25 | ```js 26 | var Mediator = TinyCore.Toolbox.request( 'mediator' ); 27 | 28 | Mediator.publish( 'phone:call', { from : '+34123456789' } ); 29 | ``` 30 | 31 | Or can be requested by the module, when defined : 32 | 33 | ```js 34 | TinyCore.Module.define( 'phone', ['mediator'], function ( mediator ) 35 | { 36 | return { 37 | onStart : function () 38 | { 39 | var self = this; 40 | 41 | mediator.publish( 'phone:boot', { when : +new Date() } ); 42 | 43 | mediator.subscribe( 'phone:call', function ( oTopic ) 44 | { 45 | var oCallData = oTopic.data; 46 | self.displayMessage( 'Call from '+oCallData.from ); 47 | } ); 48 | 49 | // Subscribe to several topics and... 50 | // ...specify the context in which "onBatteryStateChanged" should be executed. 51 | mediator.subscribe( ['battery:charged', 'battery:empty'], this.onBatteryStateChanged, this ); 52 | }, 53 | onStop : function () 54 | { 55 | mediator.unsubscribe( ['phone:call', 'battery:charged'] ); 56 | mediator.unsubscribe( 'battery:empty' ); 57 | // mediator.unsubscribeAll(); 58 | }, 59 | onBatteryStateChanged : function ( oTopic ) 60 | { 61 | var sTopicName = oTopic.name, 62 | sMsg = sTopicName === 'battery:charged' ? 63 | 'Battery fully charged.' : 64 | 'Battery is empty!'; 65 | 66 | this.displayMessage( sMsg ); 67 | }, 68 | displayMessage : function ( sMsg ) 69 | { 70 | alert( sMsg ); 71 | } 72 | }; 73 | } ); 74 | ``` 75 | -------------------------------------------------------------------------------- /src/tools/mediator/TinyCore.Toolbox.Mediator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A mediator implementation for the TinyCore.js modules. 3 | * @author mawrkus (web@sparring-partner.be) 4 | * @requires TinyCore 5 | */ 6 | ;( function ( oEnv ) 7 | { 8 | 'use strict'; 9 | 10 | var TinyCore = oEnv.TinyCore, 11 | Utils = TinyCore.Utils; 12 | 13 | /** 14 | * The one and only mediator. 15 | * param {Number} nID The internal mediator ID 16 | */ 17 | var Mediator = function ( nID ) 18 | { 19 | this.nSubscriberID = nID; 20 | }; 21 | 22 | Mediator.prototype = ( function () 23 | { 24 | /** 25 | * The topics data, e.g. : 26 | * { 27 | * 'graph:color:change' : { 0:fn0, 3:fn3 }, 28 | * 'filter:date:change' : { 1:fn1, 2:fn2 ,3:fn3 } 29 | * } 30 | * 0, 1, 2, 3 are the IDs of the subscribers and fn0, fn1, fn2, fn3, their related handlers. 31 | * @type {Object} 32 | */ 33 | var _oTopic2Subs = {}, 34 | /** 35 | * The subscribers data, given a subscriber's ID, this will allow us to retrieve easily all the topics subscribed, e.g. : 36 | * { 37 | * 0 : [ 'graph:color:change' ], 38 | * 1 : [ 'filter:date:change' ], 39 | * 2 : [ 'filter:date:change' ], 40 | * 3 : [ 'graph:color:change', 'filter:date:change' ], 41 | * } 42 | * 0, 1, 2, 3 are the IDs of the subscribers. 43 | * @type {Object} 44 | */ 45 | _oSub2Topics = {}; 46 | 47 | return { 48 | /** 49 | * Subscribes to one or more topics. 50 | * @param {String|Array} topics A topic name or an array of topics to subscribe to 51 | * @param {Function} fpHandler The topic handler 52 | * @param {Object} oContext Optional, the context in which the handler should be executed 53 | */ 54 | subscribe : function ( topics, fpHandler, oContext ) 55 | { 56 | var aTopics = Utils.isArray( topics ) ? topics : [topics], 57 | nSubscriberID = this.nSubscriberID; 58 | 59 | aTopics.forEach( function ( sTopic ) 60 | { 61 | _oTopic2Subs[sTopic] = _oTopic2Subs[sTopic] || {}; 62 | 63 | // Don't allow the same subscriber twice. 64 | if ( !_oTopic2Subs[sTopic][nSubscriberID] ) 65 | { 66 | // Catch handler errors? 67 | _oTopic2Subs[sTopic][nSubscriberID] = TinyCore.debugMode ? 68 | fpHandler.bind( oContext ) : 69 | Utils.tryCatchDecorator( oContext, fpHandler, 'Error publishing topic "' + sTopic + '": ' ); 70 | 71 | _oSub2Topics[nSubscriberID] = _oSub2Topics[nSubscriberID] || []; 72 | _oSub2Topics[nSubscriberID].push( sTopic ); 73 | } 74 | } ); 75 | }, 76 | /** 77 | * Publishes a topic. 78 | * @param {String} sTopic 79 | * @param {Object} oData 80 | * @param {Boolean} bDontCopyData 81 | */ 82 | publish : function ( sTopic, oData, bDontCopyData ) 83 | { 84 | var oDataOrCopy = bDontCopyData ? oData : oData && Utils.extend( {}, oData ); 85 | 86 | Utils.forIn( _oTopic2Subs[sTopic], function ( fpHandler ) 87 | { 88 | // Just in case, delay the handler execution after what's currently in line. 89 | oEnv.setTimeout( function () 90 | { 91 | fpHandler( { name : sTopic, data : oDataOrCopy } ); 92 | }, 0 ); 93 | } ); 94 | }, 95 | /** 96 | * Unsubscribes from one or more topics. 97 | * @param {String|Array} topics A topic or an array of topics to unsubscribe from 98 | */ 99 | unsubscribe : function ( topics ) 100 | { 101 | var aTopics = Utils.isArray( topics ) ? topics : [topics], 102 | nTopicsCount = aTopics.length, 103 | oSubs, 104 | nSubscriberID = this.nSubscriberID; 105 | 106 | while ( nTopicsCount-- ) 107 | { 108 | oSubs = _oTopic2Subs[aTopics[nTopicsCount]]; 109 | if ( oSubs && oSubs[nSubscriberID] ) 110 | { 111 | delete oSubs[nSubscriberID]; 112 | } 113 | } 114 | }, 115 | /** 116 | * Unsubscribes from all subscribed topics. 117 | */ 118 | unsubscribeAll : function () 119 | { 120 | if ( _oSub2Topics[this.nSubscriberID] ) 121 | { 122 | this.unsubscribe( _oSub2Topics[this.nSubscriberID] ); 123 | } 124 | } 125 | }; 126 | } () ); 127 | 128 | // Add our mediator factory to the available TinyCore tools. 129 | TinyCore.Toolbox.register( 'mediator', function ( nID ) 130 | { 131 | return new Mediator( nID ); 132 | } ); 133 | 134 | } ( this ) ); 135 | -------------------------------------------------------------------------------- /test/jasmine/TinyCore.SpecHelper.js: -------------------------------------------------------------------------------- 1 | // Add HTML helpers and matchers. 2 | beforeEach( function () 3 | { 4 | var nSuiteID = this.suite.id; 5 | 6 | this.prepareHTML = function () 7 | { 8 | /*console.info(this.suite.description); 9 | console.log('> '+this.description);*/ 10 | 11 | var sContainerID = 'suite-'+nSuiteID; 12 | oSuiteContainer = document.createElement( 'div' ); 13 | oSuiteContainer.id = sContainerID; 14 | document.body.appendChild( oSuiteContainer ); 15 | }; 16 | this.cleanHTML = function () 17 | { 18 | var sContainerID = 'suite-'+nSuiteID, 19 | oSuiteContainer = document.getElementById( sContainerID ); 20 | if ( oSuiteContainer ) 21 | { 22 | oSuiteContainer.parentNode.removeChild( oSuiteContainer ); 23 | } 24 | }; 25 | this.addHTML = function ( sHTML ) 26 | { 27 | var sContainerID = 'suite-'+nSuiteID, 28 | oSuiteContainer = document.getElementById( sContainerID ); 29 | oSuiteContainer.innerHTML += sHTML; 30 | }; 31 | 32 | this.prepareHTML(); 33 | 34 | // Additional matchers. 35 | this.addMatchers( { 36 | toBeFunction : function () 37 | { 38 | return Object.prototype.toString.call( this.actual ) === '[object Function]'; 39 | }, 40 | toBeObject : function () 41 | { 42 | return Object.prototype.toString.call( this.actual ) === '[object Object]'; 43 | }, 44 | toBeString : function () 45 | { 46 | return Object.prototype.toString.call( this.actual ) === '[object String]'; 47 | } 48 | } ); 49 | }); 50 | 51 | // Cleanup HTML. 52 | afterEach( function () 53 | { 54 | this.cleanHTML(); 55 | }); 56 | -------------------------------------------------------------------------------- /test/jasmine/TinyCore.SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TinyCore /// Jasmine Spec Runner 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /test/jasmine/extensions/AMD/RequireMock.js: -------------------------------------------------------------------------------- 1 | window.define = function ( sModuleName, aDependencies, fpCreator ) 2 | { 3 | fpCreator.apply( null, aDependencies ); 4 | }; 5 | 6 | window.require = function ( aModulesNames, fpCallback ) 7 | { 8 | fpCallback.apply( null, aModulesNames ); 9 | }; 10 | 11 | window.require.config = function () {}; 12 | -------------------------------------------------------------------------------- /test/jasmine/extensions/AMD/TinyCore.AMD.Spec.js: -------------------------------------------------------------------------------- 1 | // Don't catch errors. 2 | TinyCore.debugMode = true; 3 | 4 | /** 5 | * ------- AMD MODULES + REQUIRE TESTS ------- 6 | */ 7 | 8 | describe( 'TinyCore.AMD', function () 9 | { 10 | it( 'should have an interface with the following methods/properties : config, setErrorHandler, define, require, requireAndStart', function () 11 | { 12 | expect( TinyCore.AMD ).toBeObject(); 13 | expect( TinyCore.AMD.config ).toBeFunction(); 14 | expect( TinyCore.AMD.setErrorHandler ).toBeFunction(); 15 | expect( TinyCore.AMD.define ).toBeFunction(); 16 | expect( TinyCore.AMD.require ).toBeFunction(); 17 | expect( TinyCore.AMD.requireAndStart ).toBeFunction(); 18 | } ); 19 | } ); 20 | 21 | describe( 'TinyCore.AMD.config', function () 22 | { 23 | it( 'should call config() from require.js properly', function () 24 | { 25 | var oSettings = { 26 | require : { 27 | baseUrl : '../../foo' 28 | } 29 | }; 30 | 31 | spyOn( require, 'config' ); 32 | 33 | TinyCore.AMD.config( oSettings ); 34 | 35 | expect( require.config ).toHaveBeenCalledWith( oSettings.require ); 36 | } ); 37 | 38 | it( 'should return the current configuration if no parameter is passed', function () 39 | { 40 | var oSettings = { 41 | require : { 42 | baseUrl : '../../bar' 43 | } 44 | }; 45 | 46 | TinyCore.AMD.config( oSettings ); 47 | 48 | expect( TinyCore.AMD.config() ).toEqual( oSettings ); 49 | } ); 50 | } ); 51 | 52 | describe( 'TinyCore.AMD.setErrorHandler', function () 53 | { 54 | it( 'should set the require.js onError() handler properly', function () 55 | { 56 | var fpErrorLog = function () {}; 57 | 58 | TinyCore.AMD.setErrorHandler( fpErrorLog ); 59 | 60 | expect( require.onError ).toBe( fpErrorLog ); 61 | } ); 62 | } ); 63 | 64 | describe( 'TinyCore.AMD.define', function () 65 | { 66 | it( 'should define the module using require.js and then define it properly using TinyCore', function () 67 | { 68 | var sModuleName = 'foo', 69 | aModuleDeps = ['bar', 'other/baz'], 70 | oModule = {}, 71 | fpModuleCreator = jasmine.createSpy().andReturn( oModule ), 72 | aModuleDepsBaseNames = ['bar', 'baz']; 73 | 74 | spyOn( window, 'define' ).andCallThrough(); 75 | spyOn( TinyCore.Module, 'define' ); 76 | 77 | TinyCore.AMD.define( sModuleName, aModuleDeps, fpModuleCreator ); 78 | 79 | expect( window.define ).toHaveBeenCalled(); 80 | expect( window.define.calls[0].args[0] ).toBe( sModuleName ); 81 | expect( window.define.calls[0].args[1] ).toBe( aModuleDeps ); 82 | 83 | expect( TinyCore.Module.define ).toHaveBeenCalled(); 84 | expect( TinyCore.Module.define.calls[0].args[0] ).toBe( sModuleName ); 85 | expect( TinyCore.Module.define.calls[0].args[1] ).toEqual( aModuleDepsBaseNames ); 86 | expect( TinyCore.Module.define.calls[0].args[2] ).toBe( fpModuleCreator ); 87 | } ); 88 | } ); 89 | 90 | describe( 'TinyCore.AMD.require', function () 91 | { 92 | it( 'should call the require() function from require.js properly', function () 93 | { 94 | var aModuleNames = ['init', 'start', 'go'], 95 | fpCallback = jasmine.createSpy(); 96 | 97 | spyOn( window, 'require' ).andCallThrough(); 98 | 99 | TinyCore.AMD.require( aModuleNames, fpCallback ); 100 | 101 | expect( window.require ).toHaveBeenCalled(); 102 | expect( window.require.calls[0].args[0] ).toBe( aModuleNames ); 103 | expect( fpCallback ).toHaveBeenCalledWith( 'init', 'start', 'go' ); 104 | } ); 105 | } ); 106 | 107 | describe( 'TinyCore.AMD.requireAndStart', function () 108 | { 109 | it( 'should properly require and start the modules using require.js and TinyCore', function () 110 | { 111 | var aModulesData = [ 112 | { name : 'red', startData : { light : true } }, 113 | 'green', 114 | { name : 'blue', startData : { dark : false } } 115 | ], 116 | fpCallback = jasmine.createSpy(); 117 | 118 | spyOn( TinyCore.AMD, 'require' ).andCallThrough(); 119 | spyOn( TinyCore.Module, 'start' ); 120 | 121 | TinyCore.AMD.requireAndStart( aModulesData, fpCallback ); 122 | 123 | expect( TinyCore.AMD.require.calls.length ).toBe( 1 ); 124 | expect( TinyCore.AMD.require.calls[0].args[0] ).toEqual( ['red', 'green', 'blue'] ); 125 | 126 | expect( TinyCore.Module.start.calls.length ).toBe( 3 ); 127 | expect( TinyCore.Module.start.calls[0].args[0] ).toBe( aModulesData[0].name ); 128 | expect( TinyCore.Module.start.calls[0].args[1] ).toBe( aModulesData[0].startData ); 129 | expect( TinyCore.Module.start.calls[1].args[0] ).toBe( 'green' ); 130 | expect( TinyCore.Module.start.calls[1].args[1] ).toEqual( {} ); 131 | expect( TinyCore.Module.start.calls[2].args[0] ).toBe( aModulesData[2].name ); 132 | expect( TinyCore.Module.start.calls[2].args[1] ).toBe( aModulesData[2].startData ); 133 | 134 | expect( fpCallback ).toHaveBeenCalled(); 135 | } ); 136 | } ); -------------------------------------------------------------------------------- /test/jasmine/extensions/AMD/TinyCore.AMD.SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TinyCore /// Jasmine Spec Runner 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /test/jasmine/extensions/AMD/TinyCore.AMD.domBoot.SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TinyCore /// Jasmine Spec Runner 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /test/jasmine/extensions/Inheritance+Instances/TinyCore.Module.Inheritance+Instances.Spec.js: -------------------------------------------------------------------------------- 1 | // Don't catch errors. 2 | TinyCore.debugMode = true; 3 | 4 | // Tools. 5 | TinyCore.Toolbox.register( 'armour', function () 6 | { 7 | return { 8 | name : 'armour', 9 | damaged : true, 10 | repair : function () 11 | { 12 | this.damaged = false; 13 | } 14 | }; 15 | } ); 16 | TinyCore.Toolbox.register( 'sword', function () 17 | { 18 | return { 19 | name : 'sword', 20 | isClean : false, 21 | clean : function () 22 | { 23 | this.isClean = true; 24 | } 25 | }; 26 | } ); 27 | 28 | var sDestructionTrace; 29 | 30 | TinyCore.Module.define( 'person', [], function () 31 | { 32 | return { 33 | rank : '', 34 | isLiving : null, 35 | onStart : function ( oStartData ) 36 | { 37 | this.rank = oStartData.rank; 38 | this.isLiving = true; 39 | }, 40 | onStop : function () 41 | { 42 | }, 43 | onDestroy : function () 44 | { 45 | this.isLiving = false; 46 | sDestructionTrace += '>person'; 47 | } 48 | }; 49 | } ); 50 | 51 | TinyCore.Module.inherit( 'person', 'warrior', ['armour'], function ( armour ) 52 | { 53 | return { 54 | isFighting : null, 55 | isArmourDamaged : null, 56 | onStart : function ( oStartData ) 57 | { 58 | this._super( oStartData ); 59 | 60 | this.isFighting = true; 61 | 62 | if ( this.rank === '#2' ) 63 | { 64 | armour.repair(); 65 | } 66 | }, 67 | onStop : function () 68 | { 69 | this.isArmourDamaged = armour.damaged; 70 | this.isFighting = false; 71 | this._super(); 72 | }, 73 | onDestroy : function () 74 | { 75 | sDestructionTrace += '>warrior'; 76 | this._super(); 77 | } 78 | }; 79 | } ); 80 | 81 | TinyCore.Module.inherit( 'warrior', 'samurai', ['sword'], function ( sword ) 82 | { 83 | return { 84 | followsBushido : null, 85 | onStart : function ( oStartData ) 86 | { 87 | this._super( oStartData ); 88 | this.followsBushido = true; 89 | }, 90 | onStop : function () 91 | { 92 | this.followsBushido = false; 93 | this._super(); 94 | }, 95 | onDestroy : function () 96 | { 97 | sDestructionTrace += 'samurai'; 98 | this._super(); 99 | } 100 | }; 101 | } ); 102 | 103 | describe( 'TinyCore.Module.StartInstance', function () 104 | { 105 | 106 | it( 'should start/stop/destroy several inherited instances properly', function () 107 | { 108 | var oSamuraiInstance1, 109 | oSamuraiInstance2; 110 | 111 | TinyCore.Module.startInstance( 'samurai', 'Oda Nobunaga', { rank : '#1' } ); 112 | TinyCore.Module.startInstance( 'samurai', 'Toyotomi Hideyoshi', { rank : '#2' } ); 113 | 114 | oSamuraiInstance1 = TinyCore.Module.getInstance( 'samurai', 'Oda Nobunaga' ).oInstance; 115 | oSamuraiInstance2 = TinyCore.Module.getInstance( 'samurai', 'Toyotomi Hideyoshi' ).oInstance; 116 | 117 | expect( oSamuraiInstance1.rank ).toBe( '#1' ); 118 | expect( oSamuraiInstance1.isLiving ).toBe( true ); 119 | expect( oSamuraiInstance1.isFighting ).toBe( true ); 120 | expect( oSamuraiInstance1.followsBushido ).toBe( true ); 121 | 122 | expect( oSamuraiInstance2.rank ).toBe( '#2' ); 123 | expect( oSamuraiInstance2.isLiving ).toBe( true ); 124 | expect( oSamuraiInstance2.isFighting ).toBe( true ); 125 | expect( oSamuraiInstance2.followsBushido ).toBe( true ); 126 | 127 | sDestructionTrace = ''; 128 | 129 | TinyCore.Module.stopInstance( 'samurai', 'Oda Nobunaga', true ); 130 | 131 | expect( oSamuraiInstance1.isLiving ).toBe( false ); 132 | expect( oSamuraiInstance1.isArmourDamaged ).toBe( true ); 133 | expect( oSamuraiInstance1.isFighting ).toBe( false ); 134 | expect( oSamuraiInstance1.followsBushido ).toBe( false ); 135 | expect( sDestructionTrace ).toBe( 'samurai>warrior>person' ); 136 | 137 | TinyCore.Module.stopInstance( 'samurai', 'Toyotomi Hideyoshi' ); 138 | 139 | expect( oSamuraiInstance2.rank ).toBe( '#2' ); 140 | expect( oSamuraiInstance2.isLiving ).toBe( true ); 141 | expect( oSamuraiInstance2.isArmourDamaged ).toBe( false ); 142 | expect( oSamuraiInstance2.isFighting ).toBe( false ); 143 | expect( oSamuraiInstance2.followsBushido ).toBe( false ); 144 | } ); 145 | } ); 146 | -------------------------------------------------------------------------------- /test/jasmine/extensions/Inheritance+Instances/TinyCore.Module.Inheritance+Instances.SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TinyCore /// Jasmine Spec Runner 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /test/jasmine/extensions/Inheritance/TinyCore.Module.Inheritance.SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TinyCore /// Jasmine Spec Runner 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/jasmine/extensions/Jasmine/TinyCore.Toolbox.Jasmine.Spec.js: -------------------------------------------------------------------------------- 1 | // Don't catch errors. 2 | TinyCore.debugMode = true; 3 | 4 | /** 5 | * ------- TOOLBOX > REQUEST W/ JASMINE TESTS ------- 6 | */ 7 | 8 | TinyCore.Toolbox.register( 'dom', function () 9 | { 10 | var doc = window.document; 11 | 12 | return { 13 | byClass : function ( sClass ) 14 | { 15 | return doc.querySelectorAll( '.'+sClass ); 16 | }, 17 | byTagName : function ( sTagName ) 18 | { 19 | return doc.getElementsByTagName( sTagName ); 20 | } 21 | }; 22 | } ); 23 | 24 | TinyCore.Toolbox.register( 'db', function () 25 | { 26 | return { 27 | find : function ( oQuery ) { return {}; } 28 | }; 29 | } ); 30 | 31 | TinyCore.Module.define( 'user', [ 'dom', 'db' ], function ( DOM, DB ) 32 | { 33 | return { 34 | onStart : function ( oStartData ) 35 | { 36 | var userData = DB.find( { user : oStartData.user } ), 37 | container = DOM.byClass( '.current-user' ); 38 | } 39 | }; 40 | } ); 41 | 42 | describe( 'TinyCore.Toolbox.request', function () 43 | { 44 | it( 'should provide unique tools, with all methods stubbed, to the creator function of the module', function () 45 | { 46 | var oUserModule = TinyCore.Module.instantiate( 'user' ), 47 | DB = TinyCore.Toolbox.request( 'db' ), 48 | DOM = TinyCore.Toolbox.request( 'dom' ), 49 | oStartData = { user : 'blake' }; 50 | 51 | oUserModule.onStart( oStartData ); 52 | 53 | expect( DB.find.calls.length ).toBe( 1 ); 54 | expect( DB.find ).toHaveBeenCalledWith( oStartData ); 55 | 56 | expect( DOM.byClass.calls.length ).toBe( 1 ); 57 | expect( DOM.byClass ).toHaveBeenCalledWith( '.current-user' ); 58 | } ); 59 | } ); 60 | -------------------------------------------------------------------------------- /test/jasmine/extensions/Jasmine/TinyCore.Toolbox.Jasmine.SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TinyCore /// Jasmine Spec Runner 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/jasmine/extensions/instances/TinyCore.Module.Instances.SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TinyCore /// Jasmine Spec Runner 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/jasmine/lib/jasmine-1.3.1/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2011 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/jasmine/lib/jasmine-1.3.1/jasmine.css: -------------------------------------------------------------------------------- 1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } 2 | 3 | #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } 4 | #HTMLReporter a { text-decoration: none; } 5 | #HTMLReporter a:hover { text-decoration: underline; } 6 | #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } 7 | #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } 8 | #HTMLReporter #jasmine_content { position: fixed; right: 100%; } 9 | #HTMLReporter .version { color: #aaaaaa; } 10 | #HTMLReporter .banner { margin-top: 14px; } 11 | #HTMLReporter .duration { color: #aaaaaa; float: right; } 12 | #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } 13 | #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } 14 | #HTMLReporter .symbolSummary li.passed { font-size: 14px; } 15 | #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } 16 | #HTMLReporter .symbolSummary li.failed { line-height: 9px; } 17 | #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } 18 | #HTMLReporter .symbolSummary li.skipped { font-size: 14px; } 19 | #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } 20 | #HTMLReporter .symbolSummary li.pending { line-height: 11px; } 21 | #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } 22 | #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } 23 | #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 24 | #HTMLReporter .runningAlert { background-color: #666666; } 25 | #HTMLReporter .skippedAlert { background-color: #aaaaaa; } 26 | #HTMLReporter .skippedAlert:first-child { background-color: #333333; } 27 | #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } 28 | #HTMLReporter .passingAlert { background-color: #a6b779; } 29 | #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } 30 | #HTMLReporter .failingAlert { background-color: #cf867e; } 31 | #HTMLReporter .failingAlert:first-child { background-color: #b03911; } 32 | #HTMLReporter .results { margin-top: 14px; } 33 | #HTMLReporter #details { display: none; } 34 | #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } 35 | #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } 36 | #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } 37 | #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } 38 | #HTMLReporter.showDetails .summary { display: none; } 39 | #HTMLReporter.showDetails #details { display: block; } 40 | #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } 41 | #HTMLReporter .summary { margin-top: 14px; } 42 | #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } 43 | #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } 44 | #HTMLReporter .summary .specSummary.failed a { color: #b03911; } 45 | #HTMLReporter .description + .suite { margin-top: 0; } 46 | #HTMLReporter .suite { margin-top: 14px; } 47 | #HTMLReporter .suite a { color: #333333; } 48 | #HTMLReporter #details .specDetail { margin-bottom: 28px; } 49 | #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } 50 | #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } 51 | #HTMLReporter .resultMessage span.result { display: block; } 52 | #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } 53 | 54 | #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } 55 | #TrivialReporter a:visited, #TrivialReporter a { color: #303; } 56 | #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } 57 | #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } 58 | #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } 59 | #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } 60 | #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } 61 | #TrivialReporter .runner.running { background-color: yellow; } 62 | #TrivialReporter .options { text-align: right; font-size: .8em; } 63 | #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } 64 | #TrivialReporter .suite .suite { margin: 5px; } 65 | #TrivialReporter .suite.passed { background-color: #dfd; } 66 | #TrivialReporter .suite.failed { background-color: #fdd; } 67 | #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } 68 | #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } 69 | #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } 70 | #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } 71 | #TrivialReporter .spec.skipped { background-color: #bbb; } 72 | #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } 73 | #TrivialReporter .passed { background-color: #cfc; display: none; } 74 | #TrivialReporter .failed { background-color: #fbb; } 75 | #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } 76 | #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } 77 | #TrivialReporter .resultMessage .mismatch { color: black; } 78 | #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } 79 | #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } 80 | #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } 81 | #TrivialReporter #jasmine_content { position: fixed; right: 100%; } 82 | #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } 83 | -------------------------------------------------------------------------------- /test/jasmine/tools/mediator/TinyCore.Toolbox.Mediator.SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | TinyCore /// Jasmine Spec Runner 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/karma/core.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | var shared = require( './shared.conf' ); 3 | 4 | module.exports = function( config ) 5 | { 6 | shared( config ); 7 | 8 | config.files = [ 9 | 'build/TinyCore.js', 10 | 'test/jasmine/TinyCore.SpecHelper.js', 11 | 'test/jasmine/TinyCore.Spec.js' 12 | ]; 13 | }; 14 | -------------------------------------------------------------------------------- /test/karma/ext.amd.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | var shared = require( './shared.conf' ); 3 | 4 | module.exports = function( config ) 5 | { 6 | shared( config ); 7 | 8 | config.files = [ 9 | 'build/TinyCore.js', 10 | 'test/jasmine/extensions/AMD/RequireMock.js', 11 | 'src/extensions/AMD/TinyCore.AMD.js', 12 | 'test/jasmine/TinyCore.SpecHelper.js', 13 | 'test/jasmine/extensions/AMD/TinyCore.AMD.Spec.js' 14 | ]; 15 | }; -------------------------------------------------------------------------------- /test/karma/ext.amd.domBoot.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | var shared = require( './shared.conf' ); 3 | 4 | module.exports = function( config ) 5 | { 6 | shared( config ); 7 | 8 | config.files = [ 9 | 'build/TinyCore.js', 10 | 'test/jasmine/extensions/AMD/RequireMock.js', 11 | 'src/extensions/AMD/TinyCore.AMD.js', 12 | 'src/extensions/AMD/TinyCore.AMD.domBoot.js', 13 | 'test/jasmine/TinyCore.SpecHelper.js', 14 | 'test/jasmine/extensions/AMD/TinyCore.AMD.domBoot.Spec.js' 15 | ]; 16 | }; -------------------------------------------------------------------------------- /test/karma/ext.inheritance+instances.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | var shared = require( './shared.conf' ); 3 | 4 | module.exports = function( config ) 5 | { 6 | shared( config ); 7 | 8 | config.files = [ 9 | 'build/TinyCore.js', 10 | 'src/extensions/Inheritance/TinyCore.Module.Inheritance.js', 11 | 'src/extensions/Instances/TinyCore.Module.Instances.js', 12 | 'test/jasmine/TinyCore.SpecHelper.js', 13 | 'test/jasmine/extensions/Inheritance+Instances/TinyCore.Module.Inheritance+Instances.Spec.js' 14 | ]; 15 | }; 16 | -------------------------------------------------------------------------------- /test/karma/ext.inheritance.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | var shared = require( './shared.conf' ); 3 | 4 | module.exports = function( config ) 5 | { 6 | shared( config ); 7 | 8 | config.files = [ 9 | 'build/TinyCore.js', 10 | 'src/extensions/Inheritance/TinyCore.Module.Inheritance.js', 11 | 'test/jasmine/TinyCore.SpecHelper.js', 12 | 'test/jasmine/extensions/Inheritance/TinyCore.Module.Inheritance.Spec.js' 13 | ]; 14 | }; -------------------------------------------------------------------------------- /test/karma/ext.instances.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | var shared = require( './shared.conf' ); 3 | 4 | module.exports = function( config ) 5 | { 6 | shared( config ); 7 | 8 | config.files = [ 9 | 'build/TinyCore.js', 10 | 'src/extensions/Instances/TinyCore.Module.Instances.js', 11 | 'test/jasmine/TinyCore.SpecHelper.js', 12 | 'test/jasmine/extensions/Instances/TinyCore.Module.Instances.Spec.js' 13 | ]; 14 | }; -------------------------------------------------------------------------------- /test/karma/ext.jasmine.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | var shared = require( './shared.conf' ); 3 | 4 | module.exports = function( config ) 5 | { 6 | shared( config ); 7 | 8 | config.files = [ 9 | 'build/TinyCore.js', 10 | 'src/extensions/Jasmine/TinyCore.Toolbox.Jasmine.js', 11 | 'test/jasmine/TinyCore.SpecHelper.js', 12 | 'test/jasmine/extensions/Jasmine/TinyCore.Toolbox.Jasmine.Spec.js' 13 | ]; 14 | }; -------------------------------------------------------------------------------- /test/karma/shared.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | module.exports = function( config ) 3 | { 4 | config.set( 5 | { 6 | // base path, that will be used to resolve files and exclude 7 | basePath: '../..', 8 | 9 | preprocessors: 10 | { 11 | //'app/scripts/**/*.js': 'coverage' 12 | }, 13 | 14 | frameworks: [ 'jasmine' ], 15 | 16 | // list of files / patterns to load in the browser 17 | files: [], 18 | 19 | // list of files to exclude 20 | exclude: [], 21 | 22 | // test results reporter to use 23 | // possible values: dots || progress || growl 24 | reporters: [ 'progress'/*, 'growl', 'coverage'*/ ], 25 | 26 | // web server port 27 | port: 8080, 28 | 29 | // cli runner port 30 | runnerPort: 9100, 31 | 32 | // enable / disable colors in the output (reporters and logs) 33 | colors: true, 34 | 35 | // level of logging 36 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 37 | logLevel: config.LOG_INFO, 38 | 39 | // enable / disable watching file and executing tests whenever any file changes 40 | autoWatch: false, 41 | 42 | // Start these browsers, currently available: 43 | // - Chrome 44 | // - ChromeCanary 45 | // - Firefox 46 | // - Opera 47 | // - Safari (only Mac) 48 | // - PhantomJS 49 | // - IE (only Windows) 50 | browsers: [ 'Chrome', 'Firefox', 'Safari', 'Opera' ], 51 | 52 | // If browser does not capture in given timeout [ms], kill it 53 | captureTimeout: 60000, 54 | 55 | // report which specs are slower than 500ms 56 | // CLI --report-slower-than 500 57 | reportSlowerThan: 500, 58 | 59 | // Continuous Integration mode 60 | // if true, it capture browsers, run tests and exit 61 | singleRun: true/*, 62 | 63 | coverageReporter: 64 | { 65 | type: 'html', 66 | dir: 'coverage/' 67 | }*/ 68 | 69 | } ); 70 | }; 71 | -------------------------------------------------------------------------------- /test/karma/tools.mediator.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | var shared = require( './shared.conf' ); 3 | 4 | module.exports = function( config ) 5 | { 6 | shared( config ); 7 | 8 | config.files = [ 9 | 'build/TinyCore.js', 10 | 'src/tools/mediator/TinyCore.Toolbox.Mediator.js', 11 | 'test/jasmine/TinyCore.SpecHelper.js', 12 | 'test/jasmine/tools/mediator/TinyCore.Toolbox.Mediator.Spec.js' 13 | ]; 14 | }; --------------------------------------------------------------------------------