rocket_cms 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +2 -0
- data/.travis.yml +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +36 -0
- data/Rakefile +1 -0
- data/app/assets/javascripts/head.load.js +707 -0
- data/app/assets/javascripts/jquery.placeholder.js +157 -0
- data/app/assets/javascripts/rocket_cms.js.coffee +10 -0
- data/app/assets/javascripts/rocket_cms/flash.js.coffee +3 -0
- data/app/assets/javascripts/rocket_cms/map.js.coffee +22 -0
- data/app/assets/stylesheets/rocket_cms.css.sass +2 -0
- data/app/assets/stylesheets/rocket_cms/flash.css.sass +66 -0
- data/app/assets/stylesheets/rocket_cms/normalize.css.scss +406 -0
- data/app/controllers/concerns/no_cache.rb +12 -0
- data/app/controllers/concerns/rs_errors.rb +58 -0
- data/app/controllers/concerns/rs_menu.rb +45 -0
- data/app/controllers/concerns/rs_pages.rb +41 -0
- data/app/controllers/contacts_controller.rb +29 -0
- data/app/controllers/news_controller.rb +22 -0
- data/app/controllers/pages_controller.rb +12 -0
- data/app/controllers/search_controller.rb +25 -0
- data/app/mailers/contact_mailer.rb +15 -0
- data/app/models/ckeditor/asset.rb +5 -0
- data/app/models/ckeditor/attachment_file.rb +15 -0
- data/app/models/ckeditor/picture.rb +16 -0
- data/app/models/concerns/boolean_field.rb +9 -0
- data/app/models/concerns/enableable.rb +8 -0
- data/app/models/concerns/geocodeable.rb +4 -0
- data/app/models/concerns/manual_slug.rb +38 -0
- data/app/models/concerns/mappable.rb +77 -0
- data/app/models/concerns/seoable.rb +35 -0
- data/app/models/concerns/sort_field.rb +12 -0
- data/app/models/concerns/sortable.rb +8 -0
- data/app/models/concerns/trackable.rb +8 -0
- data/app/models/contact_message.rb +6 -0
- data/app/models/menu.rb +6 -0
- data/app/models/news.rb +5 -0
- data/app/models/page.rb +6 -0
- data/app/views/contact_mailer/new_message_email.html.haml +15 -0
- data/app/views/contacts/new.html.haml +10 -0
- data/app/views/contacts/sent.html.haml +4 -0
- data/app/views/errors/_base.html.haml +3 -0
- data/app/views/errors/error_403.html.haml +1 -0
- data/app/views/errors/error_404.html.haml +1 -0
- data/app/views/errors/error_500.html.haml +1 -0
- data/app/views/news/index.html.haml +8 -0
- data/app/views/news/show.html.haml +8 -0
- data/app/views/pages/show.html.haml +1 -0
- data/app/views/rails_admin/main/_check_boxes.html.haml +27 -0
- data/app/views/rails_admin/main/_form_raw.html.haml +1 -0
- data/app/views/search/index.html.haml +19 -0
- data/app/views/shared/_admin_link.html.haml +3 -0
- data/app/views/shared/_messages.html.haml +7 -0
- data/app/views/shared/_meta.html.haml +6 -0
- data/app/views/shared/_obj.html.haml +14 -0
- data/app/views/shared/_og.html.haml +4 -0
- data/config/locales/en.rocket_admin.yml +6 -0
- data/config/locales/en.rs.yml +17 -0
- data/config/locales/ru.cancan.yml +4 -0
- data/config/locales/ru.devise.yml +65 -0
- data/config/locales/ru.kaminari.yml +17 -0
- data/config/locales/ru.models.yml +78 -0
- data/config/locales/ru.mongoid.yml +450 -0
- data/config/locales/ru.rails_admin.yml +147 -0
- data/config/locales/ru.rocket_admin.yml +6 -0
- data/config/locales/ru.rs.yml +17 -0
- data/config/locales/ru.simple_captcha.yml +3 -0
- data/config/locales/ru.simple_form.yml +9 -0
- data/lib/filename_to_slug.rb +32 -0
- data/lib/generators/rocket_cms/admin_generator.rb +20 -0
- data/lib/generators/rocket_cms/templates/ability.erb +17 -0
- data/lib/generators/rocket_cms/templates/admin.erb +71 -0
- data/lib/generators/rocket_cms/utils.rb +22 -0
- data/lib/history_tracker.rb +4 -0
- data/lib/rails_admin/custom_show_in_app.rb +39 -0
- data/lib/rocket_cms.rb +57 -0
- data/lib/rocket_cms/admin.rb +128 -0
- data/lib/rocket_cms/configuration.rb +54 -0
- data/lib/rocket_cms/controller.rb +24 -0
- data/lib/rocket_cms/elastic_search.rb +32 -0
- data/lib/rocket_cms/engine.rb +4 -0
- data/lib/rocket_cms/model.rb +16 -0
- data/lib/rocket_cms/models/contact_message.rb +37 -0
- data/lib/rocket_cms/models/menu.rb +16 -0
- data/lib/rocket_cms/models/news.rb +61 -0
- data/lib/rocket_cms/models/page.rb +86 -0
- data/lib/rocket_cms/patch.rb +58 -0
- data/lib/rocket_cms/rails_admin_menu.rb +137 -0
- data/lib/rocket_cms/railtie.rb +39 -0
- data/lib/rocket_cms/tasks.rb +14 -0
- data/lib/rocket_cms/version.rb +3 -0
- data/lib/smart_excerpt.rb +98 -0
- data/rocket_cms.gemspec +50 -0
- 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
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rocket_cms
|
data/.ruby-version
ADDED
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
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));
|