tomify 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +19 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/images/tomify/gifs/basketball.gif +0 -0
  6. data/app/assets/images/tomify/gifs/laughing.gif +0 -0
  7. data/app/assets/images/tomify/gifs/sneer.gif +0 -0
  8. data/app/assets/images/tomify/gifs/wind.gif +0 -0
  9. data/app/assets/images/tomify/gifs/woods.gif +0 -0
  10. data/app/assets/javascripts/tomify/default/array.coffee +4 -0
  11. data/app/assets/javascripts/tomify/default/jquery.patch.coffee +9 -0
  12. data/app/assets/javascripts/tomify/default/object.coffee +4 -0
  13. data/app/assets/javascripts/tomify/default/string.coffee +40 -0
  14. data/app/assets/javascripts/tomify/dynamic/classes/component.coffee +7 -0
  15. data/app/assets/javascripts/tomify/dynamic/classes/field.coffee +24 -0
  16. data/app/assets/javascripts/tomify/dynamic/classes/form.coffee +72 -0
  17. data/app/assets/javascripts/tomify/dynamic/classes/model.coffee +41 -0
  18. data/app/assets/javascripts/tomify/dynamic/classes/namespace.coffee +24 -0
  19. data/app/assets/javascripts/tomify/dynamic/classes/observer.coffee +22 -0
  20. data/app/assets/javascripts/tomify/dynamic/classes/request.coffee +26 -0
  21. data/app/assets/javascripts/tomify/dynamic/classes/store.coffee +37 -0
  22. data/app/assets/javascripts/tomify/dynamic/global.coffee +16 -0
  23. data/app/assets/javascripts/tomify/dynamic/react/components/admin/pages.coffee.erb +30 -0
  24. data/app/assets/javascripts/tomify/dynamic/react/components/admin/settings.coffee +27 -0
  25. data/app/assets/javascripts/tomify/dynamic/react/components/admin/sidebars.coffee +16 -0
  26. data/app/assets/javascripts/tomify/dynamic/react/components/admin/uploads.coffee +13 -0
  27. data/app/assets/javascripts/tomify/dynamic/react/components/admin/users.coffee +18 -0
  28. data/app/assets/javascripts/tomify/dynamic/react/components/edit.coffee +51 -0
  29. data/app/assets/javascripts/tomify/dynamic/react/components/fields/checkbox.coffee +8 -0
  30. data/app/assets/javascripts/tomify/dynamic/react/components/fields/default.coffee +3 -0
  31. data/app/assets/javascripts/tomify/dynamic/react/components/fields/file.coffee +3 -0
  32. data/app/assets/javascripts/tomify/dynamic/react/components/fields/markdown.coffee +9 -0
  33. data/app/assets/javascripts/tomify/dynamic/react/components/fields/select.coffee +8 -0
  34. data/app/assets/javascripts/tomify/dynamic/react/components/fields/textarea.coffee +3 -0
  35. data/app/assets/javascripts/tomify/dynamic/react/components/index.coffee +87 -0
  36. data/app/assets/javascripts/tomify/dynamic/react/components/layout/admin_navbar.coffee +29 -0
  37. data/app/assets/javascripts/tomify/dynamic/react/components/layout/analytics.coffee +34 -0
  38. data/app/assets/javascripts/tomify/dynamic/react/components/layout/footer.coffee +11 -0
  39. data/app/assets/javascripts/tomify/dynamic/react/components/layout/header.coffee +28 -0
  40. data/app/assets/javascripts/tomify/dynamic/react/components/layout/messages.coffee +18 -0
  41. data/app/assets/javascripts/tomify/dynamic/react/components/layout/navbar.coffee +6 -0
  42. data/app/assets/javascripts/tomify/dynamic/react/components/layout/public_navbar.coffee +56 -0
  43. data/app/assets/javascripts/tomify/dynamic/react/components/new.coffee +48 -0
  44. data/app/assets/javascripts/tomify/dynamic/react/components/pagination.coffee +29 -0
  45. data/app/assets/javascripts/tomify/dynamic/react/components/public/passwords/new.coffee +38 -0
  46. data/app/assets/javascripts/tomify/dynamic/react/components/public/sessions/new.coffee +40 -0
  47. data/app/assets/javascripts/tomify/dynamic/react/components/public/sessions/show.coffee +35 -0
  48. data/app/assets/javascripts/tomify/dynamic/react/components/public/users/edit.coffee +42 -0
  49. data/app/assets/javascripts/tomify/dynamic/react/components/public/users/new.coffee +35 -0
  50. data/app/assets/javascripts/tomify/dynamic/react/mixins/follow.coffee +51 -0
  51. data/app/assets/javascripts/tomify/dynamic/react/mixins/will-initialize.coffee +7 -0
  52. data/app/assets/javascripts/tomify/dynamic/react/mount.coffee +9 -0
  53. data/app/assets/javascripts/tomify/turbolinks/compatibility.coffee +29 -0
  54. data/app/assets/javascripts/tomify/turbolinks/react.coffee +5 -0
  55. data/app/assets/javascripts/tomify-dynamic.js +8 -0
  56. data/app/assets/javascripts/tomify.js +1 -0
  57. data/app/assets/stylesheets/_tomify.scss +1 -0
  58. data/app/assets/stylesheets/tomify/_default.scss +26 -0
  59. data/app/assets/stylesheets/tomify/_footer.scss +1 -0
  60. data/app/assets/stylesheets/tomify/_navbar.scss +43 -0
  61. data/app/assets/stylesheets/tomify/_page.scss +31 -0
  62. data/app/assets/stylesheets/tomify/_pagination.scss +5 -0
  63. data/app/assets/stylesheets/tomify/_variables.scss +10 -0
  64. data/app/controllers/tomify/admin/controller.rb +7 -0
  65. data/app/controllers/tomify/admin/pages_controller.rb +2 -0
  66. data/app/controllers/tomify/admin/settings_controller.rb +2 -0
  67. data/app/controllers/tomify/admin/sidebars_controller.rb +2 -0
  68. data/app/controllers/tomify/admin/uploads_controller.rb +2 -0
  69. data/app/controllers/tomify/admin/users_controller.rb +2 -0
  70. data/app/controllers/tomify/api/admin/controller.rb +3 -0
  71. data/app/controllers/tomify/api/admin/pages_controller.rb +2 -0
  72. data/app/controllers/tomify/api/admin/settings_controller.rb +2 -0
  73. data/app/controllers/tomify/api/admin/sidebars_controller.rb +2 -0
  74. data/app/controllers/tomify/api/admin/uploads_controller.rb +2 -0
  75. data/app/controllers/tomify/api/admin/users_controller.rb +7 -0
  76. data/app/controllers/tomify/api/public/controller.rb +3 -0
  77. data/app/controllers/tomify/api/public/passwords_controller.rb +10 -0
  78. data/app/controllers/tomify/api/public/sessions_controller.rb +19 -0
  79. data/app/controllers/tomify/api/public/users_controller.rb +25 -0
  80. data/app/controllers/tomify/concerns/api/admin.rb +9 -0
  81. data/app/controllers/tomify/concerns/api/helpers.rb +57 -0
  82. data/app/controllers/tomify/concerns/api/json.rb +46 -0
  83. data/app/controllers/tomify/concerns/api/public.rb +9 -0
  84. data/app/controllers/tomify/concerns/default/auth_helper.rb +22 -0
  85. data/app/controllers/tomify/concerns/default/env_helper.rb +34 -0
  86. data/app/controllers/tomify/concerns/default/helper.rb +13 -0
  87. data/app/controllers/tomify/concerns/default/react_helper.rb +7 -0
  88. data/app/controllers/tomify/concerns/default.rb +17 -0
  89. data/app/controllers/tomify/public/controller.rb +2 -0
  90. data/app/controllers/tomify/public/pages_controller.rb +22 -0
  91. data/app/controllers/tomify/public/sessions_controller.rb +5 -0
  92. data/app/controllers/tomify/public/users_controller.rb +7 -0
  93. data/app/controllers/tomify_controller.rb +3 -0
  94. data/app/helpers/tomify/carrierwave_helper.rb +17 -0
  95. data/app/helpers/tomify/email_helper.rb +19 -0
  96. data/app/helpers/tomify/render_helper.rb +13 -0
  97. data/app/helpers/tomify/timezone_helper.rb +5 -0
  98. data/app/mailers/tomify/user_mailer.rb +13 -0
  99. data/app/mailers/tomify_mailer.rb +14 -0
  100. data/app/models/tomify/concerns/page.rb +74 -0
  101. data/app/models/tomify/concerns/sidebar.rb +18 -0
  102. data/app/models/tomify/concerns/token.rb +18 -0
  103. data/app/models/tomify/concerns/upload.rb +36 -0
  104. data/app/models/tomify/concerns/user.rb +52 -0
  105. data/app/models/tomify/page.rb +3 -0
  106. data/app/models/tomify/setting/boolean.rb +11 -0
  107. data/app/models/tomify/setting/json.rb +11 -0
  108. data/app/models/tomify/setting/text.rb +3 -0
  109. data/app/models/tomify/setting/uploader.rb +6 -0
  110. data/app/models/tomify/setting.rb +48 -0
  111. data/app/models/tomify/sidebar.rb +3 -0
  112. data/app/models/tomify/token.rb +3 -0
  113. data/app/models/tomify/upload.rb +3 -0
  114. data/app/models/tomify/user.rb +3 -0
  115. data/app/models/tomify_record.rb +33 -0
  116. data/app/uploaders/tomify/file_uploader.rb +5 -0
  117. data/app/uploaders/tomify/setting_uploader.rb +7 -0
  118. data/app/uploaders/tomify_uploader.rb +20 -0
  119. data/app/views/templates/contact.haml +24 -0
  120. data/app/views/templates/default.haml +4 -0
  121. data/app/views/tomify/defaults/_body.haml +8 -0
  122. data/app/views/tomify/defaults/_container.haml +3 -0
  123. data/app/views/tomify/defaults/_env.haml +2 -0
  124. data/app/views/tomify/defaults/_head.haml +14 -0
  125. data/app/views/tomify/defaults/_meta.haml +17 -0
  126. data/app/views/tomify/defaults/_sharing.haml +23 -0
  127. data/app/views/tomify/layouts/application.haml +4 -0
  128. data/app/views/tomify/layouts/mailer.haml +7 -0
  129. data/app/views/tomify/mailers/user_mailer/invite.haml +13 -0
  130. data/app/views/tomify/mailers/user_mailer/reset_password.haml +17 -0
  131. data/config/initializers/assets.rb +7 -0
  132. data/config/initializers/database.rb +3 -0
  133. data/config/initializers/flash_patch.rb +15 -0
  134. data/config/initializers/mailer.rb +4 -0
  135. data/config/initializers/renderers.rb +4 -0
  136. data/config/initializers/settings.rb +5 -0
  137. data/config/routes.rb +38 -0
  138. data/db/migrate/19900000000000_create_users.rb +13 -0
  139. data/db/migrate/19900000000001_create_tokens.rb +10 -0
  140. data/db/migrate/19900000000002_create_settings.rb +13 -0
  141. data/db/migrate/19900000000003_create_uploads.rb +13 -0
  142. data/db/migrate/19900000000004_create_pages.rb +22 -0
  143. data/db/migrate/19900000000005_create_sidebars.rb +12 -0
  144. data/db/seeds.rb +43 -0
  145. data/lib/generators/tomify/bundle/bundle_generator.rb +12 -0
  146. data/lib/generators/tomify/bundle/templates/default.js +2 -0
  147. data/lib/generators/tomify/bundle/templates/react.js +3 -0
  148. data/lib/previews/tomify/user_preview.rb +9 -0
  149. data/lib/previews/tomify_preview.rb +6 -0
  150. data/lib/tasks/package.rake +40 -0
  151. data/lib/tomify/constantly.rb +12 -0
  152. data/lib/tomify/engine.rb +11 -0
  153. data/lib/tomify/version.rb +3 -0
  154. data/lib/tomify.rb +40 -0
  155. data/vendor/tomify/development/react.js +20494 -0
  156. data/vendor/tomify/production/react.js +20352 -0
  157. 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
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+ require "bundler/gem_tasks"
3
+
4
+ Dir.glob("lib/tasks/**/*.rake").each { |r| import r }
5
+
6
+ task default: "tomify:package"
@@ -0,0 +1,4 @@
1
+ Object.defineProperties Array.prototype, {
2
+ first: { get: -> @[0] },
3
+ last: { get: -> @[@length - 1] }
4
+ }
@@ -0,0 +1,9 @@
1
+ for method in ["put", "delete"]
2
+ do (method) ->
3
+ $[method] = (url, data, callback, type) ->
4
+ if $.isFunction(data)
5
+ type = type || callback
6
+ callback = data
7
+ data = undefined
8
+
9
+ $.ajax(url: url, type: method, dataType: type, data: data, success: callback)
@@ -0,0 +1,4 @@
1
+ @BuildObject = (key, value) ->
2
+ object = {}
3
+ object[key] = value
4
+ object
@@ -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,3 @@
1
+ Component.create "Form.Field.Default",
2
+ render: ->
3
+ <input placeholder={@props.name.titleize} className="form-control" type={@props.type} name={@props.name} id={@props.name} value={@props.value(@props.name)} onChange={@props.onChange.bind(null, @props.name)} />
@@ -0,0 +1,3 @@
1
+ Component.create "Form.Field.File",
2
+ render: ->
3
+ <input placeholder={@props.name.titleize} className="form-control" type="file" name={@props.name} id={@props.name} onChange={@props.onChange.bind(null, @props.name)} />
@@ -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>
@@ -0,0 +1,3 @@
1
+ Component.create "Form.Field.Textarea",
2
+ render: ->
3
+ <textarea placeholder={@props.name.titleize} className="form-control" type="text" name={@props.name} id={@props.name} value={@props.value(@props.name)} onChange={@props.onChange.bind(null, @props.name)} />