tomify 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +19 -0
- data/Rakefile +6 -0
- data/app/assets/images/tomify/gifs/basketball.gif +0 -0
- data/app/assets/images/tomify/gifs/laughing.gif +0 -0
- data/app/assets/images/tomify/gifs/sneer.gif +0 -0
- data/app/assets/images/tomify/gifs/wind.gif +0 -0
- data/app/assets/images/tomify/gifs/woods.gif +0 -0
- data/app/assets/javascripts/tomify/default/array.coffee +4 -0
- data/app/assets/javascripts/tomify/default/jquery.patch.coffee +9 -0
- data/app/assets/javascripts/tomify/default/object.coffee +4 -0
- data/app/assets/javascripts/tomify/default/string.coffee +40 -0
- data/app/assets/javascripts/tomify/dynamic/classes/component.coffee +7 -0
- data/app/assets/javascripts/tomify/dynamic/classes/field.coffee +24 -0
- data/app/assets/javascripts/tomify/dynamic/classes/form.coffee +72 -0
- data/app/assets/javascripts/tomify/dynamic/classes/model.coffee +41 -0
- data/app/assets/javascripts/tomify/dynamic/classes/namespace.coffee +24 -0
- data/app/assets/javascripts/tomify/dynamic/classes/observer.coffee +22 -0
- data/app/assets/javascripts/tomify/dynamic/classes/request.coffee +26 -0
- data/app/assets/javascripts/tomify/dynamic/classes/store.coffee +37 -0
- data/app/assets/javascripts/tomify/dynamic/global.coffee +16 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/pages.coffee.erb +30 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/settings.coffee +27 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/sidebars.coffee +16 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/uploads.coffee +13 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/admin/users.coffee +18 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/edit.coffee +51 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/fields/checkbox.coffee +8 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/fields/default.coffee +3 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/fields/file.coffee +3 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/fields/markdown.coffee +9 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/fields/select.coffee +8 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/fields/textarea.coffee +3 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/index.coffee +87 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/layout/admin_navbar.coffee +29 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/layout/analytics.coffee +34 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/layout/footer.coffee +11 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/layout/header.coffee +28 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/layout/messages.coffee +18 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/layout/navbar.coffee +6 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/layout/public_navbar.coffee +56 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/new.coffee +48 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/pagination.coffee +29 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/public/passwords/new.coffee +38 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/public/sessions/new.coffee +40 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/public/sessions/show.coffee +35 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/public/users/edit.coffee +42 -0
- data/app/assets/javascripts/tomify/dynamic/react/components/public/users/new.coffee +35 -0
- data/app/assets/javascripts/tomify/dynamic/react/mixins/follow.coffee +51 -0
- data/app/assets/javascripts/tomify/dynamic/react/mixins/will-initialize.coffee +7 -0
- data/app/assets/javascripts/tomify/dynamic/react/mount.coffee +9 -0
- data/app/assets/javascripts/tomify/turbolinks/compatibility.coffee +29 -0
- data/app/assets/javascripts/tomify/turbolinks/react.coffee +5 -0
- data/app/assets/javascripts/tomify-dynamic.js +8 -0
- data/app/assets/javascripts/tomify.js +1 -0
- data/app/assets/stylesheets/_tomify.scss +1 -0
- data/app/assets/stylesheets/tomify/_default.scss +26 -0
- data/app/assets/stylesheets/tomify/_footer.scss +1 -0
- data/app/assets/stylesheets/tomify/_navbar.scss +43 -0
- data/app/assets/stylesheets/tomify/_page.scss +31 -0
- data/app/assets/stylesheets/tomify/_pagination.scss +5 -0
- data/app/assets/stylesheets/tomify/_variables.scss +10 -0
- data/app/controllers/tomify/admin/controller.rb +7 -0
- data/app/controllers/tomify/admin/pages_controller.rb +2 -0
- data/app/controllers/tomify/admin/settings_controller.rb +2 -0
- data/app/controllers/tomify/admin/sidebars_controller.rb +2 -0
- data/app/controllers/tomify/admin/uploads_controller.rb +2 -0
- data/app/controllers/tomify/admin/users_controller.rb +2 -0
- data/app/controllers/tomify/api/admin/controller.rb +3 -0
- data/app/controllers/tomify/api/admin/pages_controller.rb +2 -0
- data/app/controllers/tomify/api/admin/settings_controller.rb +2 -0
- data/app/controllers/tomify/api/admin/sidebars_controller.rb +2 -0
- data/app/controllers/tomify/api/admin/uploads_controller.rb +2 -0
- data/app/controllers/tomify/api/admin/users_controller.rb +7 -0
- data/app/controllers/tomify/api/public/controller.rb +3 -0
- data/app/controllers/tomify/api/public/passwords_controller.rb +10 -0
- data/app/controllers/tomify/api/public/sessions_controller.rb +19 -0
- data/app/controllers/tomify/api/public/users_controller.rb +25 -0
- data/app/controllers/tomify/concerns/api/admin.rb +9 -0
- data/app/controllers/tomify/concerns/api/helpers.rb +57 -0
- data/app/controllers/tomify/concerns/api/json.rb +46 -0
- data/app/controllers/tomify/concerns/api/public.rb +9 -0
- data/app/controllers/tomify/concerns/default/auth_helper.rb +22 -0
- data/app/controllers/tomify/concerns/default/env_helper.rb +34 -0
- data/app/controllers/tomify/concerns/default/helper.rb +13 -0
- data/app/controllers/tomify/concerns/default/react_helper.rb +7 -0
- data/app/controllers/tomify/concerns/default.rb +17 -0
- data/app/controllers/tomify/public/controller.rb +2 -0
- data/app/controllers/tomify/public/pages_controller.rb +22 -0
- data/app/controllers/tomify/public/sessions_controller.rb +5 -0
- data/app/controllers/tomify/public/users_controller.rb +7 -0
- data/app/controllers/tomify_controller.rb +3 -0
- data/app/helpers/tomify/carrierwave_helper.rb +17 -0
- data/app/helpers/tomify/email_helper.rb +19 -0
- data/app/helpers/tomify/render_helper.rb +13 -0
- data/app/helpers/tomify/timezone_helper.rb +5 -0
- data/app/mailers/tomify/user_mailer.rb +13 -0
- data/app/mailers/tomify_mailer.rb +14 -0
- data/app/models/tomify/concerns/page.rb +74 -0
- data/app/models/tomify/concerns/sidebar.rb +18 -0
- data/app/models/tomify/concerns/token.rb +18 -0
- data/app/models/tomify/concerns/upload.rb +36 -0
- data/app/models/tomify/concerns/user.rb +52 -0
- data/app/models/tomify/page.rb +3 -0
- data/app/models/tomify/setting/boolean.rb +11 -0
- data/app/models/tomify/setting/json.rb +11 -0
- data/app/models/tomify/setting/text.rb +3 -0
- data/app/models/tomify/setting/uploader.rb +6 -0
- data/app/models/tomify/setting.rb +48 -0
- data/app/models/tomify/sidebar.rb +3 -0
- data/app/models/tomify/token.rb +3 -0
- data/app/models/tomify/upload.rb +3 -0
- data/app/models/tomify/user.rb +3 -0
- data/app/models/tomify_record.rb +33 -0
- data/app/uploaders/tomify/file_uploader.rb +5 -0
- data/app/uploaders/tomify/setting_uploader.rb +7 -0
- data/app/uploaders/tomify_uploader.rb +20 -0
- data/app/views/templates/contact.haml +24 -0
- data/app/views/templates/default.haml +4 -0
- data/app/views/tomify/defaults/_body.haml +8 -0
- data/app/views/tomify/defaults/_container.haml +3 -0
- data/app/views/tomify/defaults/_env.haml +2 -0
- data/app/views/tomify/defaults/_head.haml +14 -0
- data/app/views/tomify/defaults/_meta.haml +17 -0
- data/app/views/tomify/defaults/_sharing.haml +23 -0
- data/app/views/tomify/layouts/application.haml +4 -0
- data/app/views/tomify/layouts/mailer.haml +7 -0
- data/app/views/tomify/mailers/user_mailer/invite.haml +13 -0
- data/app/views/tomify/mailers/user_mailer/reset_password.haml +17 -0
- data/config/initializers/assets.rb +7 -0
- data/config/initializers/database.rb +3 -0
- data/config/initializers/flash_patch.rb +15 -0
- data/config/initializers/mailer.rb +4 -0
- data/config/initializers/renderers.rb +4 -0
- data/config/initializers/settings.rb +5 -0
- data/config/routes.rb +38 -0
- data/db/migrate/19900000000000_create_users.rb +13 -0
- data/db/migrate/19900000000001_create_tokens.rb +10 -0
- data/db/migrate/19900000000002_create_settings.rb +13 -0
- data/db/migrate/19900000000003_create_uploads.rb +13 -0
- data/db/migrate/19900000000004_create_pages.rb +22 -0
- data/db/migrate/19900000000005_create_sidebars.rb +12 -0
- data/db/seeds.rb +43 -0
- data/lib/generators/tomify/bundle/bundle_generator.rb +12 -0
- data/lib/generators/tomify/bundle/templates/default.js +2 -0
- data/lib/generators/tomify/bundle/templates/react.js +3 -0
- data/lib/previews/tomify/user_preview.rb +9 -0
- data/lib/previews/tomify_preview.rb +6 -0
- data/lib/tasks/package.rake +40 -0
- data/lib/tomify/constantly.rb +12 -0
- data/lib/tomify/engine.rb +11 -0
- data/lib/tomify/version.rb +3 -0
- data/lib/tomify.rb +40 -0
- data/vendor/tomify/development/react.js +20494 -0
- data/vendor/tomify/production/react.js +20352 -0
- metadata +219 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d5d3338338ef1c5b06f88ce9df1f5456ce8484a8
|
4
|
+
data.tar.gz: 00481061072904e6dea701448bead33cf4cd0d5c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 76c1403baf7268c7b334ecdd96f9766cc5644aa2949352bf2595ad8a16ce372e27d8c763cd8e1c61bb5e6643f05090873659d040535c22fe9af115e3329193ac
|
7
|
+
data.tar.gz: 006a9d347314ba2766fbf70cb1a1d175da13c2186cd837dc6fd9723c76ba83855a7991967958ce8f337732f4d9caa01c1c86d84d37271a36973619743a0b5af9
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Tom Prats
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Tomify
|
2
|
+
|
3
|
+
## Controllers
|
4
|
+
You can override a controller in an initializer
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
Tomify.controllers.base = "ApplicationController"
|
8
|
+
```
|
9
|
+
|
10
|
+
## Views
|
11
|
+
You can override a default template in a layout file
|
12
|
+
|
13
|
+
```haml
|
14
|
+
- content_for :navbar_partial, "layouts/navbar"
|
15
|
+
= render "tomify/layouts/application"
|
16
|
+
```
|
17
|
+
|
18
|
+
## License
|
19
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,40 @@
|
|
1
|
+
String::assign = (options) ->
|
2
|
+
@replace /::.*::/, (replace) -> options[replace.slice(2, -2)] ? ""
|
3
|
+
|
4
|
+
String::break = ->
|
5
|
+
@replace(/(?!^)([A-Z])/g, "_$1").lowercase.split(/[\W_-]/)
|
6
|
+
|
7
|
+
String::date = ->
|
8
|
+
new Date(@).toLocaleDateString()
|
9
|
+
|
10
|
+
String::startsWith ?= (s) -> @slice(0, s.length) == s
|
11
|
+
String::endsWith ?= (s) -> s != "" && @slice(-s.length) == s
|
12
|
+
|
13
|
+
String::transform = (method) ->
|
14
|
+
string = @replace(/(?!^)([A-Z])/g, "_$1").lowercase
|
15
|
+
string = string.splitAndJoin "-", method
|
16
|
+
string = string.splitAndJoin "_", method
|
17
|
+
string = string.splitAndJoin ".", method
|
18
|
+
string
|
19
|
+
|
20
|
+
String::splitAnd = (separator, method) ->
|
21
|
+
method word for word in @split(separator)
|
22
|
+
|
23
|
+
String::splitAndJoin = (separator, method) ->
|
24
|
+
@splitAnd(separator, method).join(separator)
|
25
|
+
|
26
|
+
Object.defineProperties String.prototype, {
|
27
|
+
camelize: { get: ->
|
28
|
+
word = (word.capitalize for word in @break()).join()
|
29
|
+
word.charAt(0).lowercase + word.slice(1)
|
30
|
+
},
|
31
|
+
capitalize: { get: ->
|
32
|
+
@transform (word) -> word.charAt(0).toUpperCase() + word.slice(1)
|
33
|
+
},
|
34
|
+
dasherize: { get: -> @break().join("-") },
|
35
|
+
lowercase: { get: -> @toLowerCase() },
|
36
|
+
pluralize: { get: -> "#{@}s" },
|
37
|
+
titleize: { get: -> (word.capitalize for word in @break() when word isnt "id").join(" ") },
|
38
|
+
underscore: { get: -> @break().join("_") },
|
39
|
+
uppercase: { get: -> @toUpperCase() }
|
40
|
+
}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class @Component extends Namespace
|
2
|
+
@base: window
|
3
|
+
@create: (namespace, component) ->
|
4
|
+
component.displayName ?= namespace
|
5
|
+
component.mixins = @defaultMixins.concat(component.mixins || [])
|
6
|
+
@namespace namespace, React.createClass(component)
|
7
|
+
@defaultMixins: [WillInitializeMixin, FollowMixin]
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class @Field
|
2
|
+
@build: (options) ->
|
3
|
+
props = { name: options.name, type: options.type }
|
4
|
+
switch props.type
|
5
|
+
when "checkbox"
|
6
|
+
props.label = options.label
|
7
|
+
when "select"
|
8
|
+
props.allowBlank = options.allowBlank
|
9
|
+
props.model = options.model
|
10
|
+
props.options = []
|
11
|
+
for option in options.options || []
|
12
|
+
props.options.push { value: option?.value ? option, name: option?.name ? option.titleize }
|
13
|
+
field = new Field(options)
|
14
|
+
field.props = props
|
15
|
+
field.setForm(options.form)
|
16
|
+
field
|
17
|
+
copy: (form) ->
|
18
|
+
Field.build $.extend({}, @options, { form: form })
|
19
|
+
constructor: (options) ->
|
20
|
+
@options = options
|
21
|
+
setForm: (form) ->
|
22
|
+
@props.value = form.value.bind(form)
|
23
|
+
@props.onChange = form.onChange.bind(form)
|
24
|
+
@props.options = form.options.bind(form, @props) if @props.model
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class @Form
|
2
|
+
copy: ->
|
3
|
+
form = new Form(@layout)
|
4
|
+
form.fields = (field.copy(form) for field in @fields)
|
5
|
+
form.models = @models
|
6
|
+
form
|
7
|
+
constructor: (layout) ->
|
8
|
+
@layout = layout
|
9
|
+
@fields = []
|
10
|
+
@models = []
|
11
|
+
value: (name) ->
|
12
|
+
@changes.get(name) ? @record.get(name) ? ""
|
13
|
+
onChange: (name, e) ->
|
14
|
+
change = {}
|
15
|
+
change[name] = switch e.target.type
|
16
|
+
when "checkbox" then e.target.checked
|
17
|
+
when "file" then e.target.files[0]
|
18
|
+
else e.target.value
|
19
|
+
return @changes.remove(name) if @record.get(name) == change[name]
|
20
|
+
@changes.merge(change)
|
21
|
+
options: (props) ->
|
22
|
+
options = []
|
23
|
+
options.push { value: "", name: "None" } if props.allowBlank
|
24
|
+
for record in @component.state[props.model.lowercase]
|
25
|
+
options.push { value: record.id, name: record.name }
|
26
|
+
options
|
27
|
+
add: (name, type, options = {}) ->
|
28
|
+
options = $.extend { type: type, name: name, form: @ }, options
|
29
|
+
@fields.push Field.build(options)
|
30
|
+
@models.push options.model if options.model
|
31
|
+
setComponent: (component) ->
|
32
|
+
@component = component
|
33
|
+
@record = component.store.findOrCreate "Record", {}
|
34
|
+
@changes = component.store.findOrCreate "Changes", {}
|
35
|
+
@stores = { record: @record, changes: @changes }
|
36
|
+
component.followModels.push model for model in @models
|
37
|
+
component.followStores.push BuildObject(key, value) for key, value of @stores
|
38
|
+
@
|
39
|
+
setDefaultValues: ->
|
40
|
+
record = @record.get()
|
41
|
+
changes = {}
|
42
|
+
for field in @fields
|
43
|
+
name = field.props.name
|
44
|
+
value = null
|
45
|
+
continue if record[name]?
|
46
|
+
switch field.props.type
|
47
|
+
when "checkbox"
|
48
|
+
value = true
|
49
|
+
when "select"
|
50
|
+
continue if field.allowBlank
|
51
|
+
options = field.props.options?() || field.props.options
|
52
|
+
value = options[0]?.value
|
53
|
+
continue unless value?
|
54
|
+
changes[name] = value
|
55
|
+
@changes.set(changes)
|
56
|
+
renderField: (field) ->
|
57
|
+
FieldComponent = Form.Field[field.props.type.capitalize] || Form.Field.Default
|
58
|
+
if @layout == "horizontal"
|
59
|
+
<div key={field.props.name} className="form-group">
|
60
|
+
<label className="col-sm-2 control-label" htmlFor={field.props.name}>
|
61
|
+
{field.props.name.titleize}
|
62
|
+
</label>
|
63
|
+
<div className="col-sm-10">
|
64
|
+
<FieldComponent {...field.props} />
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
else
|
68
|
+
<div key={field.props.name} className="form-group">
|
69
|
+
<FieldComponent {...field.props} />
|
70
|
+
</div>
|
71
|
+
render: ->
|
72
|
+
@renderField(field) for field in @fields
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class @Model extends Observer
|
2
|
+
@base: Model
|
3
|
+
@create: (namespace, options) ->
|
4
|
+
@namespace namespace, new @(namespace, options)
|
5
|
+
constructor: (namespace, options = {}) ->
|
6
|
+
@name = namespace.split(".").last ? throw "Model: Requires Name"
|
7
|
+
@prefix = namespace.split(".").first.lowercase
|
8
|
+
@path = options.path ? @name.underscore.pluralize
|
9
|
+
@path = "/api/#{@prefix}/#{@path}"
|
10
|
+
@requests = {}
|
11
|
+
@setDefaultActions()
|
12
|
+
setAction: (name, request) ->
|
13
|
+
requests = @requests[name] = []
|
14
|
+
context = @
|
15
|
+
@[name] = (args...) ->
|
16
|
+
request.running = true
|
17
|
+
requests.push(request)
|
18
|
+
$.when(request(args...)).done (response) ->
|
19
|
+
request.running = false
|
20
|
+
context.trigger name, response
|
21
|
+
requested: (action) -> @requests[action].length > 0
|
22
|
+
request: (type, nest) ->
|
23
|
+
name = @name.underscore
|
24
|
+
basePath = @path
|
25
|
+
(path, params) ->
|
26
|
+
[path, params] = [params, path] if path instanceof Object
|
27
|
+
route = basePath
|
28
|
+
route += "/#{path}" if path
|
29
|
+
if nest
|
30
|
+
[params, nest] = [{}, params]
|
31
|
+
params[name] = nest
|
32
|
+
Request[type](route, params)
|
33
|
+
setDefaultActions: ->
|
34
|
+
@setAction "find", @request "get"
|
35
|
+
@setAction "edit", @request "get"
|
36
|
+
@setAction "update", @request "put", true
|
37
|
+
@setAction "new", Request.none
|
38
|
+
@setAction "all", @request "get"
|
39
|
+
@setAction "where", @request "get"
|
40
|
+
@setAction "create", @request "post", true
|
41
|
+
@setAction "destroy", @request "delete"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class @Namespace
|
2
|
+
@base: window,
|
3
|
+
@namespace: (namespace, value) ->
|
4
|
+
unless namespace?.length then throw "Namespace: Requires Keys"
|
5
|
+
unless value? then throw "Namespace: Requires Value"
|
6
|
+
keys = namespace.split(".")
|
7
|
+
value.namespace = namespace
|
8
|
+
scope = @base
|
9
|
+
for key in keys when key isnt keys.last
|
10
|
+
scope[key] ?= {}
|
11
|
+
scope = scope[key]
|
12
|
+
scope[keys.last] = value
|
13
|
+
value
|
14
|
+
@create: @namespace
|
15
|
+
@find: (namespace) ->
|
16
|
+
unless namespace?.length then throw "Namespace: Requires Keys"
|
17
|
+
scope = @base
|
18
|
+
scope = (scope ? {})[key] for key in namespace.split(".")
|
19
|
+
scope
|
20
|
+
@findOrCreate: (namespace, options) ->
|
21
|
+
@find(namespace, options) || @create(namespace, options)
|
22
|
+
findOrCreate: (namespace, options) ->
|
23
|
+
namespace = "#{@namespace}.#{namespace}"
|
24
|
+
@constructor.find(namespace, options) || @constructor.create(namespace, options)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class @Observer extends Namespace
|
2
|
+
@base: Observer
|
3
|
+
@create: (namespace) ->
|
4
|
+
@namespace namespace, new @
|
5
|
+
callbacksFor: (action) ->
|
6
|
+
@observe ?= {}
|
7
|
+
@observe[action] ?= $.Callbacks()
|
8
|
+
on: (action, callback) ->
|
9
|
+
context = @
|
10
|
+
callback.off = -> context.off(action, callback)
|
11
|
+
@callbacksFor(action).add callback
|
12
|
+
callback
|
13
|
+
off: (action, callback) ->
|
14
|
+
@callbacksFor(action).remove(callback)
|
15
|
+
callback
|
16
|
+
one: (action, callback) ->
|
17
|
+
@on action, callback
|
18
|
+
@on action, -> callback.off()
|
19
|
+
callback
|
20
|
+
trigger: (action, data) ->
|
21
|
+
@callbacksFor(action).fire(data)
|
22
|
+
@
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class @Request
|
2
|
+
@none: (data) -> data
|
3
|
+
@make: (type) ->
|
4
|
+
(path, params) -> @ajax(type: type, url: path, data: params)
|
5
|
+
@get: @make("GET")
|
6
|
+
@post: @make("POST")
|
7
|
+
@put: @make("PUT")
|
8
|
+
@delete: @make("DELETE")
|
9
|
+
@ajax: (options) ->
|
10
|
+
options.url = options.url.assign(options.data)
|
11
|
+
if options.type in ["POST", "PUT"]
|
12
|
+
$.extend options, {
|
13
|
+
data: @formData(new FormData(), options.data),
|
14
|
+
cache: false,
|
15
|
+
processData: false,
|
16
|
+
contentType: false
|
17
|
+
}
|
18
|
+
$.ajax(options)
|
19
|
+
@formData: (data, object, objectName) ->
|
20
|
+
for key, value of object
|
21
|
+
name = if objectName then "#{objectName}[#{key}]" else key
|
22
|
+
if value not instanceof Object || value instanceof File
|
23
|
+
data.append(name, value)
|
24
|
+
else
|
25
|
+
data = @formData(data, value, name)
|
26
|
+
data
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class @Store extends Observer
|
2
|
+
@base: Store
|
3
|
+
@create: (namespace, data) ->
|
4
|
+
@namespace namespace, new @(data)
|
5
|
+
constructor: (data) ->
|
6
|
+
@data = data ? {}
|
7
|
+
set: (data) ->
|
8
|
+
@data = data
|
9
|
+
@trigger "change", data
|
10
|
+
@
|
11
|
+
merge: (data) ->
|
12
|
+
throw "Store: Invalid Data Type" unless @isObject()
|
13
|
+
@trigger "merge", data
|
14
|
+
@set($.extend({}, @get(), data))
|
15
|
+
push: (data) ->
|
16
|
+
throw "Store: Invalid Data Type" unless @isArray()
|
17
|
+
@trigger "push", data
|
18
|
+
@set(@get().concat([data]))
|
19
|
+
remove: (field) ->
|
20
|
+
throw "Store: Invalid Data Type" unless @isObject()
|
21
|
+
@trigger "remove", data
|
22
|
+
data = @get()
|
23
|
+
delete data[field]
|
24
|
+
@set(item for item in data when item?)
|
25
|
+
get: (field) ->
|
26
|
+
throw "Store: Invalid Data Type" unless @isDefined()
|
27
|
+
return @data if @isString() && !field
|
28
|
+
data = $.extend(@data.constructor(), @data)
|
29
|
+
if field then data[field] else data
|
30
|
+
empty: ->
|
31
|
+
return @data.length == 0 if @isArray()
|
32
|
+
return Object.keys(@data).length == 0 if @isObject()
|
33
|
+
@data?
|
34
|
+
isDefined: -> @data?
|
35
|
+
isObject: -> @data instanceof Object
|
36
|
+
isArray: -> @data instanceof Array
|
37
|
+
isString: -> $.type(@data) == "string"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
messages = Store.create "Messages", []
|
2
|
+
settings = Store.create "Settings", []
|
3
|
+
user = Store.create "User", {}
|
4
|
+
env = Store.create "Env", {}
|
5
|
+
env.on "change", ->
|
6
|
+
current = env.get()
|
7
|
+
messages.set current.messages
|
8
|
+
settings.set current.settings
|
9
|
+
user.set current.user ? {}
|
10
|
+
|
11
|
+
$ -> env.set(window.env)
|
12
|
+
|
13
|
+
@message = (message) -> messages.push message
|
14
|
+
@setting = (name) ->
|
15
|
+
setting = settings.get().find (setting) -> setting.name == name
|
16
|
+
setting?.value
|
@@ -0,0 +1,30 @@
|
|
1
|
+
model = Model.findOrCreate "Admin.Page"
|
2
|
+
model.columns = [
|
3
|
+
{ name: "active", value: (r) -> if r.active then "Yes" else "No" },
|
4
|
+
{ name: "rank" },
|
5
|
+
{ name: "parent_id", value: (r) -> r.parent?.name ? "None" },
|
6
|
+
{ name: "sidebar_id", value: (r) -> r.sidebar?.name ? "None" },
|
7
|
+
{ name: "name" },
|
8
|
+
{ name: "path" },
|
9
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() },
|
10
|
+
{ name: "actions", edit: true, destroy: true, view: (r) ->
|
11
|
+
<a key="visit" href="/#{r.path}" target="_blank">Visit</a>
|
12
|
+
}
|
13
|
+
]
|
14
|
+
|
15
|
+
form = new Form("horizontal")
|
16
|
+
form.add "parent_id", "select", model: "Admin.Page", allowBlank: true
|
17
|
+
form.add "sidebar_id", "select", model: "Admin.Sidebar", allowBlank: true
|
18
|
+
form.add "active", "checkbox"
|
19
|
+
form.add "root", "checkbox", label: "If checked, this page will be available at <a href='/' target='_blank'>#{location.origin}</a>"
|
20
|
+
form.add "rank", "number"
|
21
|
+
form.add "name", "text"
|
22
|
+
form.add "template", "select", options: <%= Tomify::Page.templates %>
|
23
|
+
form.add "path", "text"
|
24
|
+
form.add "title", "text"
|
25
|
+
form.add "description", "textarea"
|
26
|
+
form.add "cover_image", "file"
|
27
|
+
form.add "share_image", "file"
|
28
|
+
form.add "text", "markdown"
|
29
|
+
|
30
|
+
Component.create "Admin.Pages.Index.Container", render: -> <Index.Container name="Admin.Page" form={form} />
|
@@ -0,0 +1,27 @@
|
|
1
|
+
model = Model.findOrCreate "Admin.Setting"
|
2
|
+
model.columns = [
|
3
|
+
{ name: "name" },
|
4
|
+
{ name: "type", value: (r) -> r.type.split("::").last },
|
5
|
+
{ name: "value", value: (r) ->
|
6
|
+
type = r.type.split("::").last
|
7
|
+
return "#{r.value}" if type == "Boolean"
|
8
|
+
return "JSON (Don't Edit)" if type == "Json"
|
9
|
+
r.value
|
10
|
+
},
|
11
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() },
|
12
|
+
{ name: "actions", edit: true, destroy: true }
|
13
|
+
]
|
14
|
+
|
15
|
+
options = ["Boolean", "Json", "Text", "Uploader"]
|
16
|
+
options = ({ name: option, value: "Tomify::Setting::#{option}" } for option in options)
|
17
|
+
|
18
|
+
newForm = new Form("horizontal")
|
19
|
+
newForm.add "type", "select", options: options
|
20
|
+
newForm.add "name", "text"
|
21
|
+
newForm.add "value", "text"
|
22
|
+
|
23
|
+
editForm = new Form("horizontal")
|
24
|
+
editForm.add "name", "text"
|
25
|
+
editForm.add "value", "text"
|
26
|
+
|
27
|
+
Component.create "Admin.Settings.Index.Container", render: -> <Index.Container name="Admin.Setting" newForm={newForm} editForm={editForm} />
|
@@ -0,0 +1,16 @@
|
|
1
|
+
model = Model.findOrCreate "Admin.Sidebar"
|
2
|
+
model.columns = [
|
3
|
+
{ name: "active", value: (r) -> if r.active then "Yes" else "No" },
|
4
|
+
{ name: "name" },
|
5
|
+
{ name: "heading" },
|
6
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() },
|
7
|
+
{ name: "actions", edit: true, destroy: true }
|
8
|
+
]
|
9
|
+
|
10
|
+
form = new Form("horizontal")
|
11
|
+
form.add "active", "checkbox"
|
12
|
+
form.add "name", "text"
|
13
|
+
form.add "heading", "text"
|
14
|
+
form.add "text", "markdown"
|
15
|
+
|
16
|
+
Component.create "Admin.Sidebars.Index.Container", render: -> <Index.Container name="Admin.Sidebar" form={form} />
|
@@ -0,0 +1,13 @@
|
|
1
|
+
model = Model.findOrCreate "Admin.Upload"
|
2
|
+
model.columns = [
|
3
|
+
{ name: "name" },
|
4
|
+
{ name: "file", value: (r) -> <a href={r.file.url} target="_blank">View</a> },
|
5
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() },
|
6
|
+
{ name: "actions", edit: true, destroy: true }
|
7
|
+
]
|
8
|
+
|
9
|
+
form = new Form("horizontal")
|
10
|
+
form.add "name", "text"
|
11
|
+
form.add "file", "file"
|
12
|
+
|
13
|
+
Component.create "Admin.Uploads.Index.Container", render: -> <Index.Container name="Admin.Upload" form={form} />
|
@@ -0,0 +1,18 @@
|
|
1
|
+
model = Model.findOrCreate "Admin.User"
|
2
|
+
model.columns = [
|
3
|
+
{ name: "admin", value: (r) -> if r.admin then "Yes" else "No" },
|
4
|
+
{ name: "email" },
|
5
|
+
{ name: "first_name" },
|
6
|
+
{ name: "last_name" },
|
7
|
+
{ name: "created_at", value: (r) -> r.created_at.date() },
|
8
|
+
{ name: "updated_at", value: (r) -> r.updated_at.date() },
|
9
|
+
{ name: "actions", edit: true, destroy: true }
|
10
|
+
]
|
11
|
+
|
12
|
+
form = new Form("horizontal")
|
13
|
+
form.add "admin", "checkbox"
|
14
|
+
form.add "email", "text"
|
15
|
+
form.add "first_name", "text"
|
16
|
+
form.add "last_name", "text"
|
17
|
+
|
18
|
+
Component.create "Admin.Users.Index.Container", render: -> <Index.Container name="Admin.User" form={form} />
|
@@ -0,0 +1,51 @@
|
|
1
|
+
Component.create "Edit.Container",
|
2
|
+
componentWillInitialize: ->
|
3
|
+
@model = Model.findOrCreate @props.name
|
4
|
+
@store = Store.findOrCreate "#{@props.name}.Edit"
|
5
|
+
@form = @props.form.setComponent @
|
6
|
+
@follow @model.on "new", @modelNew
|
7
|
+
@follow @model.on "edit", @modelEdit
|
8
|
+
@follow @model.on "update", @modelUpdate
|
9
|
+
@follow @model.on "destroy", @modelDestroy
|
10
|
+
modelNew: ->
|
11
|
+
@store.merge(show: false)
|
12
|
+
modelEdit: (response) ->
|
13
|
+
@form.record.set(response.data)
|
14
|
+
@form.setDefaultValues()
|
15
|
+
@store.merge(show: true)
|
16
|
+
modelUpdate: (response) ->
|
17
|
+
message type: response.type, text: response.message
|
18
|
+
@store.merge(show: false) if response.type == "success"
|
19
|
+
modelDestroy: ->
|
20
|
+
@store.merge(show: false)
|
21
|
+
submit: (e) ->
|
22
|
+
e.preventDefault()
|
23
|
+
return @update() && false unless @form.changes.empty()
|
24
|
+
message type: "warning", text: "#{@model.name.titleize} was not updated"
|
25
|
+
false
|
26
|
+
update: ->
|
27
|
+
@model.update @form.record.get("id"), @form.changes.get()
|
28
|
+
cancel: (e) ->
|
29
|
+
e.preventDefault()
|
30
|
+
@store.merge(show: false)
|
31
|
+
false
|
32
|
+
render: ->
|
33
|
+
return <div /> unless @state.store.show
|
34
|
+
<div className="row">
|
35
|
+
<div className="col-xs-12">
|
36
|
+
<div className="panel panel-default">
|
37
|
+
<div className="panel-heading">
|
38
|
+
<h4>Edit {@model.name.titleize}</h4>
|
39
|
+
<a className="btn btn-danger pull-right" href="#" onClick={@cancel}><i className="fa fa-close" /></a>
|
40
|
+
</div>
|
41
|
+
<div className="panel-body">
|
42
|
+
<form className="form-horizontal" onSubmit={@submit}>
|
43
|
+
{@form.render()}
|
44
|
+
<div className="col-sm-offset-2 col-sm-10">
|
45
|
+
<input type="submit" name="commit" value="Update #{@model.name.titleize}" className="btn btn-primary btn-disabled" />
|
46
|
+
</div>
|
47
|
+
</form>
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
</div>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Component.create "Form.Field.Checkbox",
|
2
|
+
render: ->
|
3
|
+
<div className="checkbox">
|
4
|
+
<label>
|
5
|
+
<input type="checkbox" name={@props.name} id={@props.name} checked={@props.value(@props.name)} onChange={@props.onChange.bind(null, @props.name)} />
|
6
|
+
<span dangerouslySetInnerHTML={{__html: @props.label}} />
|
7
|
+
</label>
|
8
|
+
</div>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Component.create "Form.Field.Markdown",
|
2
|
+
render: ->
|
3
|
+
<div>
|
4
|
+
<p className="form-control-static">
|
5
|
+
You can use <a href="https://guides.github.com/features/mastering-markdown/#examples" target="_blank">markdown syntax</a> to bedazzle the content
|
6
|
+
</p>
|
7
|
+
<textarea placeholder="## Example Markdown\n\nTry to **be bold** or *maybe italic*.\n\nWrite a link [to somewhere](http://tomify.me) or even embed an image with ![Some alt text](http://tomify.me/logo.jpg)." rows=5 className="form-control" type="text" name={@props.name} id={@props.name} value={@props.value(@props.name)} onChange={@props.onChange.bind(null, @props.name)} />
|
8
|
+
<p className="help-block">Drag the bottom right corner down to enlarge the box</p>
|
9
|
+
</div>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Component.create "Form.Field.Select",
|
2
|
+
render: ->
|
3
|
+
<select className="form-control" name={@props.name} id={@props.name} value={@props.value(@props.name)} onChange={@props.onChange.bind(null, @props.name)}>
|
4
|
+
{<option key="none">None</option> if @props.allow_blank}
|
5
|
+
{for option in @props.options?() || @props.options
|
6
|
+
<option key={option.value} value={option.value}>{option.name}</option>
|
7
|
+
}
|
8
|
+
</select>
|