├── .gitignore ├── History.md ├── LICENSE ├── README.md ├── gadicohen:inject-initial └── package.js ├── lib ├── inject-client.js ├── inject-core.js └── inject-server.js ├── package.js ├── smart.json └── test ├── inject-core.js ├── inject-helpers.js ├── inject-internal-api.js ├── inject-public-api.js └── injected-public-api.js /.gitignore: -------------------------------------------------------------------------------- 1 | .build* 2 | .npm 3 | 4 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | # vNEXT 2 | 3 | # v1.0.4 4 | 5 | * Change injection strategy to work with both Meteor 1.3 and earlier. 6 | (fixes #17, #18) 7 | 8 | # v1.0.3 9 | 10 | * Fix broken `Injected.meta()`, thanks @JProgrammer! (#16) 11 | 12 | # v1.0.2 13 | 14 | * No code change. Deprecate gadicohen:inject-initial for meteorhacks:inject-initial 15 | 16 | # v1.0.1 17 | 18 | * Escape use of $ in string.replace in all internal functions (fixes #11) 19 | 20 | # v1.0.0 21 | 22 | * Re-release in Meteor 0.9.0 package system, with better semver 23 | * Note, package will be renamed to meteorhacks:inject-initial once MDG 24 | finishes up with rename and redirect support. 25 | 26 | # v0.0.19 27 | 28 | * Critical work (fixes and tests) from arunoda. Thank you! 29 | 30 | # v0.0.9 31 | 32 | * Fixed incomplete code for manipulating rawBodies (thanks arunoda) 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2014 Arunoda Susiripala 4 | and Gadi Cohen 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | 'Software'), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # inject-initial 2 | 3 | API to modify the initial HTML sent by Meteor to the client. An abstraction 4 | of the approach first used by Arunoda Susiripala in [fast-render](https://atmosphere.meteor.com/package/fast-render). 5 | 6 | **Preview release** but is already being used in a number of projects. 7 | 8 | Released under the MIT license (see the [LICENSE](LICENSE) file). 9 | 10 | ## How, when, why, etc? 11 | 12 | This script is targetted at advanced smart package developers. Sometimes we 13 | want to pass data with the original HTML request to make the site available 14 | quicker. Note, if you're just wanting to accelerate data that is made 15 | available via publish/subscribe functions, use [fast-render](https://atmosphere.meteor.com/package/fast-render). 16 | 17 | Put `api.use('inject-initial', ['client', 'server']);` in `package.js`. 18 | The injected data is (in the higher level methods) inserted at the top 19 | of the HEAD element, and BEFORE any other scripts are called. Thus, 20 | you can access this data when your script is loaded, without needing 21 | to wait for anything or use any callbacks (like a return from a 22 | `Meteor.call()` to get initial data). 23 | 24 | ## API (from highest to lowest level) 25 | 26 | Common arguments: 27 | 28 | * **id**: Each method expects a unique `id`. This is used both to give you 29 | useful output in error handling, and to allow you to override/replace functions 30 | in time. E.g. instead of having a function executed on every connect that 31 | retrieves data, simply call the same method with the same id every time the 32 | data changes (using `observeChanges` on a server-only collection, or whatever). 33 | 34 | * **data**: The data parameter can either be a value, or a function. If 35 | a function, it will be called when serving the page to generate appropriate 36 | data. It doesn't make sense to use a function in conjunction with a `res` 37 | argument (see below), but we won't stop you for now. 38 | 39 | * **res** is the optional final argument. If ommitted, 40 | the data will be injected on every HTTP request. If present, the data will 41 | be specific for (and used only on) that http connection resource (set it 42 | up with a regular connectHandler). 43 | 44 | Methods: 45 | 46 | * `Inject.obj(id, objOrFunc, [res])` on server, accessible via 47 | `Injected.obj(id)` on the client. Obj 48 | of course may contain data only, and no references (to functions, other objects, 49 | etc). Stored in EJSON in a 50 | `<script id="id" type="application/ejson"><` 51 | tag in the HEAD; this allows it to work even if 52 | `browserPolicy.content.disallowInlineScripts()` has been called. Note that `id` 53 | must be unique in the entire DOM! e.g. 54 | 55 | ```js 56 | if (Meteor.isServer) 57 | Inject.obj('myData', myData); 58 | 59 | // always available immediately 60 | if (Meteor.isClient) 61 | var myData = Injected.obj('myData'); 62 | ``` 63 | 64 | * `Inject.meta(id, textOrFunc, [res])`, accessible via `Injected.meta(id)` 65 | on client. This is plain text that will be stored in a META tag in the HEAD. 66 | 67 | * `Inject.rawHead(id, textOrFunc, [res])`. `text` will be inserted in the HTML HEAD. 68 | 69 | * `Inject.rawBody(id, textOrFunc, [res])`. `text` will be inserted in the HTML BODY. 70 | 71 | * `Inject.rawModHtml(id, func)`. At injection time, calls `func(html, res)` with 72 | the full page HTML which it expects to be returned, in full, after modification. 73 | `res` is the current http connection response request. 74 | e.g. 75 | 76 | ```js 77 | Inject.rawModHtml('doSomething', function(html) { 78 | return html.replace(/blah/, 'something'); 79 | }); 80 | ``` 81 | 82 | * `Inject.appUrl(url)`. A copy of Meteor's internal appUrl() method to see 83 | if a resource is should be served the initial HTML page. 84 | 85 | **Example of a "per-request" handler:** 86 | 87 | ```js 88 | if (Meteor.isServer) { 89 | if (!Package.appcache) 90 | WebApp.connectHandlers.use(function(req, res, next) { 91 | if(Inject.appUrl(req.url)) { 92 | Inject.obj('myData', makeDataFor(req), res); 93 | } 94 | next(); 95 | }); 96 | } 97 | 98 | if (Meteor.isClient) { 99 | // available immediately 100 | var myData = Injected.obj('myData'); 101 | } 102 | ``` 103 | 104 | ## App Cache 105 | 106 | In general, you probably don't want to pass any information if appcache 107 | is enabled, since you'll never be able to refresh it until you update 108 | your app again and force a HCP. However, we'll leave these in your hands 109 | to decide... maybe that behvaviour is ok. But consider including 110 | appcache as a weak dependency, checking for `Package.appcache`, and 111 | avoiding injecting when present (instead use whatever method you used 112 | previously to pass data to the client). 113 | 114 | ## Security 115 | 116 | This script is very useful for passing general info on the initial request, 117 | and fast-render is very useful for passing authenticated publication info, 118 | with the additional security checks in place. 119 | 120 | To that end, if you are attempting to use this script to pass priviledged 121 | information to the client, be aware of the kinds of issues pointed out 122 | by Emily Stark 123 | [here](https://groups.google.com/d/msg/meteor-talk/1Fg4rNk9JZM/ELX3672QsrEJ) 124 | and take the necessary precautions in the callbacks you pass to 125 | inject-initial. 126 | 127 | ## Roadmap 128 | 129 | * Should we have functions like `isInjectable()` to check if appcache is 130 | loaded, and what else? 131 | 132 | * Should we have an Inject.script(), which is inserted inline if possible 133 | (i.e. if appcache disabled and disallowInlineScripts not set), otherwise, 134 | automatically calls (as a 1st script in the head) another page served by 135 | the package, which includes the script (great for appcache but slows down 136 | page load otherwise?). 137 | -------------------------------------------------------------------------------- /gadicohen:inject-initial/package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | summary: "Deprecated. Use meteorhacks:inject-initial instead.", 3 | version: "1.0.2", 4 | git: "https://github.com/meteorhacks/meteor-inject-initial.git", 5 | name: "gadicohen:inject-initial" 6 | }); 7 | 8 | Package.on_use(function (api) { 9 | api.use('meteorhacks:inject-initial@1.0.2'); 10 | api.imply('meteorhacks:inject-initial'); 11 | 12 | // XXX COMPAT WITH PACKAGES BUILT FOR 0.9.0. 13 | // 14 | // (in particular, packages that have a weak dependency on this 15 | // package, since then exported symbols live on the 16 | // `Package[gadicohen:inject-initial]` object) 17 | api.export('Inject', 'server'); 18 | api.export(['Injected', 'Inject'], 'client'); 19 | }); 20 | -------------------------------------------------------------------------------- /lib/inject-client.js: -------------------------------------------------------------------------------- 1 | Injected = { 2 | 3 | obj: function(name) { 4 | var json = document.getElementById(name); 5 | // Apparently .text doesn't work on some IE's. 6 | return json ? EJSON.parse(json.innerHTML) : undefined; 7 | }, 8 | 9 | meta: function(name) { 10 | return this.metas[name]; 11 | }, 12 | 13 | /* internal methods */ 14 | 15 | parseMetas: function() { 16 | var metaEls = document.getElementsByTagName('meta'); 17 | for (var i=0; i < metaEls.length; i++) 18 | this.metas[ metaEls[i].getAttribute('id') ] 19 | = metaEls[i].getAttribute('content'); 20 | }, 21 | metas: {} 22 | } 23 | 24 | Injected.parseMetas(); 25 | 26 | // deprecated 27 | Inject = { 28 | getObj: Injected.obj, 29 | getMeta: Injected.meta 30 | } 31 | -------------------------------------------------------------------------------- /lib/inject-core.js: -------------------------------------------------------------------------------- 1 | // Hijack core node API and attach data to the response dynamically 2 | // We are simply using this hack because, there is no way to alter 3 | // Meteor's html content on the server side 4 | 5 | Inject._hijackWrite = function(res) { 6 | var originalWrite = res.write; 7 | res.write = function(chunk, encoding) { 8 | //prevent hijacking other http requests 9 | if(!res.iInjected && 10 | encoding === undefined && /^/.test(chunk)) { 11 | chunk = chunk.toString(); 12 | 13 | for (id in Inject.rawModHtmlFuncs) { 14 | chunk = Inject.rawModHtmlFuncs[id](chunk, res); 15 | if (!_.isString(chunk)) { 16 | throw new Error('Inject func id "' + id + '" must return HTML, not ' 17 | + typeof(chunk) + '\n' + JSON.stringify(chunk, null, 2)); 18 | } 19 | } 20 | 21 | res.iInjected = true; 22 | } 23 | 24 | originalWrite.call(res, chunk, encoding); 25 | }; 26 | } 27 | 28 | WebApp.connectHandlers.use(function(req, res, next) { 29 | // We only separate this to make testing easier 30 | Inject._hijackWrite(res); 31 | 32 | next(); 33 | }); 34 | 35 | //meteor algorithm to check if this is a meteor serving http request or not 36 | Inject.appUrl = function(url) { 37 | if (url === '/favicon.ico' || url === '/robots.txt') 38 | return false; 39 | 40 | // NOTE: app.manifest is not a web standard like favicon.ico and 41 | // robots.txt. It is a file id we have chosen to use for HTML5 42 | // appcache URLs. It is included here to prevent using an appcache 43 | // then removing it from poisoning an app permanently. Eventually, 44 | // once we have server side routing, this won't be needed as 45 | // unknown URLs with return a 404 automatically. 46 | if (url === '/app.manifest') 47 | return false; 48 | 49 | // Avoid serving app HTML for declared routes such as /sockjs/. 50 | if (typeof(RoutePolicy) != 'undefined' && RoutePolicy.classify(url)) 51 | return false; 52 | 53 | // we currently return app HTML on all URLs by default 54 | return true; 55 | }; 56 | -------------------------------------------------------------------------------- /lib/inject-server.js: -------------------------------------------------------------------------------- 1 | function escapeReplaceString(str) { 2 | /* 3 | * When using string.replace(str, newSubStr), the dollar sign ("$") is 4 | * considered a special character in newSubStr, and needs to be escaped 5 | * as "$$". We have to do this twice, for escaping the newSubStr in 6 | * this function, and for the resulting string which is passed back. 7 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace 8 | */ 9 | return str.replace(/\$/g, '$$$$'); 10 | } 11 | 12 | Inject = { 13 | // stores in a script type=application/ejson tag, accessed with Injected.obj('id') 14 | obj: function(id, data, res) { 15 | this._checkForObjOrFunction(data, 16 | 'Inject.obj(id, data [,res]) expects `data` to be an Object or Function'); 17 | 18 | if (res) { 19 | this._resAssign(res, 'objList', id, data); 20 | } else { 21 | this.objList[id] = data; 22 | } 23 | }, 24 | objList: {}, 25 | 26 | // Inserts a META called `id`, whose `content` can be accessed with Injected.meta() 27 | meta: function(id, data, res) { 28 | this._checkForTextOrFunction(data, 29 | 'Inject.meta(id, data [,res]) expects `data` to be an String or Function'); 30 | 31 | if (res) { 32 | this._resAssign(res, 'metaList', id, data); 33 | } else { 34 | this.metaList[id] = data; 35 | } 36 | }, 37 | metaList: {}, 38 | 39 | rawHead: function(id, textOrFunc, res) { 40 | this._checkForTextOrFunction(textOrFunc, 41 | 'Inject.rawHead(id, content [,res]) expects `content` to be an String or Function'); 42 | 43 | if (res) { 44 | this._resAssign(res, 'rawHeads', id, textOrFunc); 45 | } else { 46 | this.rawHeads[id] = textOrFunc; 47 | } 48 | }, 49 | rawHeads: {}, 50 | 51 | rawBody: function(id, textOrFunc, res) { 52 | this._checkForTextOrFunction(textOrFunc, 53 | 'Inject.rawBody(id, content [,res]) expects `content` to be an String or Function'); 54 | 55 | if (res) { 56 | this._resAssign(res, 'rawBodies', id, textOrFunc); 57 | } else { 58 | this.rawBodies[id] = textOrFunc; 59 | } 60 | }, 61 | rawBodies: {}, 62 | 63 | // The callback receives the entire HTML page and must return a modified version 64 | rawModHtml: function(id, func) { 65 | if (!_.isFunction(func)) { 66 | var message = 'Inject func id "' + id + '" should be a function, not ' + typeof(func); 67 | throw new Error(message); 68 | } 69 | 70 | this.rawModHtmlFuncs[id] = func; 71 | }, 72 | rawModHtmlFuncs: {}, 73 | 74 | _injectObjects: function(html, res) { 75 | var objs = _.extend({}, Inject.objList, res.Inject && res.Inject.objList); 76 | if (_.isEmpty(objs)) { 77 | return html; 78 | } 79 | 80 | var obj, injectHtml = ''; 81 | for (id in objs) { 82 | obj = _.isFunction(objs[id]) ? objs[id](res) : objs[id]; 83 | injectHtml += " \n"; 86 | } 87 | 88 | return html.replace('', '\n' + escapeReplaceString(injectHtml)); 89 | }, 90 | 91 | _injectMeta: function(html, res) { 92 | var metas = _.extend({}, Inject.metaList, res.Inject && res.Inject.metaList); 93 | if (_.isEmpty(metas)) 94 | return html; 95 | 96 | var injectHtml = ''; 97 | for (id in metas) { 98 | var meta = this._evalToText(metas[id], res, html); 99 | injectHtml += " \n", res; 101 | } 102 | 103 | return html.replace('', '\n' + escapeReplaceString(injectHtml)); 104 | }, 105 | 106 | _injectHeads: function(html, res) { 107 | var heads = _.extend({}, Inject.rawHeads, res.Inject && res.Inject.rawHeads); 108 | if (_.isEmpty(heads)) 109 | return html; 110 | 111 | var injectHtml = ''; 112 | for (id in heads) { 113 | var head = this._evalToText(heads[id], res, html); 114 | injectHtml += head + '\n'; 115 | } 116 | 117 | return html.replace('', '\n' + escapeReplaceString(injectHtml)); 118 | }, 119 | 120 | _injectBodies: function(html, res) { 121 | var bodies = _.extend({}, Inject.rawBodies, res.Inject && res.Inject.rawBodies); 122 | if (_.isEmpty(bodies)) 123 | return html; 124 | 125 | var injectHtml = ''; 126 | for (id in bodies) { 127 | var body = this._evalToText(bodies[id], res, html); 128 | injectHtml += body + '\n'; 129 | } 130 | 131 | return html.replace('', '\n' + escapeReplaceString(injectHtml)); 132 | }, 133 | 134 | // ensure object exists and store there 135 | _resAssign: function(res, key, id, value) { 136 | if (!res.Inject) 137 | res.Inject = {}; 138 | if (!res.Inject[key]) 139 | res.Inject[key] = {}; 140 | res.Inject[key][id] = value; 141 | }, 142 | 143 | _checkForTextOrFunction: function (arg, message) { 144 | if(!(_.isString(arg) || _.isFunction(arg))) { 145 | throw new Error(message); 146 | } 147 | }, 148 | 149 | _checkForObjOrFunction: function (arg, message) { 150 | if(!(_.isObject(arg) || _.isFunction(arg))) { 151 | throw new Error(message); 152 | } 153 | }, 154 | 155 | // we don't handle errors here. Let them to handle in a higher level 156 | _evalToText: function(textOrFunc, res, html) { 157 | if(_.isFunction(textOrFunc)) { 158 | return textOrFunc(res, html); 159 | } else { 160 | return textOrFunc; 161 | } 162 | } 163 | }; 164 | 165 | Inject.rawModHtml('injectHeads', Inject._injectHeads.bind(Inject)); 166 | Inject.rawModHtml('injectMeta', Inject._injectMeta.bind(Inject)); 167 | Inject.rawModHtml('injectBodies', Inject._injectBodies.bind(Inject)); 168 | Inject.rawModHtml('injectObjects', Inject._injectObjects.bind(Inject)); 169 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | summary: "Allow injection of arbitrary data to initial Meteor HTML page", 3 | version: "1.0.5", 4 | git: "https://github.com/meteorhacks/meteor-inject-initial.git", 5 | name: "meteorhacks:inject-initial" 6 | }); 7 | 8 | Npm.depends({ 9 | "connect": "2.12.0" 10 | }); 11 | 12 | Package.onUse(function (api) { 13 | configurePackage(api); 14 | api.export('Inject', 'server'); 15 | api.export(['Injected', 'Inject'], 'client'); 16 | }); 17 | 18 | Package.onTest(function(api) { 19 | configurePackage(api); 20 | api.use('tinytest', ['client', 'server']); 21 | 22 | api.addFiles('test/inject-helpers.js', 'server'); 23 | api.addFiles('test/inject-public-api.js', 'server'); 24 | api.addFiles('test/inject-internal-api.js', 'server'); 25 | api.addFiles('test/inject-core.js', 'server'); 26 | 27 | api.addFiles('test/injected-public-api.js', ['server', 'client']); 28 | }); 29 | 30 | function configurePackage(api) { 31 | if(api.versionsFrom) { 32 | api.versionsFrom('METEOR@1.0'); 33 | } 34 | 35 | api.use(['routepolicy', 'webapp'], 'server'); 36 | api.use(['ejson', 'underscore'], ['client','server']); 37 | 38 | api.addFiles('lib/inject-server.js', 'server'); 39 | api.addFiles('lib/inject-core.js', 'server'); 40 | api.addFiles('lib/inject-client.js', 'client'); 41 | } 42 | -------------------------------------------------------------------------------- /smart.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inject-initial", 3 | "description": "Allow injection of arbitrary data to initial Meteor HTML page", 4 | "homepage": "https://github.com/gadicohen/meteor-inject-initial", 5 | "author": "Gadi Cohen , adapting code by Arunoda", 6 | "version": "0.0.10", 7 | "git": "https://github.com/gadicc/meteor-inject-initial.git", 8 | "packages": {} 9 | } 10 | -------------------------------------------------------------------------------- /test/inject-core.js: -------------------------------------------------------------------------------- 1 | // This test assumes Inject._hijackWrite is called inside of a 2 | // a WebApp.connectHandlers.use function (it is). 3 | 4 | Tinytest.add( 5 | 'Inject Core - injecting', 6 | function (test, done) { 7 | var context = { 8 | _header: {}, 9 | _hadBody: true 10 | }; 11 | 12 | context.write = function(html) { 13 | test.equal(html, "ABC"); 14 | }; 15 | 16 | var htmlText = "---" 17 | 18 | Inject.rawModHtmlFuncs = { 19 | "id1": function(html, res) { 20 | test.isTrue(res, context); 21 | test.equal(html, htmlText); 22 | return html.replace('---', 'ABC'); 23 | } 24 | } 25 | 26 | Inject._hijackWrite(context); 27 | context.write(htmlText); 28 | 29 | test.equal(context.iInjected, true); 30 | Inject.rawModHtmlFuncs = {}; 31 | } 32 | ); 33 | 34 | 35 | /* 36 | 37 | We no longer override http.OutgoingMessage.prototype.write; instead 38 | we hijack res.write in connect middleware. 39 | 40 | Tinytest.add( 41 | 'Inject Core - injecting', 42 | function (test, done) { 43 | var context = { 44 | _header: {}, 45 | _hadBody: true 46 | }; 47 | var http = Npm.require('http'); 48 | var originalWrite = http.OutgoingMessage.prototype.write; 49 | 50 | var htmlText = "---" 51 | 52 | Inject.rawModHtmlFuncs = { 53 | "id1": function(html, res) { 54 | test.isTrue(res, context); 55 | test.equal(html, htmlText); 56 | return html.replace('---', 'ABC'); 57 | } 58 | } 59 | 60 | originalWrite.call(context, htmlText); 61 | test.equal(context.iInjected, true); 62 | Inject.rawModHtmlFuncs = {}; 63 | } 64 | ); 65 | 66 | */ -------------------------------------------------------------------------------- /test/inject-helpers.js: -------------------------------------------------------------------------------- 1 | Tinytest.add( 2 | 'Inject Helpers - _evalToText - function', 3 | function (test) { 4 | var func = function(res, html) { 5 | return res + html; 6 | }; 7 | 8 | var result = Inject._evalToText(func, 'res', 'html'); 9 | test.equal(result, 'reshtml'); 10 | } 11 | ); 12 | 13 | Tinytest.add( 14 | 'Inject Helpers - _evalToText - text', 15 | function (test) { 16 | var result = Inject._evalToText('reshtml'); 17 | test.equal(result, 'reshtml'); 18 | } 19 | ); 20 | 21 | Tinytest.add( 22 | 'Inject Helpers - _checkForTextOrFunction - function', 23 | function (test) { 24 | var result = Inject._checkForTextOrFunction(function() {}); 25 | test.ok(); 26 | } 27 | ); 28 | 29 | Tinytest.add( 30 | 'Inject Helpers - _checkForTextOrFunction - text', 31 | function (test) { 32 | var result = Inject._checkForTextOrFunction('some-text'); 33 | test.ok(); 34 | } 35 | ); 36 | 37 | Tinytest.add( 38 | 'Inject Helpers - _checkForTextOrFunction - other', 39 | function (test) { 40 | try { 41 | var result = Inject._checkForTextOrFunction({}, 'message'); 42 | test.fail(); 43 | } catch(ex) { 44 | test.equal(ex.message, 'message'); 45 | } 46 | } 47 | ); 48 | -------------------------------------------------------------------------------- /test/inject-internal-api.js: -------------------------------------------------------------------------------- 1 | Tinytest.add( 2 | 'Inject Internal Apis - _injectHeads', 3 | function (test) { 4 | function func() {}; 5 | Inject.rawHeads = { 6 | "id1": "one", 7 | "id2": "two$$" 8 | }; 9 | 10 | var res = { 11 | Inject: { 12 | rawHeads: { 13 | "id3": "three", 14 | "id4": "four$$" 15 | } 16 | } 17 | }; 18 | 19 | var newHtml = Inject._injectHeads("", res); 20 | test.equal(newHtml, "\none\ntwo$$\nthree\nfour$$\n") 21 | } 22 | ); 23 | 24 | Tinytest.add( 25 | 'Inject Internal Apis - _injectBodies', 26 | function (test) { 27 | function func() {}; 28 | Inject.rawBodies = { 29 | "id1": "one", 30 | "id2": "two$$" 31 | }; 32 | 33 | var res = { 34 | Inject: { 35 | rawBodies: { 36 | "id3": "three", 37 | "id4": "four$$" 38 | } 39 | } 40 | }; 41 | 42 | var newHtml = Inject._injectBodies("", res); 43 | test.equal(newHtml, "\none\ntwo$$\nthree\nfour$$\n") 44 | } 45 | ); 46 | 47 | Tinytest.add( 48 | 'Inject Internal Apis - _injectObjects', 49 | function (test) { 50 | function func() {}; 51 | Inject.objList = { 52 | "id1": { value: 1 }, 53 | "id2": { value: "two$$" } 54 | }; 55 | 56 | var res = { 57 | Inject: { 58 | objList: { 59 | "id3": { value: "three$$" }, 60 | "id4": { value: 4 } 61 | } 62 | } 63 | }; 64 | 65 | var newHtml = Inject._injectObjects("", res); 66 | test.equal(newHtml, "\n" 67 | + " \n" 68 | + " \n" 69 | + " \n" 70 | + " \n" 71 | + ""); 72 | } 73 | ); 74 | 75 | Tinytest.add( 76 | 'Inject Internal Apis - _injectMeta', 77 | function (test) { 78 | function func() {}; 79 | Inject.metaList = { 80 | "id1": "1", 81 | "id2": "two$$" 82 | }; 83 | 84 | var res = { 85 | Inject: { 86 | metaList: { 87 | "id3": "three", 88 | "id4": "four$$" 89 | } 90 | } 91 | }; 92 | 93 | var newHtml = Inject._injectMeta("", res); 94 | test.equal(newHtml, "\n" 95 | + " \n" 96 | + " \n" 97 | + " \n" 98 | + " \n" 99 | + ""); 100 | } 101 | ); 102 | -------------------------------------------------------------------------------- /test/inject-public-api.js: -------------------------------------------------------------------------------- 1 | Tinytest.add( 2 | 'Inject Public Apis - rawModHtml - text', 3 | function (test) { 4 | function func() {}; 5 | Inject.rawModHtml('id', func); 6 | test.isTrue(func == Inject.rawModHtmlFuncs['id']); 7 | } 8 | ); 9 | 10 | Tinytest.add( 11 | 'Inject Public Apis - obj - without res', 12 | function (test) { 13 | var aa = {hello: 10}; 14 | Inject.obj('id1', aa); 15 | test.equal(aa, Inject.objList['id1']); 16 | } 17 | ); 18 | 19 | Tinytest.add( 20 | 'Inject Public Apis - obj - with res', 21 | function (test) { 22 | var aa = {hello: 10}; 23 | var res = {}; 24 | Inject.obj('id1', aa, res); 25 | test.equal(aa, res.Inject.objList['id1']); 26 | } 27 | ); 28 | 29 | Tinytest.add( 30 | 'Inject Public Apis - meta - without res', 31 | function (test) { 32 | var aa = 'text'; 33 | Inject.meta('id1', aa); 34 | test.equal(aa, Inject.metaList['id1']); 35 | } 36 | ); 37 | 38 | Tinytest.add( 39 | 'Inject Public Apis - meta - with res', 40 | function (test) { 41 | var aa = 'text'; 42 | var res = {}; 43 | Inject.meta('id1', aa, res); 44 | test.equal(aa, res.Inject.metaList['id1']); 45 | } 46 | ); 47 | 48 | Tinytest.add( 49 | 'Inject Public Apis - rawHead - without res', 50 | function (test) { 51 | var aa = 'text'; 52 | Inject.rawHead('id1', aa); 53 | test.equal(aa, Inject.rawHeads['id1']); 54 | } 55 | ); 56 | 57 | Tinytest.add( 58 | 'Inject Public Apis - rawHead - with res', 59 | function (test) { 60 | var aa = 'text'; 61 | var res = {}; 62 | Inject.rawHead('id1', aa, res); 63 | test.equal(aa, res.Inject.rawHeads['id1']); 64 | } 65 | ); 66 | 67 | Tinytest.add( 68 | 'Inject Public Apis - rawBody - without res', 69 | function (test) { 70 | var aa = 'text'; 71 | Inject.rawBody('id1', aa); 72 | test.equal(aa, Inject.rawBodies['id1']); 73 | } 74 | ); 75 | 76 | Tinytest.add( 77 | 'Inject Public Apis - rawBody - with res', 78 | function (test) { 79 | var aa = 'text'; 80 | var res = {}; 81 | Inject.rawBody('id1', aa, res); 82 | test.equal(aa, res.Inject.rawBodies['id1']); 83 | } 84 | ); 85 | -------------------------------------------------------------------------------- /test/injected-public-api.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Tests for client side functions 3 | */ 4 | 5 | if (Meteor.isServer) { 6 | 7 | Inject.obj('injected-obj', { value: 'injected-obj-data'} ); 8 | Inject.meta('injected-meta', 'injected-meta-data'); 9 | } 10 | 11 | if (Meteor.isClient) { 12 | 13 | Tinytest.add( 14 | 'Inject Public Apis - obj - Injected.obj (client-side)', 15 | function (test) { 16 | test.equal(Injected.obj('injected-obj'), { value: 'injected-obj-data' }); 17 | } 18 | ); 19 | 20 | Tinytest.add( 21 | 'Inject Public Apis - meta - Injected.meta (client-side)', 22 | function (test) { 23 | test.equal(Injected.meta('injected-meta'), 'injected-meta-data'); 24 | } 25 | ); 26 | 27 | } 28 | 29 | --------------------------------------------------------------------------------