admin_it 1.2.6 → 1.2.7
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 +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
|