admin_it 1.2.6 → 1.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +3 -0
- data/app/controllers/admin_it/signed_url_controller.rb +3 -2
- data/client/.bowerrc +3 -0
- data/client/Gemfile +3 -0
- data/client/bower.json +27 -0
- data/client/index.html +16 -0
- data/client/index.html.slim +22 -0
- data/client/lib/nestedtypes.js +332 -0
- data/client/metadata.json +15 -0
- data/client/src/js/app.js +188 -0
- data/client/src/js/collections/resources.js +14 -0
- data/client/src/js/icons.js +28 -0
- data/client/src/js/main.js +21 -0
- data/client/src/js/models/base.js +12 -0
- data/client/src/js/models/metadata.js +16 -0
- data/client/src/js/models/resource.js +12 -0
- data/client/src/js/template.js +102 -0
- data/client/src/js/views/base.js +16 -0
- data/client/src/js/views/main.js +34 -0
- data/client/src/js/views/menu.js +51 -0
- data/client/src/templates/bootstrap/main_menu.html +1 -0
- data/client/src/templates/bootstrap/main_menu.html.slim +5 -0
- data/client/src/templates/bootstrap/main_view.html +1 -0
- data/client/src/templates/bootstrap/main_view.html.slim +4 -0
- data/client/src/templates/bootstrap.html +1 -0
- data/client/src/templates/bootstrap.html.slim +3 -0
- data/client/tasks/compile.thor +51 -0
- data/config/routes.rb +1 -1
- data/lib/admin_it/version.rb +1 -1
- metadata +27 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c090fcc7e63225ac6601c85ac9deb4ef9a1f277
|
4
|
+
data.tar.gz: a9e2fe30ceb1589d9cdd7e2f791ce3f076549e6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c2a43fc1579cf58eebdc0acbeb7e29bb6d2cc34475cd4e2bcf81a71f63177d7611a0865f40cbae032d3e656ab9c79b5dcd53d3d4ecb1945bb0e426c9b2b85c0
|
7
|
+
data.tar.gz: 747cbf4c44754ac86562595b339b0a02ad57f97b15e90394c0832b6b696114afc8662f873e544d44023f54b0955342c2b085e5c8a30f5aebfa8689229e573c71
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -18,7 +18,7 @@ module AdminIt
|
|
18
18
|
def s3_upload_policy_document
|
19
19
|
Base64.encode64(
|
20
20
|
{
|
21
|
-
expiration:
|
21
|
+
expiration: 12.hours.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
|
22
22
|
conditions: [
|
23
23
|
{ bucket: AdminIt.config.s3[:bucket] },
|
24
24
|
{ acl: 'public-read' },
|
@@ -31,9 +31,10 @@ module AdminIt
|
|
31
31
|
|
32
32
|
# sign our request by Base64 encoding the policy document.
|
33
33
|
def s3_upload_signature
|
34
|
+
puts AdminIt.config.s3
|
34
35
|
Base64.encode64(
|
35
36
|
OpenSSL::HMAC.digest(
|
36
|
-
OpenSSL::Digest
|
37
|
+
OpenSSL::Digest.new('sha1'),
|
37
38
|
AdminIt.config.s3[:secret_access_key],
|
38
39
|
s3_upload_policy_document
|
39
40
|
)
|
data/client/.bowerrc
ADDED
data/client/Gemfile
ADDED
data/client/bower.json
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"name": "admin-it-client",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"homepage": "https://github.com/cybernetlab/admin_it",
|
5
|
+
"authors": [
|
6
|
+
"Alexey Ovchinnikov <alexiss@cybernetlab.ru>"
|
7
|
+
],
|
8
|
+
"main": "src/js/main.js",
|
9
|
+
"private": true,
|
10
|
+
"ignore": [
|
11
|
+
"components",
|
12
|
+
"dev",
|
13
|
+
"test",
|
14
|
+
"tests",
|
15
|
+
"**/*.txt"
|
16
|
+
],
|
17
|
+
"dependencies": {
|
18
|
+
"requirejs": "latest",
|
19
|
+
"backbone": "latest",
|
20
|
+
"underscore": "latest"
|
21
|
+
},
|
22
|
+
"devDependencies": {
|
23
|
+
"mustache": "latest",
|
24
|
+
"bootstrap": "latest",
|
25
|
+
"fontawesome": "latest"
|
26
|
+
}
|
27
|
+
}
|
data/client/index.html
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
<!DOCTYPE html><html><head><title>AdminIt Client Test App</title><link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet" /><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script><script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script><link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet" /><script data-main="src/js/main" src="components/requirejs/require.js"></script><script data-name="metadata" data-type="data" type="application/json">{
|
2
|
+
"resources": [
|
3
|
+
{
|
4
|
+
"id": "users",
|
5
|
+
"name": "Users",
|
6
|
+
"icon": "users"
|
7
|
+
},
|
8
|
+
|
9
|
+
{
|
10
|
+
"id": "roles",
|
11
|
+
"name": "Roles",
|
12
|
+
"icon": "lock"
|
13
|
+
}
|
14
|
+
]
|
15
|
+
}
|
16
|
+
</script><script data-name="main_menu" data-type="template" type="application/mustache"><ul class="nav nav-pills nav-stacked">{{#items}}<li class="admin-it-main-menu-item" data-id="{{id}}"><a href="#">{{#icon}}{{>icon}} {{/icon}}{{name}}</a></li>{{/items}}</ul></script><script data-name="main_view" data-type="template" type="application/mustache"><div class="container-fluid"><div class="row"><div class="admin-it-main-menu col-md-2"></div><div class="admin-it-work-area col-md-10"></div></div></div></script></head><body></body></html>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
doctype html
|
2
|
+
html
|
3
|
+
head
|
4
|
+
title AdminIt Client Test App
|
5
|
+
|
6
|
+
/ Bootstrap
|
7
|
+
link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet"
|
8
|
+
script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"
|
9
|
+
script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"
|
10
|
+
|
11
|
+
/ Font-Awesome
|
12
|
+
link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet"
|
13
|
+
|
14
|
+
/ AdminIt
|
15
|
+
script data-main="src/js/main" src="components/requirejs/require.js"
|
16
|
+
|
17
|
+
/ data
|
18
|
+
script type="application/json" data-type="data" data-name="metadata"
|
19
|
+
== include File.join(pwd, 'metadata.json')
|
20
|
+
/ bootstrap templates
|
21
|
+
== render 'bootstrap'
|
22
|
+
body
|
@@ -0,0 +1,332 @@
|
|
1
|
+
// Backbone.nestedTypes 0.2 (https://github.com/Volicon/backbone.nestedTypes)
|
2
|
+
// (c) 2014 Vlad Balin, may be freely distributed under the MIT license
|
3
|
+
|
4
|
+
( function( root, factory ){
|
5
|
+
if( typeof define === 'function' && define.amd ) {
|
6
|
+
define( [ 'exports', 'backbone', 'underscore' ], factory );
|
7
|
+
}
|
8
|
+
else if( typeof exports !== 'undefined' ){
|
9
|
+
factory( exports, require( 'backbone' ), require( 'underscore' ) );
|
10
|
+
}
|
11
|
+
else{
|
12
|
+
root.NestedTypes = {};
|
13
|
+
factory( root.NestedTypes, root.Backbone, root._ );
|
14
|
+
}
|
15
|
+
}( this, function( exports, Backbone, _ ){
|
16
|
+
'use strict';
|
17
|
+
var extend = Backbone.Model.extend;
|
18
|
+
|
19
|
+
function attachNativeProperties( This, properties, Base ){
|
20
|
+
_.each( properties, function( propDesc, name ){
|
21
|
+
var prop = typeof propDesc === 'function' ? {
|
22
|
+
get: propDesc,
|
23
|
+
enumerable: false
|
24
|
+
} : propDesc;
|
25
|
+
|
26
|
+
if( name in Base.prototype ){
|
27
|
+
throw new TypeError( 'extend: property ' + name + ' conflicts with base class members!' );
|
28
|
+
}
|
29
|
+
|
30
|
+
Object.defineProperty( This.prototype, name, prop );
|
31
|
+
});
|
32
|
+
}
|
33
|
+
|
34
|
+
function extendWithProperties( Base ){
|
35
|
+
return function( protoProps, staticProps ){
|
36
|
+
var This = extend.call( this, protoProps, staticProps );
|
37
|
+
attachNativeProperties( This, protoProps.properties, Base );
|
38
|
+
return This;
|
39
|
+
};
|
40
|
+
}
|
41
|
+
|
42
|
+
exports.Class = function(){
|
43
|
+
function Class(){
|
44
|
+
this.initialize.apply( this, arguments );
|
45
|
+
};
|
46
|
+
|
47
|
+
_.extend( Class.prototype, Backbone.Events, { initialize: function (){} } );
|
48
|
+
Class.extend = extendWithProperties( Class );
|
49
|
+
|
50
|
+
return Class;
|
51
|
+
}();
|
52
|
+
|
53
|
+
exports.Collection = function(){
|
54
|
+
var Collection,
|
55
|
+
CollectionProto = Backbone.Collection.prototype;
|
56
|
+
|
57
|
+
function wrapCall( func ){
|
58
|
+
return function(){
|
59
|
+
if( !this.__changing++ ){
|
60
|
+
this.trigger( 'before:change' );
|
61
|
+
}
|
62
|
+
|
63
|
+
func.apply( this, arguments );
|
64
|
+
|
65
|
+
if( !--this.__changing ){
|
66
|
+
this.trigger( 'after:change' );
|
67
|
+
}
|
68
|
+
};
|
69
|
+
}
|
70
|
+
|
71
|
+
Collection = Backbone.Collection.extend({
|
72
|
+
triggerWhenChanged: 'change add remove reset sort',
|
73
|
+
|
74
|
+
deepClone: function(){
|
75
|
+
var copy = CollectionProto.clone.call( this );
|
76
|
+
|
77
|
+
copy.reset( this.map( function( model ){
|
78
|
+
return model.deepClone();
|
79
|
+
} ) );
|
80
|
+
|
81
|
+
return copy;
|
82
|
+
},
|
83
|
+
|
84
|
+
__changing: 0,
|
85
|
+
set: wrapCall( CollectionProto.set ),
|
86
|
+
remove: wrapCall( CollectionProto.remove ),
|
87
|
+
add: wrapCall( CollectionProto.add ),
|
88
|
+
reset: wrapCall( CollectionProto.reset ),
|
89
|
+
sort: wrapCall( CollectionProto.sort )
|
90
|
+
});
|
91
|
+
|
92
|
+
Collection.extend = extendWithProperties( Collection );
|
93
|
+
|
94
|
+
return Collection;
|
95
|
+
}();
|
96
|
+
|
97
|
+
exports.Model = function(){
|
98
|
+
var extend = Backbone.Model.extend,
|
99
|
+
ModelProto = Backbone.Model.prototype;
|
100
|
+
|
101
|
+
function delegateEvents( name, oldValue, newValue ){
|
102
|
+
var replace = false;
|
103
|
+
|
104
|
+
if( _.isEqual( oldValue, newValue ) ){
|
105
|
+
return;
|
106
|
+
}
|
107
|
+
|
108
|
+
if( oldValue && oldValue.triggerWhenChanged ){
|
109
|
+
replace = true;
|
110
|
+
this.stopListening( oldValue );
|
111
|
+
}
|
112
|
+
|
113
|
+
if( newValue && newValue.triggerWhenChanged ){
|
114
|
+
replace = true;
|
115
|
+
|
116
|
+
this.listenTo( newValue, 'before:change', onEnter );
|
117
|
+
this.listenTo( newValue, 'after:change', onExit );
|
118
|
+
|
119
|
+
this.listenTo( newValue, newValue.triggerWhenChanged, function(){
|
120
|
+
var value = this.get( name );
|
121
|
+
|
122
|
+
if( this.__duringSet ){
|
123
|
+
this.__nestedChanges[ name ] = value;
|
124
|
+
}
|
125
|
+
else{
|
126
|
+
this.attributes[ name ] = null;
|
127
|
+
ModelProto.set.call( this, name, value );
|
128
|
+
}
|
129
|
+
} );
|
130
|
+
|
131
|
+
_.each( this.listening[ name ], function( handler, events ){
|
132
|
+
var callback = typeof handler === 'string' ? this[ handler ] : handler;
|
133
|
+
this.listenTo( newValue, events, callback );
|
134
|
+
}, this );
|
135
|
+
}
|
136
|
+
|
137
|
+
if( replace ){
|
138
|
+
this.trigger( 'replace:' + name, this, newValue, oldValue );
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
function typeCast( Ctor, name, value ){
|
143
|
+
var oldValue = this.attributes[ name ],
|
144
|
+
valueHasOtherType = ( value != null ) && !( value instanceof Ctor ),
|
145
|
+
newValue;
|
146
|
+
|
147
|
+
if( oldValue && oldValue.set && valueHasOtherType ){
|
148
|
+
oldValue.set( value );
|
149
|
+
newValue = oldValue;
|
150
|
+
}
|
151
|
+
else{
|
152
|
+
newValue = valueHasOtherType ? new Ctor( value ) : value;
|
153
|
+
delegateEvents.call( this, name, oldValue, newValue );
|
154
|
+
}
|
155
|
+
|
156
|
+
return newValue;
|
157
|
+
}
|
158
|
+
|
159
|
+
function onExit( a_attrs, options ){
|
160
|
+
var attrs = a_attrs || {};
|
161
|
+
|
162
|
+
if( !--this.__duringSet ){
|
163
|
+
_.each( this.__nestedChanges, function( value, name ){
|
164
|
+
if( !( name in attrs ) ){
|
165
|
+
attrs[ name ] = value;
|
166
|
+
}
|
167
|
+
|
168
|
+
if( attrs[ name ] === this.attributes[ name ] ){
|
169
|
+
this.attributes[ name ] = null;
|
170
|
+
}
|
171
|
+
}, this );
|
172
|
+
|
173
|
+
this.__nestedChanges = {};
|
174
|
+
|
175
|
+
ModelProto.set.call( this, attrs, options );
|
176
|
+
}
|
177
|
+
else if( a_attrs ){
|
178
|
+
ModelProto.set.call( this, a_attrs, options );
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
function onEnter(){
|
183
|
+
if( !this.__duringSet++ ){
|
184
|
+
this.__nestedChanges = {};
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
var Model = Backbone.Model.extend( {
|
189
|
+
triggerWhenChanged: 'change',
|
190
|
+
listening: {},
|
191
|
+
__duringSet: 0,
|
192
|
+
__defaults: {},
|
193
|
+
__types: {},
|
194
|
+
|
195
|
+
set: function( first, second, third ){
|
196
|
+
// handle different call options...
|
197
|
+
var attrs, options, types = this.__types;
|
198
|
+
|
199
|
+
if( typeof first === 'string' ){
|
200
|
+
( attrs = {} )[ first ] = second;
|
201
|
+
options = third;
|
202
|
+
}
|
203
|
+
else{
|
204
|
+
attrs = first;
|
205
|
+
options = second;
|
206
|
+
}
|
207
|
+
|
208
|
+
onEnter.call( this );
|
209
|
+
|
210
|
+
// cast values to default types...
|
211
|
+
_.each( attrs, function( value, name ){
|
212
|
+
var Ctor = types[ name ];
|
213
|
+
|
214
|
+
if( Ctor ){
|
215
|
+
attrs[ name ] = typeCast.call( this, Ctor, name, value );
|
216
|
+
}
|
217
|
+
}, this );
|
218
|
+
|
219
|
+
// apply changes
|
220
|
+
onExit.call( this, attrs, options );
|
221
|
+
|
222
|
+
return this;
|
223
|
+
},
|
224
|
+
|
225
|
+
// Create deep copy for all nested objects...
|
226
|
+
deepClone: function(){
|
227
|
+
var copy = ModelProto.clone.call( this );
|
228
|
+
|
229
|
+
_.each( copy.attributes, function( value, key ){
|
230
|
+
if( value && value.deepClone ){
|
231
|
+
copy.set( key, value.deepClone() );
|
232
|
+
}
|
233
|
+
} );
|
234
|
+
|
235
|
+
return copy;
|
236
|
+
},
|
237
|
+
|
238
|
+
// Support for nested models and objects.
|
239
|
+
// Apply toJSON recursively to produce correct JSON.
|
240
|
+
toJSON: function(){
|
241
|
+
var res = ModelProto.toJSON.apply( this, arguments );
|
242
|
+
|
243
|
+
_.each( res, function( value, key ){
|
244
|
+
if( value && value.toJSON ){
|
245
|
+
res[ key ] = value.toJSON();
|
246
|
+
}
|
247
|
+
} );
|
248
|
+
|
249
|
+
return res;
|
250
|
+
},
|
251
|
+
|
252
|
+
_: _ // add underscore to be accessible in templates
|
253
|
+
} );
|
254
|
+
|
255
|
+
function parseDefaults( spec, Base ){
|
256
|
+
var defaults = _.defaults( spec.defaults || {}, Base.prototype.__defaults ),
|
257
|
+
fnames = _.functions( defaults ),
|
258
|
+
values = _.omit( defaults, fnames ),
|
259
|
+
types = _.pick( defaults, fnames );
|
260
|
+
|
261
|
+
return _.extend( {}, spec, {
|
262
|
+
defaults : createDefaults( values, types ),
|
263
|
+
__defaults : defaults,
|
264
|
+
__types : types
|
265
|
+
});
|
266
|
+
}
|
267
|
+
|
268
|
+
function createDefaults( values, ctors ){
|
269
|
+
return function(){
|
270
|
+
var defaults = _.clone( values );
|
271
|
+
|
272
|
+
_.each( ctors, function( Ctor, name ){
|
273
|
+
defaults[ name ] = new Ctor();
|
274
|
+
} );
|
275
|
+
|
276
|
+
return defaults;
|
277
|
+
};
|
278
|
+
}
|
279
|
+
|
280
|
+
function createAttrPropDesc( name ){
|
281
|
+
return {
|
282
|
+
get: function(){
|
283
|
+
return this.attributes[ name ];
|
284
|
+
},
|
285
|
+
|
286
|
+
set: function( val ){
|
287
|
+
this.set( name, val );
|
288
|
+
return val;
|
289
|
+
},
|
290
|
+
|
291
|
+
enumerable: false
|
292
|
+
};
|
293
|
+
}
|
294
|
+
|
295
|
+
function attachNativeProperties( This, spec ){
|
296
|
+
var properties = {};
|
297
|
+
|
298
|
+
if( spec.properties !== false ){
|
299
|
+
_.each( spec.defaults, function( notUsed, name ){
|
300
|
+
properties[ name ] = createAttrPropDesc( name );
|
301
|
+
} );
|
302
|
+
|
303
|
+
_.each( spec.properties, function( propDesc, name ){
|
304
|
+
properties[ name ] = typeof propDesc === 'function' ? {
|
305
|
+
get: propDesc,
|
306
|
+
enumerable: false
|
307
|
+
} : propDesc;
|
308
|
+
} );
|
309
|
+
|
310
|
+
_.each( properties, function( prop, name ){
|
311
|
+
if( name in ModelProto ||
|
312
|
+
name === 'cid' || name === 'id' || name === 'attributes' ){
|
313
|
+
throw new TypeError( 'extend: attribute ' + name + ' conflicts with Backbone.Model base class members!' );
|
314
|
+
}
|
315
|
+
|
316
|
+
Object.defineProperty( This.prototype, name, prop );
|
317
|
+
} );
|
318
|
+
}
|
319
|
+
}
|
320
|
+
|
321
|
+
Model.extend = function( protoProps, staticProps ){
|
322
|
+
var spec = parseDefaults( protoProps, this ),
|
323
|
+
This = extend.call( this, spec, staticProps );
|
324
|
+
|
325
|
+
attachNativeProperties( This, protoProps );
|
326
|
+
|
327
|
+
return This;
|
328
|
+
};
|
329
|
+
|
330
|
+
return Model;
|
331
|
+
}();
|
332
|
+
}));
|
@@ -0,0 +1,188 @@
|
|
1
|
+
define(
|
2
|
+
|
3
|
+
['jquery', 'underscore', 'backbone',
|
4
|
+
'admin_it/template', 'admin_it/icons',
|
5
|
+
'admin_it/models/metadata', 'admin_it/views/main'],
|
6
|
+
|
7
|
+
// > Usefull services for debugging: http://ip.jsontest.com
|
8
|
+
|
9
|
+
function($, _, Backbone, Template, Icons, Metadata, MainView) {
|
10
|
+
'use strict';
|
11
|
+
|
12
|
+
// loads and verifies user config
|
13
|
+
var checkConfig = function(options) {
|
14
|
+
var config = _.extend({
|
15
|
+
api_url: '/api',
|
16
|
+
template_url: '',
|
17
|
+
template: _.template,
|
18
|
+
metadata: 'metadata',
|
19
|
+
icons: 'glyphicon'
|
20
|
+
}, options);
|
21
|
+
|
22
|
+
if (!_.isString(config.api_url)) throw new Error('Wrong api_url option');
|
23
|
+
config.api_url = config.api_url.replace(/\/+$/, '');
|
24
|
+
|
25
|
+
if (!_.isString(config.template_url)) throw new Error('Wrong template_url option');
|
26
|
+
config.template_url = config.template_url.replace(/\/+$/, '');
|
27
|
+
|
28
|
+
if (_.isEmpty(config.container)) config.container = $('body').first();
|
29
|
+
if (_.isString(config.container)) config.container = $(config.container).first();
|
30
|
+
return config;
|
31
|
+
}
|
32
|
+
|
33
|
+
// loads resource
|
34
|
+
var loadResource = function(app, source, target) {
|
35
|
+
// for jQuery source return inner html
|
36
|
+
if (source instanceof $) return source.html();
|
37
|
+
if (_.isString(source)) {
|
38
|
+
if (/[{}<>]/.test(source)) {
|
39
|
+
// for strings, that contains JSON or HTML symbols return string itself
|
40
|
+
return source;
|
41
|
+
} else {
|
42
|
+
// for other strings assumes that string is jQuery selector or URI
|
43
|
+
if (source.indexOf('http') != 0 && source[0] != '/') {
|
44
|
+
var selector = '[data-name="' + source + '"][data-type="';
|
45
|
+
selector += (target == app.config.template) ? 'template"]' : 'data"]';
|
46
|
+
var jquery = $(selector);
|
47
|
+
if (jquery.length > 0) return jquery.first().html();
|
48
|
+
// source is relative URI - make URI absolute
|
49
|
+
if (target == app.config.template) {
|
50
|
+
source = app.config.template_url + '/' + source;
|
51
|
+
} else {
|
52
|
+
source = app.config.api_url + '/' + source;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
if (target instanceof Backbone.Model) {
|
56
|
+
// if target is model set its urlRoot to source
|
57
|
+
if (_.isEmpty(target.urlRoot) && _.isEmpty(target.collection)) {
|
58
|
+
target.urlRoot = source;
|
59
|
+
}
|
60
|
+
return target.fetch();
|
61
|
+
} else if (target instanceof Backbone.Collection) {
|
62
|
+
// if target is collection set its url to source
|
63
|
+
if (_.isEmpty(target.url)) target.url = source;
|
64
|
+
return target.fetch();
|
65
|
+
}
|
66
|
+
return $.ajax({ url: source });
|
67
|
+
}
|
68
|
+
}
|
69
|
+
};
|
70
|
+
|
71
|
+
// creates resource after it loaded
|
72
|
+
var createResource = function(app, str, target) {
|
73
|
+
if (target instanceof Backbone.Model ||
|
74
|
+
target instanceof Backbone.Collection) {
|
75
|
+
if (_.isObject(str)) return target;
|
76
|
+
if (!_.isString(str)) return null;
|
77
|
+
str = str.replace(/[\t\n\r]/g, '');
|
78
|
+
str = $.parseJSON(str);
|
79
|
+
if (!_.isObject(str)) return null;
|
80
|
+
(target instanceof Backbone.Model) ? target.set(str) : target.reset(str);
|
81
|
+
return target;
|
82
|
+
} else if (target == app.config.template) {
|
83
|
+
return new target(str);
|
84
|
+
}
|
85
|
+
};
|
86
|
+
|
87
|
+
// loads resources from html
|
88
|
+
var loadLocals = function(deferred) {
|
89
|
+
var app = this;
|
90
|
+
$('[data-type][data-name]').each(function() {
|
91
|
+
var $this = $(this);
|
92
|
+
var data = $this.data();
|
93
|
+
if (data.type == 'template') {
|
94
|
+
app.templates[data.name] = new app.config.template($this.html());
|
95
|
+
} else if (data.type == 'data' || data.type == 'collection' || data.type == 'model') {
|
96
|
+
var Model = app.metadata.get(data.name);
|
97
|
+
if (!Model) return;
|
98
|
+
app.data[data.name] = new Model($.parseJSON($this.html));
|
99
|
+
}
|
100
|
+
});
|
101
|
+
_.each(app.config.template.defaults, function(template, name) {
|
102
|
+
if (!_.has(app.templates, name)) app.templates[name] = template;
|
103
|
+
});
|
104
|
+
deferred.resolve();
|
105
|
+
};
|
106
|
+
|
107
|
+
// creates main view
|
108
|
+
var loadMainView = function(template) {
|
109
|
+
this.mainView = new MainView({
|
110
|
+
app: this,
|
111
|
+
el: this.config.container,
|
112
|
+
template: template
|
113
|
+
});
|
114
|
+
};
|
115
|
+
|
116
|
+
var App = {
|
117
|
+
/**
|
118
|
+
* @brief starts admin application
|
119
|
+
* @details this is the main function for start admin application
|
120
|
+
*
|
121
|
+
* @param options application options
|
122
|
+
* @return jQuery deferred object that resolves when application is
|
123
|
+
* completele loaded.
|
124
|
+
*/
|
125
|
+
initialize: function(options) {
|
126
|
+
this.config = checkConfig(options);
|
127
|
+
this.icons = new Icons(this.config.icons);
|
128
|
+
this.config.template = Template(this.config.template);
|
129
|
+
this.config.template.registerHelper('iconClass', _.bind(this.icons.htmlClass, this.icons));
|
130
|
+
this.metadata = new Metadata();
|
131
|
+
this.data = {};
|
132
|
+
this.templates = {};
|
133
|
+
|
134
|
+
var app = this;
|
135
|
+
var loadingLocals = $.Deferred();
|
136
|
+
|
137
|
+
// load main view
|
138
|
+
var loadingMain = this.load('main_view', this.config.template)
|
139
|
+
.done(_.bind(loadMainView, this));
|
140
|
+
// load metadata
|
141
|
+
this.load(this.config.metadata, this.metadata)
|
142
|
+
.done(_.bind(loadLocals, this, loadingLocals));
|
143
|
+
|
144
|
+
this.loading = $.when(loadingLocals, loadingMain)
|
145
|
+
.done(function() { app.trigger('loaded') });
|
146
|
+
},
|
147
|
+
|
148
|
+
/**
|
149
|
+
* @brief triggers application error
|
150
|
+
*
|
151
|
+
* @param msg error message
|
152
|
+
* @return null
|
153
|
+
*/
|
154
|
+
error: function(msg) {
|
155
|
+
console.log('Error occured: ' + msg);
|
156
|
+
app.trigger('error', msg);
|
157
|
+
},
|
158
|
+
|
159
|
+
/**
|
160
|
+
* @brief loads application resource
|
161
|
+
* @details resource is a template or a data
|
162
|
+
*
|
163
|
+
* @param source source to load resource from. Can be a jQuery object or
|
164
|
+
* a string that can be a jQuery selector or an url
|
165
|
+
* @param target resource type. Can be a template class or Backbone
|
166
|
+
* model or collection
|
167
|
+
*
|
168
|
+
* @return jQuery deferred
|
169
|
+
*/
|
170
|
+
load: function(source, target) {
|
171
|
+
var deferred = $.Deferred();
|
172
|
+
var app = this;
|
173
|
+
$.when(loadResource(this, source, target))
|
174
|
+
.done(function(str) {
|
175
|
+
deferred.resolve(createResource(app, str, target));
|
176
|
+
})
|
177
|
+
.fail(function() {
|
178
|
+
deferred.reject('while loading component');
|
179
|
+
app.error('while loading component');
|
180
|
+
});
|
181
|
+
return deferred;
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
_.extend(App, Backbone.Events);
|
186
|
+
|
187
|
+
return App;
|
188
|
+
});
|
@@ -0,0 +1,28 @@
|
|
1
|
+
define(
|
2
|
+
|
3
|
+
function() {
|
4
|
+
'use strict';
|
5
|
+
|
6
|
+
var iconSets = {
|
7
|
+
fa: {},
|
8
|
+
glyphicon: { users: 'user' }
|
9
|
+
}
|
10
|
+
|
11
|
+
function Icons(iconSet) {
|
12
|
+
var set = iconSets[iconSet];
|
13
|
+
this.icons = (set) ? set : {};
|
14
|
+
this.set = iconSet;
|
15
|
+
};
|
16
|
+
|
17
|
+
Icons.prototype.get = function(name) {
|
18
|
+
var icon = this.icons[name];
|
19
|
+
return (icon) ? icon : name;
|
20
|
+
};
|
21
|
+
|
22
|
+
Icons.prototype.htmlClass = function(name) {
|
23
|
+
console.log(this, name);
|
24
|
+
return this.set + ' ' + this.set + '-' + this.get(name);
|
25
|
+
}
|
26
|
+
|
27
|
+
return Icons
|
28
|
+
});
|
@@ -0,0 +1,21 @@
|
|
1
|
+
requirejs.config({
|
2
|
+
baseUrl: 'src/js',
|
3
|
+
paths: {
|
4
|
+
admin_it: './',
|
5
|
+
jquery: '../../components/jquery/dist/jquery',
|
6
|
+
underscore: '../../components/underscore/underscore',
|
7
|
+
backbone: '../../components/backbone/backbone',
|
8
|
+
mustache: '../../components/mustache/mustache',
|
9
|
+
nestedtypes: '../../lib/nestedtypes'
|
10
|
+
}
|
11
|
+
});
|
12
|
+
|
13
|
+
require(
|
14
|
+
|
15
|
+
['jquery', 'admin_it/app', 'mustache'],
|
16
|
+
|
17
|
+
function($, App, Mustache) {
|
18
|
+
'use strict';
|
19
|
+
|
20
|
+
App.initialize({ template: Mustache, icons: 'fa' });
|
21
|
+
});
|
@@ -0,0 +1,16 @@
|
|
1
|
+
define(
|
2
|
+
|
3
|
+
['jquery', 'underscore', 'nestedtypes',
|
4
|
+
'admin_it/collections/resources'],
|
5
|
+
|
6
|
+
function($, _, Backbone, Resources) {
|
7
|
+
'use strict';
|
8
|
+
|
9
|
+
var Metadata = Backbone.Model.extend({
|
10
|
+
defaults: {
|
11
|
+
resources: Resources
|
12
|
+
}
|
13
|
+
});
|
14
|
+
|
15
|
+
return Metadata
|
16
|
+
});
|
@@ -0,0 +1,102 @@
|
|
1
|
+
define(
|
2
|
+
|
3
|
+
['jquery', 'underscore'],
|
4
|
+
|
5
|
+
function($, _) {
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
function Template() {
|
9
|
+
this.initialize.apply(this, arguments);
|
10
|
+
}
|
11
|
+
|
12
|
+
Template.globals = {};
|
13
|
+
Template.helpers = {};
|
14
|
+
|
15
|
+
return function(engine) {
|
16
|
+
if (_.isFunction(engine)) {
|
17
|
+
// underscore or other function-like template
|
18
|
+
Template.registerHelper = function(name, func) {
|
19
|
+
Template.helpers[name] = func;
|
20
|
+
};
|
21
|
+
|
22
|
+
Template.prototype.initialize = function(template) {
|
23
|
+
this.engine = engine
|
24
|
+
this.template = template;
|
25
|
+
};
|
26
|
+
|
27
|
+
Template.prototype.render = function(data, partials) {
|
28
|
+
if (!_.isEmpty(partials)) {
|
29
|
+
data.partial = function(k, d) {
|
30
|
+
var v = partials[k];
|
31
|
+
if (!v) return '';
|
32
|
+
if (v instanceof Template) return v.render(d, partials);
|
33
|
+
return v;
|
34
|
+
};
|
35
|
+
}
|
36
|
+
data = _.extend({}, this.helpers, Template.globals, data);
|
37
|
+
return this.engine(this.template, data);
|
38
|
+
};
|
39
|
+
|
40
|
+
Template.defaults = {
|
41
|
+
icon: new Template('<i class="<%-iconClass(icon)"></i>'),
|
42
|
+
button: new Template('<div class="btn<%if(type) print(" "+type%>"><%partial("icon",{icon:icon})%><%=content%></div>')
|
43
|
+
};
|
44
|
+
|
45
|
+
} else if (_.isObject(engine)) {
|
46
|
+
|
47
|
+
if (engine.name == 'mustache.js') {
|
48
|
+
// Mustache template
|
49
|
+
Template.registerHelper = function(name, func) {
|
50
|
+
Template.helpers[name] = function() { return function(text, render) {
|
51
|
+
return func(render(text));
|
52
|
+
}};
|
53
|
+
};
|
54
|
+
|
55
|
+
Template.prototype.initialize = function(template) {
|
56
|
+
this.engine = engine;
|
57
|
+
this.template = template;
|
58
|
+
this.engine.parse(template);
|
59
|
+
};
|
60
|
+
|
61
|
+
Template.prototype.render = function(data, partials) {
|
62
|
+
_.each(partials, function(v, k) {
|
63
|
+
if (v instanceof Template) partials[k] = v.template;
|
64
|
+
});
|
65
|
+
data = _.extend({}, Template.helpers, Template.globals, data);
|
66
|
+
return this.engine.render(this.template, data, partials);
|
67
|
+
};
|
68
|
+
} else if (_.isFunction(engine.compile) &&
|
69
|
+
_.isFunction(engine.registerHelper)) {
|
70
|
+
// Handlebars template
|
71
|
+
Template.registerHelper = function(name, func) {
|
72
|
+
engine.registerHelper(name, func);
|
73
|
+
};
|
74
|
+
|
75
|
+
Template.prototype.initialize = function(template) {
|
76
|
+
this.engine = engine;
|
77
|
+
this.template = this.engine.compile(template);
|
78
|
+
};
|
79
|
+
|
80
|
+
Template.prototype.render = function(data, partials) {
|
81
|
+
_.each(partials, function(v, k) {
|
82
|
+
if (v instanceof Template) v = v.template;
|
83
|
+
this.engine.registerPartial(k, v)
|
84
|
+
});
|
85
|
+
data = _.extend({}, Template.globals, data);
|
86
|
+
return this.template(data);
|
87
|
+
};
|
88
|
+
} else {
|
89
|
+
throw new Error('Unsupported template engine');
|
90
|
+
}
|
91
|
+
|
92
|
+
Template.defaults = {
|
93
|
+
icon: new Template('<i class="{{#iconClass}}{{icon}}{{/iconClass}}"></i>'),
|
94
|
+
button: new Template('<div class="btn{{#type}} {{.}}{{/type}}">{{>icon}}{{content}}</div>')
|
95
|
+
};
|
96
|
+
} else {
|
97
|
+
throw new Error('Wrong template engine');
|
98
|
+
}
|
99
|
+
|
100
|
+
return Template;
|
101
|
+
}
|
102
|
+
});
|
@@ -0,0 +1,16 @@
|
|
1
|
+
define(
|
2
|
+
|
3
|
+
['jquery', 'underscore', 'backbone'],
|
4
|
+
|
5
|
+
function($, _, Backbone) {
|
6
|
+
'use strict';
|
7
|
+
|
8
|
+
var Base = Backbone.View.extend({
|
9
|
+
initialize: function(opts) {
|
10
|
+
this.template = opts.template;
|
11
|
+
this.app = opts.app;
|
12
|
+
}
|
13
|
+
});
|
14
|
+
|
15
|
+
return Base
|
16
|
+
});
|
@@ -0,0 +1,34 @@
|
|
1
|
+
define(
|
2
|
+
|
3
|
+
['jquery', 'underscore', 'backbone',
|
4
|
+
'admin_it/views/base', 'admin_it/views/menu'],
|
5
|
+
|
6
|
+
function($, _, Backbone, Base, Menu) {
|
7
|
+
'use strict';
|
8
|
+
|
9
|
+
var appLoaded = function() {
|
10
|
+
this.render();
|
11
|
+
this.menu = new Menu({
|
12
|
+
app: this.app,
|
13
|
+
template: this.app.templates['main_menu'],
|
14
|
+
collection: this.app.metadata.resources,
|
15
|
+
el: this.$('.admin-it-main-menu')
|
16
|
+
});
|
17
|
+
this.menu.render();
|
18
|
+
this.menu.select();
|
19
|
+
};
|
20
|
+
|
21
|
+
var Main = Base.extend({
|
22
|
+
initialize: function(opts) {
|
23
|
+
Base.prototype.initialize.apply(this, arguments);
|
24
|
+
this.listenTo(this.app, 'loaded', appLoaded);
|
25
|
+
},
|
26
|
+
|
27
|
+
render: function() {
|
28
|
+
this.$el.html(this.template.render());
|
29
|
+
return this;
|
30
|
+
}
|
31
|
+
});
|
32
|
+
|
33
|
+
return Main
|
34
|
+
});
|
@@ -0,0 +1,51 @@
|
|
1
|
+
define(
|
2
|
+
|
3
|
+
['jquery', 'underscore', 'backbone',
|
4
|
+
'admin_it/views/base'],
|
5
|
+
|
6
|
+
function($, _, Backbone, Base) {
|
7
|
+
'use strict';
|
8
|
+
|
9
|
+
var Menu = Base.extend({
|
10
|
+
events: {
|
11
|
+
'click .admin-it-main-menu-item': 'select'
|
12
|
+
},
|
13
|
+
|
14
|
+
initialize: function(opts) {
|
15
|
+
Base.prototype.initialize.apply(this, arguments);
|
16
|
+
this.selected = null;
|
17
|
+
this.listenTo(this.collection, 'reset add change', this.render);
|
18
|
+
},
|
19
|
+
|
20
|
+
render: function() {
|
21
|
+
if (!this.el) return this;
|
22
|
+
this.$el.html(this.template.render(
|
23
|
+
{ items: this.collection.toJSON() },
|
24
|
+
{ icon: this.app.templates.icon }
|
25
|
+
));
|
26
|
+
return this;
|
27
|
+
},
|
28
|
+
|
29
|
+
select: function(index) {
|
30
|
+
if (!index) index = 0;
|
31
|
+
var model = null;
|
32
|
+
if (index instanceof $.Event) {
|
33
|
+
index.preventDefault();
|
34
|
+
index = $(index.currentTarget).data('id');
|
35
|
+
}
|
36
|
+
if (_.isNumber(index)) {
|
37
|
+
if (index < 0 || index >= this.collection.length) index = 0;
|
38
|
+
model = this.collection.at(index);
|
39
|
+
} else if (_.isString(index)) {
|
40
|
+
model = this.collection.get(index);
|
41
|
+
}
|
42
|
+
if (!model) return;
|
43
|
+
console.log(this);
|
44
|
+
this.$('.active').removeClass('active');
|
45
|
+
this.$('[data-id="' + model.id + '"]').addClass('active');
|
46
|
+
this.trigger('selected', model);
|
47
|
+
}
|
48
|
+
});
|
49
|
+
|
50
|
+
return Menu
|
51
|
+
});
|
@@ -0,0 +1 @@
|
|
1
|
+
<ul class="nav nav-pills nav-stacked">{{#items}}<li class="admin-it-main-menu-item" data-id="{{id}}"><a href="#">{{#icon}}{{>icon}} {{/icon}}{{name}}</a></li>{{/items}}</ul>
|
@@ -0,0 +1 @@
|
|
1
|
+
<div class="container-fluid"><div class="row"><div class="admin-it-main-menu col-md-2"></div><div class="admin-it-work-area col-md-10"></div></div></div>
|
@@ -0,0 +1 @@
|
|
1
|
+
<script data-name="main_menu" data-type="template" type="application/mustache"><ul class="nav nav-pills nav-stacked">{{#items}}<li class="admin-it-main-menu-item" data-id="{{id}}"><a href="#">{{#icon}}{{>icon}} {{/icon}}{{name}}</a></li>{{/items}}</ul></script><script data-name="main_view" data-type="template" type="application/mustache"><div class="container-fluid"><div class="row"><div class="admin-it-main-menu col-md-2"></div><div class="admin-it-work-area col-md-10"></div></div></div></script>
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'tilt'
|
3
|
+
|
4
|
+
# project compilation
|
5
|
+
class Compile < Thor
|
6
|
+
FOLDERS = [File.join(Dir.pwd, 'src', 'templates')]
|
7
|
+
|
8
|
+
# context
|
9
|
+
class Context
|
10
|
+
attr_reader :pwd
|
11
|
+
|
12
|
+
def render_each(folder)
|
13
|
+
return unless File.directory?(folder)
|
14
|
+
Dir[File.join(folder, '**', '*.html.*')].each do |file|
|
15
|
+
name = file.split('.')[0..-3].join('.')
|
16
|
+
yield name, render(name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def include(name)
|
21
|
+
FOLDERS.each do |folder|
|
22
|
+
files = Dir[File.absolute_path(name, folder)]
|
23
|
+
files.each { |file| return File.read(file) }
|
24
|
+
end
|
25
|
+
fail "Couldn't include #{name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def render(name)
|
29
|
+
FOLDERS.each do |folder|
|
30
|
+
files = Dir[File.absolute_path("#{name}.html.*", folder)]
|
31
|
+
files.each do |file|
|
32
|
+
@pwd = File.dirname(file)
|
33
|
+
return Tilt.new(file).render(self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
fail "Couldn't render #{name}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
desc 'templates', 'compile templates'
|
41
|
+
def templates
|
42
|
+
files = Dir[File.join(Dir.pwd, '*.html.*')]
|
43
|
+
FOLDERS.each { |folder| files += Dir[File.join(folder, '**', '*.html.*')] }
|
44
|
+
context = Context.new
|
45
|
+
files.each do |file|
|
46
|
+
name = file.split('.')[0..-3].join('.')
|
47
|
+
output = "#{name}.html"
|
48
|
+
File.write(output, context.render(name))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/config/routes.rb
CHANGED
data/lib/admin_it/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: admin_it
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexey Ovchinnikov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -284,6 +284,31 @@ files:
|
|
284
284
|
- app/views/admin_it/tiles.html.slim
|
285
285
|
- app/views/layouts/admin_it.html.slim
|
286
286
|
- app/views/layouts/admin_it_dialog.html.slim
|
287
|
+
- client/.bowerrc
|
288
|
+
- client/Gemfile
|
289
|
+
- client/bower.json
|
290
|
+
- client/index.html
|
291
|
+
- client/index.html.slim
|
292
|
+
- client/lib/nestedtypes.js
|
293
|
+
- client/metadata.json
|
294
|
+
- client/src/js/app.js
|
295
|
+
- client/src/js/collections/resources.js
|
296
|
+
- client/src/js/icons.js
|
297
|
+
- client/src/js/main.js
|
298
|
+
- client/src/js/models/base.js
|
299
|
+
- client/src/js/models/metadata.js
|
300
|
+
- client/src/js/models/resource.js
|
301
|
+
- client/src/js/template.js
|
302
|
+
- client/src/js/views/base.js
|
303
|
+
- client/src/js/views/main.js
|
304
|
+
- client/src/js/views/menu.js
|
305
|
+
- client/src/templates/bootstrap.html
|
306
|
+
- client/src/templates/bootstrap.html.slim
|
307
|
+
- client/src/templates/bootstrap/main_menu.html
|
308
|
+
- client/src/templates/bootstrap/main_menu.html.slim
|
309
|
+
- client/src/templates/bootstrap/main_view.html
|
310
|
+
- client/src/templates/bootstrap/main_view.html.slim
|
311
|
+
- client/tasks/compile.thor
|
287
312
|
- config.ru
|
288
313
|
- config/routes.rb
|
289
314
|
- lib/admin_it.rb
|