tomify 0.0.0

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.
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)} />