rocket_cms 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +2 -0
  6. data/.travis.yml +19 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +36 -0
  10. data/Rakefile +1 -0
  11. data/app/assets/javascripts/head.load.js +707 -0
  12. data/app/assets/javascripts/jquery.placeholder.js +157 -0
  13. data/app/assets/javascripts/rocket_cms.js.coffee +10 -0
  14. data/app/assets/javascripts/rocket_cms/flash.js.coffee +3 -0
  15. data/app/assets/javascripts/rocket_cms/map.js.coffee +22 -0
  16. data/app/assets/stylesheets/rocket_cms.css.sass +2 -0
  17. data/app/assets/stylesheets/rocket_cms/flash.css.sass +66 -0
  18. data/app/assets/stylesheets/rocket_cms/normalize.css.scss +406 -0
  19. data/app/controllers/concerns/no_cache.rb +12 -0
  20. data/app/controllers/concerns/rs_errors.rb +58 -0
  21. data/app/controllers/concerns/rs_menu.rb +45 -0
  22. data/app/controllers/concerns/rs_pages.rb +41 -0
  23. data/app/controllers/contacts_controller.rb +29 -0
  24. data/app/controllers/news_controller.rb +22 -0
  25. data/app/controllers/pages_controller.rb +12 -0
  26. data/app/controllers/search_controller.rb +25 -0
  27. data/app/mailers/contact_mailer.rb +15 -0
  28. data/app/models/ckeditor/asset.rb +5 -0
  29. data/app/models/ckeditor/attachment_file.rb +15 -0
  30. data/app/models/ckeditor/picture.rb +16 -0
  31. data/app/models/concerns/boolean_field.rb +9 -0
  32. data/app/models/concerns/enableable.rb +8 -0
  33. data/app/models/concerns/geocodeable.rb +4 -0
  34. data/app/models/concerns/manual_slug.rb +38 -0
  35. data/app/models/concerns/mappable.rb +77 -0
  36. data/app/models/concerns/seoable.rb +35 -0
  37. data/app/models/concerns/sort_field.rb +12 -0
  38. data/app/models/concerns/sortable.rb +8 -0
  39. data/app/models/concerns/trackable.rb +8 -0
  40. data/app/models/contact_message.rb +6 -0
  41. data/app/models/menu.rb +6 -0
  42. data/app/models/news.rb +5 -0
  43. data/app/models/page.rb +6 -0
  44. data/app/views/contact_mailer/new_message_email.html.haml +15 -0
  45. data/app/views/contacts/new.html.haml +10 -0
  46. data/app/views/contacts/sent.html.haml +4 -0
  47. data/app/views/errors/_base.html.haml +3 -0
  48. data/app/views/errors/error_403.html.haml +1 -0
  49. data/app/views/errors/error_404.html.haml +1 -0
  50. data/app/views/errors/error_500.html.haml +1 -0
  51. data/app/views/news/index.html.haml +8 -0
  52. data/app/views/news/show.html.haml +8 -0
  53. data/app/views/pages/show.html.haml +1 -0
  54. data/app/views/rails_admin/main/_check_boxes.html.haml +27 -0
  55. data/app/views/rails_admin/main/_form_raw.html.haml +1 -0
  56. data/app/views/search/index.html.haml +19 -0
  57. data/app/views/shared/_admin_link.html.haml +3 -0
  58. data/app/views/shared/_messages.html.haml +7 -0
  59. data/app/views/shared/_meta.html.haml +6 -0
  60. data/app/views/shared/_obj.html.haml +14 -0
  61. data/app/views/shared/_og.html.haml +4 -0
  62. data/config/locales/en.rocket_admin.yml +6 -0
  63. data/config/locales/en.rs.yml +17 -0
  64. data/config/locales/ru.cancan.yml +4 -0
  65. data/config/locales/ru.devise.yml +65 -0
  66. data/config/locales/ru.kaminari.yml +17 -0
  67. data/config/locales/ru.models.yml +78 -0
  68. data/config/locales/ru.mongoid.yml +450 -0
  69. data/config/locales/ru.rails_admin.yml +147 -0
  70. data/config/locales/ru.rocket_admin.yml +6 -0
  71. data/config/locales/ru.rs.yml +17 -0
  72. data/config/locales/ru.simple_captcha.yml +3 -0
  73. data/config/locales/ru.simple_form.yml +9 -0
  74. data/lib/filename_to_slug.rb +32 -0
  75. data/lib/generators/rocket_cms/admin_generator.rb +20 -0
  76. data/lib/generators/rocket_cms/templates/ability.erb +17 -0
  77. data/lib/generators/rocket_cms/templates/admin.erb +71 -0
  78. data/lib/generators/rocket_cms/utils.rb +22 -0
  79. data/lib/history_tracker.rb +4 -0
  80. data/lib/rails_admin/custom_show_in_app.rb +39 -0
  81. data/lib/rocket_cms.rb +57 -0
  82. data/lib/rocket_cms/admin.rb +128 -0
  83. data/lib/rocket_cms/configuration.rb +54 -0
  84. data/lib/rocket_cms/controller.rb +24 -0
  85. data/lib/rocket_cms/elastic_search.rb +32 -0
  86. data/lib/rocket_cms/engine.rb +4 -0
  87. data/lib/rocket_cms/model.rb +16 -0
  88. data/lib/rocket_cms/models/contact_message.rb +37 -0
  89. data/lib/rocket_cms/models/menu.rb +16 -0
  90. data/lib/rocket_cms/models/news.rb +61 -0
  91. data/lib/rocket_cms/models/page.rb +86 -0
  92. data/lib/rocket_cms/patch.rb +58 -0
  93. data/lib/rocket_cms/rails_admin_menu.rb +137 -0
  94. data/lib/rocket_cms/railtie.rb +39 -0
  95. data/lib/rocket_cms/tasks.rb +14 -0
  96. data/lib/rocket_cms/version.rb +3 -0
  97. data/lib/smart_excerpt.rb +98 -0
  98. data/rocket_cms.gemspec +50 -0
  99. metadata +533 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ed1a64eb57baf03872734690527a98a0e18941ad
4
+ data.tar.gz: 86cf1d01b615448a3614335bbd3f082cc252191b
5
+ SHA512:
6
+ metadata.gz: f488b1b964d7aec6568244a537d1c77d07ac86846e5d8e82ba56838390c10da9597c7c27ebab134b781f1161e84c98390c57d0434bfcf0b9b67fef64b85da246
7
+ data.tar.gz: 3bcc95ae984e863f9564a45a0132c43ac4d815a6f09624e52778ab9d489be73ba4210c6d34e7a9bf54a5567bb10806ccdf5a66f155f8a0d9ea4ef76070c0c247
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ rocket_cms
data/.ruby-version ADDED
@@ -0,0 +1,2 @@
1
+ 2.1.2
2
+
data/.travis.yml ADDED
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+ services: mongodb
3
+
4
+ notifications:
5
+ email: false
6
+
7
+ rvm:
8
+ - 1.9.3
9
+ - 2.0.0
10
+ - 2.1.0
11
+ - jruby-20mode
12
+
13
+ env:
14
+ - "UPLOADS=paperclip"
15
+ - "UPLOADS=carrierwave"
16
+
17
+ gemfile:
18
+ - gemfiles/mongoid-3.1.gemfile
19
+ - gemfiles/mongoid-4.0.gemfile
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rocket_cms.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 glebtv
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # RocketCMS
2
+
3
+ rails_admin + mongoid + elasticsearch CMS
4
+
5
+ Very opinionated and tuned for our needs.
6
+
7
+ Sorry, no documentation or examples are availiable yet. Stay tuned
8
+
9
+ **Before 1.0 API and class names should be considered unstable and can change at
10
+ any time!**
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'rocket_cms'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install rocket_cms
25
+
26
+ ## Usage
27
+
28
+ TODO: Write usage instructions here
29
+
30
+ ## Contributing
31
+
32
+ 1. Fork it
33
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
34
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
35
+ 4. Push to the branch (`git push origin my-new-feature`)
36
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,707 @@
1
+ ///#source 1 1 /src/1.0.0/load.js
2
+ /*! head.load - v1.0.3 */
3
+ /*
4
+ * HeadJS The only script in your <HEAD>
5
+ * Author Tero Piirainen (tipiirai)
6
+ * Maintainer Robert Hoffmann (itechnology)
7
+ * License MIT / http://bit.ly/mit-license
8
+ * WebSite http://headjs.com
9
+ */
10
+ (function (win, undefined) {
11
+ "use strict";
12
+
13
+ //#region variables
14
+ var doc = win.document,
15
+ domWaiters = [],
16
+ handlers = {}, // user functions waiting for events
17
+ assets = {}, // loadable items in various states
18
+ isAsync = "async" in doc.createElement("script") || "MozAppearance" in doc.documentElement.style || win.opera,
19
+ isDomReady,
20
+
21
+ /*** public API ***/
22
+ headVar = win.head_conf && win.head_conf.head || "head",
23
+ api = win[headVar] = (win[headVar] || function () { api.ready.apply(null, arguments); }),
24
+
25
+ // states
26
+ PRELOADING = 1,
27
+ PRELOADED = 2,
28
+ LOADING = 3,
29
+ LOADED = 4;
30
+ //#endregion
31
+
32
+ //#region PRIVATE functions
33
+
34
+ //#region Helper functions
35
+ function noop() {
36
+ // does nothing
37
+ }
38
+
39
+ function each(arr, callback) {
40
+ if (!arr) {
41
+ return;
42
+ }
43
+
44
+ // arguments special type
45
+ if (typeof arr === "object") {
46
+ arr = [].slice.call(arr);
47
+ }
48
+
49
+ // do the job
50
+ for (var i = 0, l = arr.length; i < l; i++) {
51
+ callback.call(arr, arr[i], i);
52
+ }
53
+ }
54
+
55
+ /* A must read: http://bonsaiden.github.com/JavaScript-Garden
56
+ ************************************************************/
57
+ function is(type, obj) {
58
+ var clas = Object.prototype.toString.call(obj).slice(8, -1);
59
+ return obj !== undefined && obj !== null && clas === type;
60
+ }
61
+
62
+ function isFunction(item) {
63
+ return is("Function", item);
64
+ }
65
+
66
+ function isArray(item) {
67
+ return is("Array", item);
68
+ }
69
+
70
+ function toLabel(url) {
71
+ ///<summary>Converts a url to a file label</summary>
72
+ var items = url.split("/"),
73
+ name = items[items.length - 1],
74
+ i = name.indexOf("?");
75
+
76
+ return i !== -1 ? name.substring(0, i) : name;
77
+ }
78
+
79
+ // INFO: this look like a "im triggering callbacks all over the place, but only wanna run it one time function" ..should try to make everything work without it if possible
80
+ // INFO: Even better. Look into promises/defered's like jQuery is doing
81
+ function one(callback) {
82
+ ///<summary>Execute a callback only once</summary>
83
+ callback = callback || noop;
84
+
85
+ if (callback._done) {
86
+ return;
87
+ }
88
+
89
+ callback();
90
+ callback._done = 1;
91
+ }
92
+ //#endregion
93
+
94
+ function conditional(test, success, failure, callback) {
95
+ ///<summary>
96
+ /// INFO: use cases:
97
+ /// head.test(condition, null , "file.NOk" , callback);
98
+ /// head.test(condition, "fileOk.js", null , callback);
99
+ /// head.test(condition, "fileOk.js", "file.NOk" , callback);
100
+ /// head.test(condition, "fileOk.js", ["file.NOk", "file.NOk"], callback);
101
+ /// head.test({
102
+ /// test : condition,
103
+ /// success : [{ label1: "file1Ok.js" }, { label2: "file2Ok.js" }],
104
+ /// failure : [{ label1: "file1NOk.js" }, { label2: "file2NOk.js" }],
105
+ /// callback: callback
106
+ /// );
107
+ /// head.test({
108
+ /// test : condition,
109
+ /// success : ["file1Ok.js" , "file2Ok.js"],
110
+ /// failure : ["file1NOk.js", "file2NOk.js"],
111
+ /// callback: callback
112
+ /// );
113
+ ///</summary>
114
+ var obj = (typeof test === "object") ? test : {
115
+ test: test,
116
+ success: !!success ? isArray(success) ? success : [success] : false,
117
+ failure: !!failure ? isArray(failure) ? failure : [failure] : false,
118
+ callback: callback || noop
119
+ };
120
+
121
+ // Test Passed ?
122
+ var passed = !!obj.test;
123
+
124
+ // Do we have a success case
125
+ if (passed && !!obj.success) {
126
+ obj.success.push(obj.callback);
127
+ api.load.apply(null, obj.success);
128
+ }
129
+ // Do we have a fail case
130
+ else if (!passed && !!obj.failure) {
131
+ obj.failure.push(obj.callback);
132
+ api.load.apply(null, obj.failure);
133
+ }
134
+ else {
135
+ callback();
136
+ }
137
+
138
+ return api;
139
+ }
140
+
141
+ function getAsset(item) {
142
+ ///<summary>
143
+ /// Assets are in the form of
144
+ /// {
145
+ /// name : label,
146
+ /// url : url,
147
+ /// state: state
148
+ /// }
149
+ ///</summary>
150
+ var asset = {};
151
+
152
+ if (typeof item === "object") {
153
+ for (var label in item) {
154
+ if (!!item[label]) {
155
+ asset = {
156
+ name: label,
157
+ url : item[label]
158
+ };
159
+ }
160
+ }
161
+ }
162
+ else {
163
+ asset = {
164
+ name: toLabel(item),
165
+ url : item
166
+ };
167
+ }
168
+
169
+ // is the item already existant
170
+ var existing = assets[asset.name];
171
+ if (existing && existing.url === asset.url) {
172
+ return existing;
173
+ }
174
+
175
+ assets[asset.name] = asset;
176
+ return asset;
177
+ }
178
+
179
+ function allLoaded(items) {
180
+ items = items || assets;
181
+
182
+ for (var name in items) {
183
+ if (items.hasOwnProperty(name) && items[name].state !== LOADED) {
184
+ return false;
185
+ }
186
+ }
187
+
188
+ return true;
189
+ }
190
+
191
+ function onPreload(asset) {
192
+ asset.state = PRELOADED;
193
+
194
+ each(asset.onpreload, function (afterPreload) {
195
+ afterPreload.call();
196
+ });
197
+ }
198
+
199
+ function preLoad(asset, callback) {
200
+ if (asset.state === undefined) {
201
+
202
+ asset.state = PRELOADING;
203
+ asset.onpreload = [];
204
+
205
+ loadAsset({ url: asset.url, type: "cache" }, function () {
206
+ onPreload(asset);
207
+ });
208
+ }
209
+ }
210
+
211
+ function apiLoadHack() {
212
+ /// <summary>preload with text/cache hack
213
+ ///
214
+ /// head.load("http://domain.com/file.js","http://domain.com/file.js", callBack)
215
+ /// head.load(["http://domain.com/file.js","http://domain.com/file.js"], callBack)
216
+ /// head.load({ label1: "http://domain.com/file.js" }, { label2: "http://domain.com/file.js" }, callBack)
217
+ /// head.load([{ label1: "http://domain.com/file.js" }, { label2: "http://domain.com/file.js" }], callBack)
218
+ /// </summary>
219
+ var args = arguments,
220
+ callback = args[args.length - 1],
221
+ rest = [].slice.call(args, 1),
222
+ next = rest[0];
223
+
224
+ if (!isFunction(callback)) {
225
+ callback = null;
226
+ }
227
+
228
+ // if array, repush as args
229
+ if (isArray(args[0])) {
230
+ args[0].push(callback);
231
+ api.load.apply(null, args[0]);
232
+
233
+ return api;
234
+ }
235
+
236
+ // multiple arguments
237
+ if (!!next) {
238
+ /* Preload with text/cache hack (not good!)
239
+ * http://blog.getify.com/on-script-loaders/
240
+ * http://www.nczonline.net/blog/2010/12/21/thoughts-on-script-loaders/
241
+ * If caching is not configured correctly on the server, then items could load twice !
242
+ *************************************************************************************/
243
+ each(rest, function (item) {
244
+ // item is not a callback or empty string
245
+ if (!isFunction(item) && !!item) {
246
+ preLoad(getAsset(item));
247
+ }
248
+ });
249
+
250
+ // execute
251
+ load(getAsset(args[0]), isFunction(next) ? next : function () {
252
+ api.load.apply(null, rest);
253
+ });
254
+ }
255
+ else {
256
+ // single item
257
+ load(getAsset(args[0]));
258
+ }
259
+
260
+ return api;
261
+ }
262
+
263
+ function apiLoadAsync() {
264
+ ///<summary>
265
+ /// simply load and let browser take care of ordering
266
+ ///
267
+ /// head.load("http://domain.com/file.js","http://domain.com/file.js", callBack)
268
+ /// head.load(["http://domain.com/file.js","http://domain.com/file.js"], callBack)
269
+ /// head.load({ label1: "http://domain.com/file.js" }, { label2: "http://domain.com/file.js" }, callBack)
270
+ /// head.load([{ label1: "http://domain.com/file.js" }, { label2: "http://domain.com/file.js" }], callBack)
271
+ ///</summary>
272
+ var args = arguments,
273
+ callback = args[args.length - 1],
274
+ items = {};
275
+
276
+ if (!isFunction(callback)) {
277
+ callback = null;
278
+ }
279
+
280
+ // if array, repush as args
281
+ if (isArray(args[0])) {
282
+ args[0].push(callback);
283
+ api.load.apply(null, args[0]);
284
+
285
+ return api;
286
+ }
287
+
288
+ // JRH 262#issuecomment-26288601
289
+ // First populate the items array.
290
+ // When allLoaded is called, all items will be populated.
291
+ // Issue when lazy loaded, the callback can execute early.
292
+ each(args, function (item, i) {
293
+ if (item !== callback) {
294
+ item = getAsset(item);
295
+ items[item.name] = item;
296
+ }
297
+ });
298
+
299
+ each(args, function (item, i) {
300
+ if (item !== callback) {
301
+ item = getAsset(item);
302
+
303
+ load(item, function () {
304
+ if (allLoaded(items)) {
305
+ one(callback);
306
+ }
307
+ });
308
+ }
309
+ });
310
+
311
+ return api;
312
+ }
313
+
314
+ function load(asset, callback) {
315
+ ///<summary>Used with normal loading logic</summary>
316
+ callback = callback || noop;
317
+
318
+ if (asset.state === LOADED) {
319
+ callback();
320
+ return;
321
+ }
322
+
323
+ // INFO: why would we trigger a ready event when its not really loaded yet ?
324
+ if (asset.state === LOADING) {
325
+ api.ready(asset.name, callback);
326
+ return;
327
+ }
328
+
329
+ if (asset.state === PRELOADING) {
330
+ asset.onpreload.push(function () {
331
+ load(asset, callback);
332
+ });
333
+ return;
334
+ }
335
+
336
+ asset.state = LOADING;
337
+
338
+ loadAsset(asset, function () {
339
+ asset.state = LOADED;
340
+
341
+ callback();
342
+
343
+ // handlers for this asset
344
+ each(handlers[asset.name], function (fn) {
345
+ one(fn);
346
+ });
347
+
348
+ // dom is ready & no assets are queued for loading
349
+ // INFO: shouldn't we be doing the same test above ?
350
+ if (isDomReady && allLoaded()) {
351
+ each(handlers.ALL, function (fn) {
352
+ one(fn);
353
+ });
354
+ }
355
+ });
356
+ }
357
+
358
+ function getExtension(url) {
359
+ url = url || "";
360
+
361
+ var items = url.split("?")[0].split(".");
362
+ return items[items.length-1].toLowerCase();
363
+ }
364
+
365
+ /* Parts inspired from: https://github.com/cujojs/curl
366
+ ******************************************************/
367
+ function loadAsset(asset, callback) {
368
+ callback = callback || noop;
369
+
370
+ function error(event) {
371
+ event = event || win.event;
372
+
373
+ // release event listeners
374
+ ele.onload = ele.onreadystatechange = ele.onerror = null;
375
+
376
+ // do callback
377
+ callback();
378
+
379
+ // need some more detailed error handling here
380
+ }
381
+
382
+ function process(event) {
383
+ event = event || win.event;
384
+
385
+ // IE 7/8 (2 events on 1st load)
386
+ // 1) event.type = readystatechange, s.readyState = loading
387
+ // 2) event.type = readystatechange, s.readyState = loaded
388
+
389
+ // IE 7/8 (1 event on reload)
390
+ // 1) event.type = readystatechange, s.readyState = complete
391
+
392
+ // event.type === 'readystatechange' && /loaded|complete/.test(s.readyState)
393
+
394
+ // IE 9 (3 events on 1st load)
395
+ // 1) event.type = readystatechange, s.readyState = loading
396
+ // 2) event.type = readystatechange, s.readyState = loaded
397
+ // 3) event.type = load , s.readyState = loaded
398
+
399
+ // IE 9 (2 events on reload)
400
+ // 1) event.type = readystatechange, s.readyState = complete
401
+ // 2) event.type = load , s.readyState = complete
402
+
403
+ // event.type === 'load' && /loaded|complete/.test(s.readyState)
404
+ // event.type === 'readystatechange' && /loaded|complete/.test(s.readyState)
405
+
406
+ // IE 10 (3 events on 1st load)
407
+ // 1) event.type = readystatechange, s.readyState = loading
408
+ // 2) event.type = load , s.readyState = complete
409
+ // 3) event.type = readystatechange, s.readyState = loaded
410
+
411
+ // IE 10 (3 events on reload)
412
+ // 1) event.type = readystatechange, s.readyState = loaded
413
+ // 2) event.type = load , s.readyState = complete
414
+ // 3) event.type = readystatechange, s.readyState = complete
415
+
416
+ // event.type === 'load' && /loaded|complete/.test(s.readyState)
417
+ // event.type === 'readystatechange' && /complete/.test(s.readyState)
418
+
419
+ // Other Browsers (1 event on 1st load)
420
+ // 1) event.type = load, s.readyState = undefined
421
+
422
+ // Other Browsers (1 event on reload)
423
+ // 1) event.type = load, s.readyState = undefined
424
+
425
+ // event.type == 'load' && s.readyState = undefined
426
+
427
+ // !doc.documentMode is for IE6/7, IE8+ have documentMode
428
+ if (event.type === "load" || (/loaded|complete/.test(ele.readyState) && (!doc.documentMode || doc.documentMode < 9))) {
429
+ // remove timeouts
430
+ win.clearTimeout(asset.errorTimeout);
431
+ win.clearTimeout(asset.cssTimeout);
432
+
433
+ // release event listeners
434
+ ele.onload = ele.onreadystatechange = ele.onerror = null;
435
+
436
+ // do callback
437
+ callback();
438
+ }
439
+ }
440
+
441
+ function isCssLoaded() {
442
+ // should we test again ? 20 retries = 5secs ..after that, the callback will be triggered by the error handler at 7secs
443
+ if (asset.state !== LOADED && asset.cssRetries <= 20) {
444
+
445
+ // loop through stylesheets
446
+ for (var i = 0, l = doc.styleSheets.length; i < l; i++) {
447
+ // do we have a match ?
448
+ // we need to tests agains ele.href and not asset.url, because a local file will be assigned the full http path on a link element
449
+ if (doc.styleSheets[i].href === ele.href) {
450
+ process({ "type": "load" });
451
+ return;
452
+ }
453
+ }
454
+
455
+ // increment & try again
456
+ asset.cssRetries++;
457
+ asset.cssTimeout = win.setTimeout(isCssLoaded, 250);
458
+ }
459
+ }
460
+
461
+ var ele;
462
+ var ext = getExtension(asset.url);
463
+
464
+ if (ext === "css") {
465
+ ele = doc.createElement("link");
466
+ ele.type = "text/" + (asset.type || "css");
467
+ ele.rel = "stylesheet";
468
+ ele.href = asset.url;
469
+
470
+ /* onload supported for CSS on unsupported browsers
471
+ * Safari windows 5.1.7, FF < 10
472
+ */
473
+
474
+ // Set counter to zero
475
+ asset.cssRetries = 0;
476
+ asset.cssTimeout = win.setTimeout(isCssLoaded, 500);
477
+ }
478
+ else {
479
+ ele = doc.createElement("script");
480
+ ele.type = "text/" + (asset.type || "javascript");
481
+ ele.src = asset.url;
482
+ }
483
+
484
+ ele.onload = ele.onreadystatechange = process;
485
+ ele.onerror = error;
486
+
487
+ /* Good read, but doesn't give much hope !
488
+ * http://blog.getify.com/on-script-loaders/
489
+ * http://www.nczonline.net/blog/2010/12/21/thoughts-on-script-loaders/
490
+ * https://hacks.mozilla.org/2009/06/defer/
491
+ */
492
+
493
+ // ASYNC: load in parallel and execute as soon as possible
494
+ ele.async = false;
495
+ // DEFER: load in parallel but maintain execution order
496
+ ele.defer = false;
497
+
498
+ // timout for asset loading
499
+ asset.errorTimeout = win.setTimeout(function () {
500
+ error({ type: "timeout" });
501
+ }, 7e3);
502
+
503
+ // use insertBefore to keep IE from throwing Operation Aborted (thx Bryan Forbes!)
504
+ var head = doc.head || doc.getElementsByTagName("head")[0];
505
+
506
+ // but insert at end of head, because otherwise if it is a stylesheet, it will not override values
507
+ head.insertBefore(ele, head.lastChild);
508
+ }
509
+
510
+ /* Parts inspired from: https://github.com/jrburke/requirejs
511
+ ************************************************************/
512
+ function init() {
513
+ var items = doc.getElementsByTagName("script");
514
+
515
+ // look for a script with a data-head-init attribute
516
+ for (var i = 0, l = items.length; i < l; i++) {
517
+ var dataMain = items[i].getAttribute("data-headjs-load");
518
+ if (!!dataMain) {
519
+ api.load(dataMain);
520
+ return;
521
+ }
522
+ }
523
+ }
524
+
525
+ function ready(key, callback) {
526
+ ///<summary>
527
+ /// INFO: use cases:
528
+ /// head.ready(callBack);
529
+ /// head.ready(document , callBack);
530
+ /// head.ready("file.js", callBack);
531
+ /// head.ready("label" , callBack);
532
+ /// head.ready(["label1", "label2"], callback);
533
+ ///</summary>
534
+
535
+ // DOM ready check: head.ready(document, function() { });
536
+ if (key === doc) {
537
+ if (isDomReady) {
538
+ one(callback);
539
+ }
540
+ else {
541
+ domWaiters.push(callback);
542
+ }
543
+
544
+ return api;
545
+ }
546
+
547
+ // shift arguments
548
+ if (isFunction(key)) {
549
+ callback = key;
550
+ key = "ALL"; // holds all callbacks that where added without labels: ready(callBack)
551
+ }
552
+
553
+ // queue all items from key and return. The callback will be executed if all items from key are already loaded.
554
+ if (isArray(key)) {
555
+ var items = {};
556
+
557
+ each(key, function (item) {
558
+ items[item] = assets[item];
559
+
560
+ api.ready(item, function() {
561
+ if (allLoaded(items)) {
562
+ one(callback);
563
+ }
564
+ });
565
+ });
566
+
567
+ return api;
568
+ }
569
+
570
+ // make sure arguments are sane
571
+ if (typeof key !== "string" || !isFunction(callback)) {
572
+ return api;
573
+ }
574
+
575
+ // this can also be called when we trigger events based on filenames & labels
576
+ var asset = assets[key];
577
+
578
+ // item already loaded --> execute and return
579
+ if (asset && asset.state === LOADED || key === "ALL" && allLoaded() && isDomReady) {
580
+ one(callback);
581
+ return api;
582
+ }
583
+
584
+ var arr = handlers[key];
585
+ if (!arr) {
586
+ arr = handlers[key] = [callback];
587
+ }
588
+ else {
589
+ arr.push(callback);
590
+ }
591
+
592
+ return api;
593
+ }
594
+
595
+ /* Mix of stuff from jQuery & IEContentLoaded
596
+ * http://dev.w3.org/html5/spec/the-end.html#the-end
597
+ ***************************************************/
598
+ function domReady() {
599
+ // Make sure body exists, at least, in case IE gets a little overzealous (jQuery ticket #5443).
600
+ if (!doc.body) {
601
+ // let's not get nasty by setting a timeout too small.. (loop mania guaranteed if assets are queued)
602
+ win.clearTimeout(api.readyTimeout);
603
+ api.readyTimeout = win.setTimeout(domReady, 50);
604
+ return;
605
+ }
606
+
607
+ if (!isDomReady) {
608
+ isDomReady = true;
609
+
610
+ init();
611
+ each(domWaiters, function (fn) {
612
+ one(fn);
613
+ });
614
+ }
615
+ }
616
+
617
+ function domContentLoaded() {
618
+ // W3C
619
+ if (doc.addEventListener) {
620
+ doc.removeEventListener("DOMContentLoaded", domContentLoaded, false);
621
+ domReady();
622
+ }
623
+
624
+ // IE
625
+ else if (doc.readyState === "complete") {
626
+ // we're here because readyState === "complete" in oldIE
627
+ // which is good enough for us to call the dom ready!
628
+ doc.detachEvent("onreadystatechange", domContentLoaded);
629
+ domReady();
630
+ }
631
+ }
632
+
633
+ // Catch cases where ready() is called after the browser event has already occurred.
634
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
635
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
636
+ if (doc.readyState === "complete") {
637
+ domReady();
638
+ }
639
+
640
+ // W3C
641
+ else if (doc.addEventListener) {
642
+ doc.addEventListener("DOMContentLoaded", domContentLoaded, false);
643
+
644
+ // A fallback to window.onload, that will always work
645
+ win.addEventListener("load", domReady, false);
646
+ }
647
+
648
+ // IE
649
+ else {
650
+ // Ensure firing before onload, maybe late but safe also for iframes
651
+ doc.attachEvent("onreadystatechange", domContentLoaded);
652
+
653
+ // A fallback to window.onload, that will always work
654
+ win.attachEvent("onload", domReady);
655
+
656
+ // If IE and not a frame
657
+ // continually check to see if the document is ready
658
+ var top = false;
659
+
660
+ try {
661
+ top = !win.frameElement && doc.documentElement;
662
+ } catch (e) { }
663
+
664
+ if (top && top.doScroll) {
665
+ (function doScrollCheck() {
666
+ if (!isDomReady) {
667
+ try {
668
+ // Use the trick by Diego Perini
669
+ // http://javascript.nwbox.com/IEContentLoaded/
670
+ top.doScroll("left");
671
+ } catch (error) {
672
+ // let's not get nasty by setting a timeout too small.. (loop mania guaranteed if assets are queued)
673
+ win.clearTimeout(api.readyTimeout);
674
+ api.readyTimeout = win.setTimeout(doScrollCheck, 50);
675
+ return;
676
+ }
677
+
678
+ // and execute any waiting functions
679
+ domReady();
680
+ }
681
+ }());
682
+ }
683
+ }
684
+ //#endregion
685
+
686
+ //#region Public Exports
687
+ // INFO: determine which method to use for loading
688
+ api.load = api.js = isAsync ? apiLoadAsync : apiLoadHack;
689
+ api.test = conditional;
690
+ api.ready = ready;
691
+ //#endregion
692
+
693
+ //#region INIT
694
+ // perform this when DOM is ready
695
+ api.ready(doc, function () {
696
+ if (allLoaded()) {
697
+ each(handlers.ALL, function (callback) {
698
+ one(callback);
699
+ });
700
+ }
701
+
702
+ if (api.feature) {
703
+ api.feature("domloaded", true);
704
+ }
705
+ });
706
+ //#endregion
707
+ }(window));