loft 0.2.9 → 0.3.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/loft.coffee +4 -1
- data/app/assets/javascripts/loft/asset-item.coffee +2 -17
- data/app/assets/javascripts/loft/group-actions.coffee +4 -21
- data/app/assets/javascripts/loft/inputs/loft-image.coffee +37 -38
- data/app/assets/javascripts/loft/module.coffee +57 -123
- data/app/assets/stylesheets/inputs/loft-image.scss +46 -5
- data/app/assets/stylesheets/loft.scss +109 -104
- data/app/controllers/admin/assets_controller.rb +7 -3
- data/app/models/concerns/loft_asset.rb +3 -26
- data/lib/loft.rb +10 -8
- data/lib/loft/routing.rb +7 -0
- data/lib/loft/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfbd343f5dbe51c685e42a35545b11ba6cfc9a75
|
4
|
+
data.tar.gz: d170f5aae6bf0409f13384c08a5f681fa3b0a271
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97cbd7ec0cf1be3aa5dcfeeed783771992fc0fa9aa6ab1eec0325924be97f4936130fd9ede409c1370195cd2855156359a10246cc6e163877c51399d5e5f409c
|
7
|
+
data.tar.gz: 2ac7ed3b842e923257cb9a3dc5950b825f9f5768f7f73ca6939364477ccf3993099ca9064fcc10a024b9d794a45eca9bbf2063408e97c3c74578b815b268b5ed
|
@@ -7,5 +7,8 @@
|
|
7
7
|
## OPTIONAL
|
8
8
|
# require loft/redactor-loft
|
9
9
|
|
10
|
+
@Icons.upload = "<i class='fa fa-cloud-upload'></i>"
|
11
|
+
@Icons.remove = "<i class='fa fa-close'></i>"
|
12
|
+
|
10
13
|
# TODOs:
|
11
|
-
# - refactor group remove action: delete a bunch first, then reload the page
|
14
|
+
# - refactor group remove action: delete a bunch first, then reload the page
|
@@ -1,11 +1,6 @@
|
|
1
1
|
# -----------------------------------------------------------------------------
|
2
2
|
# Author: Alexander Kravets <alex@slatestudio.com>,
|
3
3
|
# Slate Studio (http://www.slatestudio.com)
|
4
|
-
#
|
5
|
-
# Coding Guide:
|
6
|
-
# https://github.com/thoughtbot/guides/tree/master/style/coffeescript
|
7
|
-
# -----------------------------------------------------------------------------
|
8
|
-
|
9
4
|
# -----------------------------------------------------------------------------
|
10
5
|
# Loft Asset Item
|
11
6
|
# -----------------------------------------------------------------------------
|
@@ -14,8 +9,7 @@ class @LoftAssetItem extends Item
|
|
14
9
|
@$el =$ "<div class='item asset asset-#{ @object.type }' data-id='#{ @object._id }'></div>"
|
15
10
|
@render()
|
16
11
|
|
17
|
-
|
18
|
-
# PRIVATE ===============================================
|
12
|
+
# PRIVATE ===================================================================
|
19
13
|
|
20
14
|
_bind_name_input: ->
|
21
15
|
@$nameInput.on 'blur', (e) => @_update_name_if_changed()
|
@@ -23,18 +17,15 @@ class @LoftAssetItem extends Item
|
|
23
17
|
if e.keyCode == 13 then $(e.target).blur()
|
24
18
|
if e.keyCode == 27 then @_cancel_name_change()
|
25
19
|
|
26
|
-
|
27
20
|
_edit_name: (e) ->
|
28
21
|
@$el.addClass('edit-name')
|
29
22
|
@$nameInput.focus().select()
|
30
23
|
|
31
|
-
|
32
24
|
_cancel_name_change: ->
|
33
25
|
@$el.removeClass('edit-name')
|
34
26
|
name = @$title.html()
|
35
27
|
@$nameInput.val(name)
|
36
28
|
|
37
|
-
|
38
29
|
_update_name_if_changed: ->
|
39
30
|
@$el.removeClass('edit-name')
|
40
31
|
name = @$nameInput.val()
|
@@ -46,8 +37,7 @@ class @LoftAssetItem extends Item
|
|
46
37
|
onSuccess: (object) =>
|
47
38
|
onError: (errors) => # process errors
|
48
39
|
|
49
|
-
|
50
|
-
# PUBLIC ================================================
|
40
|
+
# PUBLIC ====================================================================
|
51
41
|
|
52
42
|
render: ->
|
53
43
|
@$el.html('').removeClass('item-folder has-subtitle has-thumbnail')
|
@@ -72,7 +62,6 @@ class @LoftAssetItem extends Item
|
|
72
62
|
@$checkbox.append(@$checkboxInput)
|
73
63
|
@$el.prepend(@$checkbox)
|
74
64
|
|
75
|
-
|
76
65
|
# input for assets name
|
77
66
|
name = @$title.text()
|
78
67
|
@$name =$ "<div class='asset-name'></div>"
|
@@ -83,7 +72,3 @@ class @LoftAssetItem extends Item
|
|
83
72
|
|
84
73
|
# handler for asset name change on title click
|
85
74
|
@$title.on 'click', (e) => @_edit_name(e)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
@@ -1,11 +1,6 @@
|
|
1
1
|
# -----------------------------------------------------------------------------
|
2
2
|
# Author: Alexander Kravets <alex@slatestudio.com>,
|
3
3
|
# Slate Studio (http://www.slatestudio.com)
|
4
|
-
#
|
5
|
-
# Coding Guide:
|
6
|
-
# https://github.com/thoughtbot/guides/tree/master/style/coffeescript
|
7
|
-
# -----------------------------------------------------------------------------
|
8
|
-
|
9
4
|
# -----------------------------------------------------------------------------
|
10
5
|
# Loft Group Actions
|
11
6
|
# -----------------------------------------------------------------------------
|
@@ -14,8 +9,7 @@ class @LoftGroupActions
|
|
14
9
|
@_render()
|
15
10
|
@_bind_checkboxes()
|
16
11
|
|
17
|
-
|
18
|
-
# PRIVATE ===============================================
|
12
|
+
# PRIVATE ===================================================================
|
19
13
|
|
20
14
|
_render: ->
|
21
15
|
@$el =$ "<div class='assets-group-actions' style='display:none;'></div>"
|
@@ -36,7 +30,6 @@ class @LoftGroupActions
|
|
36
30
|
@$unselectBtn.on 'click', (e) => e.preventDefault(); @_unselect_list_items()
|
37
31
|
@$el.append @$unselectBtn
|
38
32
|
|
39
|
-
|
40
33
|
_bind_checkboxes: ->
|
41
34
|
@list.$el.on 'click', '.asset .asset-checkbox input', (e) =>
|
42
35
|
# when multiple selection disabled select only one asset a time
|
@@ -49,22 +42,19 @@ class @LoftGroupActions
|
|
49
42
|
else
|
50
43
|
@hide()
|
51
44
|
|
52
|
-
|
53
45
|
_select_single_item: ($checkbox) ->
|
54
46
|
if $checkbox.prop('checked')
|
55
47
|
@list.$el.find('.asset .asset-checkbox input:checked').prop('checked' , false)
|
56
48
|
$checkbox.prop('checked', true)
|
57
49
|
|
58
|
-
|
59
50
|
_selected_list_items: ->
|
60
|
-
$.map @list.$el.find('.asset .asset-checkbox input:checked'), (checkbox) ->
|
61
|
-
|
51
|
+
$.map @list.$el.find('.asset .asset-checkbox input:checked'), (checkbox) ->
|
52
|
+
$(checkbox).parent().parent()
|
62
53
|
|
63
54
|
_unselect_list_items: ->
|
64
55
|
@list.$el.find('.asset .asset-checkbox input').prop('checked', false)
|
65
56
|
@hide()
|
66
57
|
|
67
|
-
|
68
58
|
_delete_selected_list_items: ->
|
69
59
|
if confirm("Are you sure?")
|
70
60
|
$selectedItems = @_selected_list_items()
|
@@ -79,7 +69,6 @@ class @LoftGroupActions
|
|
79
69
|
onError: => # error notification
|
80
70
|
@hide()
|
81
71
|
|
82
|
-
|
83
72
|
_accept_selected_items: ->
|
84
73
|
$selectedItems = @_selected_list_items()
|
85
74
|
|
@@ -96,16 +85,10 @@ class @LoftGroupActions
|
|
96
85
|
else
|
97
86
|
@loft.onAcceptCallback(objects, => @loft.closeModal())
|
98
87
|
|
99
|
-
|
100
88
|
_show: ->
|
101
89
|
@$el.show()
|
102
90
|
|
103
|
-
|
104
|
-
# PUBLIC ================================================
|
91
|
+
# PUBLIC ====================================================================
|
105
92
|
|
106
93
|
hide: ->
|
107
94
|
@$el.hide()
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
@@ -1,36 +1,41 @@
|
|
1
1
|
# -----------------------------------------------------------------------------
|
2
2
|
# Author: Alexander Kravets <alex@slatestudio.com>,
|
3
3
|
# Slate Studio (http://www.slatestudio.com)
|
4
|
-
#
|
5
|
-
# Coding Guide:
|
6
|
-
# https://github.com/thoughtbot/guides/tree/master/style/coffeescript
|
7
|
-
# -----------------------------------------------------------------------------
|
8
|
-
|
9
4
|
# -----------------------------------------------------------------------------
|
10
5
|
# INPUT LOFT IMAGE
|
11
6
|
# -----------------------------------------------------------------------------
|
12
7
|
class @InputLoftImage extends InputString
|
13
8
|
|
14
|
-
# PRIVATE
|
9
|
+
# PRIVATE ===================================================================
|
15
10
|
|
16
11
|
_add_input: ->
|
17
|
-
@config.placeholder ?= 'Image
|
12
|
+
@config.placeholder ?= 'Image URL'
|
13
|
+
|
14
|
+
type = "string"
|
15
|
+
if @config.fullsizePreview
|
16
|
+
@$el.addClass "fullsize-preview"
|
17
|
+
type = "hidden"
|
18
18
|
|
19
|
-
@$input =$ "<input type='
|
19
|
+
@$input =$ """<input type='#{type}'
|
20
|
+
name='#{ @name }'
|
21
|
+
value='#{ @_safe_value() }'
|
22
|
+
id='#{ @name }' />"""
|
20
23
|
@$el.append @$input
|
21
24
|
@$input.on 'change', (e) =>
|
22
25
|
@updateValue($(e.target).val())
|
23
26
|
|
24
|
-
@
|
27
|
+
if @config.fullsizePreview
|
28
|
+
@_update_preview_background()
|
29
|
+
else
|
30
|
+
@_add_image()
|
31
|
+
@_update_image()
|
32
|
+
|
25
33
|
@_add_actions()
|
26
34
|
@_update_input_class()
|
27
35
|
|
28
|
-
|
29
36
|
_add_image: ->
|
30
37
|
@$image =$ "<a href='' target='_blank' class='image'><img src='' /></a>"
|
31
38
|
@$el.append @$image
|
32
|
-
@_update_image()
|
33
|
-
|
34
39
|
|
35
40
|
_add_actions: ->
|
36
41
|
@$actions =$ "<span class='input-actions'></span>"
|
@@ -39,57 +44,51 @@ class @InputLoftImage extends InputString
|
|
39
44
|
@_add_choose_button()
|
40
45
|
@_add_remove_button()
|
41
46
|
|
42
|
-
|
43
47
|
_add_choose_button: ->
|
44
|
-
@$chooseBtn =$ "<
|
48
|
+
@$chooseBtn =$ "<button class='choose'>#{Icons.upload}</button>"
|
45
49
|
@$actions.append @$chooseBtn
|
46
50
|
|
47
|
-
@_update_choose_button_title()
|
48
|
-
|
49
51
|
@$chooseBtn.on 'click', (e) =>
|
50
|
-
e.preventDefault()
|
51
52
|
chr.modules.loft.showModal 'images', false, (objects) =>
|
52
53
|
asset = objects[0]
|
53
54
|
@updateValue(asset.file.url)
|
54
55
|
|
55
|
-
|
56
56
|
_add_remove_button: ->
|
57
|
-
@$removeBtn =$ "<
|
57
|
+
@$removeBtn =$ "<button class='remove'>#{Icons.remove}</button>"
|
58
58
|
@$actions.append @$removeBtn
|
59
59
|
|
60
60
|
@$removeBtn.on 'click', (e) =>
|
61
|
-
e.preventDefault()
|
62
61
|
if confirm('Are you sure?')
|
63
62
|
@updateValue('')
|
64
63
|
|
64
|
+
_update_preview_background: ->
|
65
|
+
url = @value
|
66
|
+
@$el.css { "background-image": "url(#{url})" }
|
65
67
|
|
66
68
|
_update_image: ->
|
67
69
|
url = @value
|
68
|
-
@$image.attr('href',
|
69
|
-
if
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
title = if @value == '' then 'Choose or upload an image' else 'Choose other or upload'
|
74
|
-
@$chooseBtn.html(title)
|
75
|
-
|
70
|
+
@$image.attr('href', url).children().attr('src', url)
|
71
|
+
if url == ''
|
72
|
+
@$image.hide()
|
73
|
+
else
|
74
|
+
@$image.show()
|
76
75
|
|
77
76
|
_update_input_class: ->
|
78
|
-
if @value == ''
|
77
|
+
if @value == ''
|
78
|
+
@$el.removeClass('has-value')
|
79
|
+
else
|
80
|
+
@$el.addClass('has-value')
|
79
81
|
|
80
|
-
|
81
|
-
# PUBLIC ================================================
|
82
|
+
# PUBLIC ====================================================================
|
82
83
|
|
83
84
|
updateValue: (@value) ->
|
84
85
|
@$input.val(@value)
|
85
86
|
|
86
|
-
@
|
87
|
-
|
88
|
-
|
87
|
+
if @config.fullsizePreview
|
88
|
+
@_update_preview_background()
|
89
|
+
else
|
90
|
+
@_update_image()
|
89
91
|
|
92
|
+
@_update_input_class()
|
90
93
|
|
91
94
|
chr.formInputs['loft-image'] = InputLoftImage
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
@@ -1,135 +1,84 @@
|
|
1
1
|
# -----------------------------------------------------------------------------
|
2
2
|
# Author: Alexander Kravets <alex@slatestudio.com>,
|
3
3
|
# Slate Studio (http://www.slatestudio.com)
|
4
|
-
#
|
5
|
-
# Coding Guide:
|
6
|
-
# https://github.com/thoughtbot/guides/tree/master/style/coffeescript
|
7
|
-
# -----------------------------------------------------------------------------
|
8
|
-
|
9
4
|
# -----------------------------------------------------------------------------
|
10
5
|
# Loft
|
11
6
|
# -----------------------------------------------------------------------------
|
12
|
-
#
|
13
7
|
# Public methods:
|
14
8
|
# new Loft(title, resource, resourcePath)
|
15
9
|
# showModal(assetType, @selectMultipleAssets, @onAcceptCallback, @closeOnAccept)
|
16
10
|
# closeModal()
|
17
|
-
#
|
18
11
|
# -----------------------------------------------------------------------------
|
19
12
|
class @Loft
|
20
|
-
constructor: (title=
|
13
|
+
constructor: (title="Media", resource="asset", resourcePath="/admin/assets") ->
|
21
14
|
@module = {}
|
22
15
|
@store = {}
|
16
|
+
@_uploadsCounter = 0
|
17
|
+
|
18
|
+
@title = title
|
19
|
+
@menuIcon = "cloud-upload"
|
23
20
|
|
24
|
-
@
|
25
|
-
@
|
26
|
-
resource:
|
27
|
-
path:
|
28
|
-
sortBy:
|
21
|
+
@itemClass = LoftAssetItem
|
22
|
+
@arrayStore = new RailsArrayStore
|
23
|
+
resource: resource
|
24
|
+
path: resourcePath
|
25
|
+
sortBy: "created_at"
|
29
26
|
sortReverse: true
|
30
27
|
searchable: true
|
31
28
|
|
32
|
-
@
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
menuIcon: 'cloud-upload'
|
37
|
-
showNestedListsAside: true
|
38
|
-
items:
|
39
|
-
loft_all: @_nested_list_config 'All'
|
40
|
-
loft_images: @_nested_list_config 'Images', 'image'
|
41
|
-
loft_text: @_nested_list_config 'Text', 'text'
|
42
|
-
loft_archives: @_nested_list_config 'Archives', 'archive'
|
43
|
-
loft_audio: @_nested_list_config 'Audio', 'audio'
|
44
|
-
loft_video: @_nested_list_config 'Video', 'video'
|
45
|
-
loft_other: @_nested_list_config 'Other', 'other'
|
29
|
+
@listTabs =
|
30
|
+
"All": {}
|
31
|
+
"Images": { images: true }
|
32
|
+
"Documents": { not_images: true }
|
46
33
|
|
47
|
-
|
48
|
-
|
34
|
+
@onListInit = (list) =>
|
35
|
+
@_add_upload_button(list)
|
36
|
+
@_add_group_actions(list)
|
37
|
+
@_add_mode_switch(list)
|
49
38
|
|
50
|
-
|
39
|
+
@onListShow = (list) =>
|
40
|
+
@_clear_assets_selection(list)
|
51
41
|
|
42
|
+
@onModuleInit = (module) =>
|
43
|
+
@_initialize_module(module)
|
52
44
|
|
53
|
-
# PRIVATE
|
45
|
+
# PRIVATE ===================================================================
|
54
46
|
|
55
|
-
_initialize_module: (module) ->
|
56
|
-
@
|
57
|
-
@store
|
58
|
-
@nestedLists = @module.nestedLists
|
59
|
-
moduleName = @module.name
|
60
|
-
firstNestedListPath = _firstNonEmptyValue(@nestedLists).path
|
47
|
+
_initialize_module: (@module) ->
|
48
|
+
@selectMultipleAssets = true
|
49
|
+
@store = @module.rootList.config.arrayStore
|
61
50
|
|
62
|
-
# API method
|
63
51
|
@module.showModal = (assetType, selectMultipleAssets, callback, closeOnAccept) =>
|
64
52
|
@showModal(assetType, selectMultipleAssets, callback, closeOnAccept)
|
65
|
-
@selectMultipleAssets = true
|
66
53
|
|
67
|
-
|
54
|
+
@_add_close_button()
|
55
|
+
@_enable_grid_mode()
|
56
|
+
|
57
|
+
_add_close_button: ->
|
68
58
|
@module.rootList.$modalCloseBtn =$ """<a href='#' class='modal-close'>
|
69
59
|
<i class='fa fa-times'></i>
|
70
60
|
</a>"""
|
71
61
|
@module.rootList.$header.prepend @module.rootList.$modalCloseBtn
|
72
|
-
@module.rootList.$modalCloseBtn.on
|
73
|
-
|
74
|
-
|
75
|
-
@module.rootList.$items.on 'click', 'a', (e) =>
|
76
|
-
if @module.$el.hasClass 'module-modal'
|
77
|
-
e.preventDefault()
|
78
|
-
|
79
|
-
$item = $(e.currentTarget)
|
80
|
-
listName = $item.attr('href').split('/')[2]
|
62
|
+
@module.rootList.$modalCloseBtn.on "click", (e) =>
|
63
|
+
e.preventDefault()
|
64
|
+
@closeModal()
|
81
65
|
|
82
|
-
|
83
|
-
@module.showList(listName)
|
84
|
-
@module.activeList.updateItems()
|
85
|
-
|
86
|
-
$item.parent().children('.active').removeClass('active')
|
87
|
-
$item.addClass('active')
|
88
|
-
|
89
|
-
# enable grid mode as default on desktop/tablet
|
66
|
+
_enable_grid_mode: ->
|
90
67
|
if ! chr.isMobile()
|
91
|
-
@module.$el.addClass
|
92
|
-
|
93
|
-
@module.$el.addClass("module-categories")
|
94
|
-
if chr.isDesktop()
|
95
|
-
chr.$mainMenu
|
96
|
-
.find(".menu-#{moduleName}")
|
97
|
-
.attr("href", firstNestedListPath)
|
68
|
+
@module.$el.addClass "grid-mode"
|
98
69
|
|
99
|
-
|
100
|
-
_nested_list_config: (moduleName, assetType) ->
|
101
|
-
storeConfig = {}
|
102
|
-
$.extend(storeConfig, @arrayStoreConfig)
|
103
|
-
|
104
|
-
if assetType
|
105
|
-
$.extend(storeConfig, { urlParams: { by_type: assetType } })
|
106
|
-
|
107
|
-
config =
|
108
|
-
title: moduleName
|
109
|
-
showWithParent: true
|
110
|
-
itemClass: LoftAssetItem
|
111
|
-
arrayStore: new @arrayStoreClass(storeConfig)
|
112
|
-
onListInit: (list) => @_inititialize_list(list)
|
113
|
-
onListShow: (list) => @_clear_assets_selection()
|
114
|
-
|
115
|
-
return config
|
116
|
-
|
117
|
-
|
118
|
-
_inititialize_list: (list) ->
|
119
|
-
# file input button for uploading new files
|
70
|
+
_add_upload_button: (list) ->
|
120
71
|
list.$uploadInput =$ "<input class='asset-upload' type='file' multiple='multiple' />"
|
121
72
|
list.$search.before list.$uploadInput
|
122
|
-
|
123
|
-
# file upload handler
|
124
|
-
list.$uploadInput.on 'change', (e) =>
|
73
|
+
list.$uploadInput.on "change", (e) =>
|
125
74
|
files = e.target.files
|
126
75
|
if files.length > 0
|
127
76
|
@_upload(file, list) for file in files
|
128
77
|
|
129
|
-
|
78
|
+
_add_group_actions: (list) ->
|
130
79
|
list.groupActions = new LoftGroupActions(list, this)
|
131
80
|
|
132
|
-
|
81
|
+
_add_mode_switch: (list) ->
|
133
82
|
list.$switchMode =$ """<a class='assets-switch-mode' href='#'>
|
134
83
|
<i class='fa fa-fw fa-th-large'></i>
|
135
84
|
<i class='fa fa-fw fa-th-list'></i>
|
@@ -137,12 +86,11 @@ class @Loft
|
|
137
86
|
list.$backBtn.after list.$switchMode
|
138
87
|
list.$switchMode.on 'click', (e) => e.preventDefault() ; @module.$el.toggleClass('grid-mode')
|
139
88
|
|
140
|
-
# modal back for mobiles
|
141
|
-
list.$header.on 'click', '.back', (e) =>
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
89
|
+
# # modal back for mobiles
|
90
|
+
# list.$header.on 'click', '.back', (e) =>
|
91
|
+
# if @module.$el.hasClass 'module-modal'
|
92
|
+
# e.preventDefault()
|
93
|
+
# @module.showList()
|
146
94
|
|
147
95
|
_upload: (file, list) ->
|
148
96
|
obj = {}
|
@@ -153,49 +101,35 @@ class @Loft
|
|
153
101
|
onSuccess: (object) => @_finish_file_upload(list)
|
154
102
|
onError: (errors) =>
|
155
103
|
@_finish_file_upload(list)
|
156
|
-
chr.showError(
|
157
|
-
|
104
|
+
chr.showError("Can't upload file.")
|
158
105
|
|
159
106
|
_start_file_upload: ->
|
160
107
|
@_uploadsCounter += 1
|
161
|
-
@module.$el.addClass(
|
162
|
-
|
108
|
+
@module.$el.addClass("assets-uploading")
|
163
109
|
|
164
110
|
_finish_file_upload: (list) ->
|
165
111
|
@_uploadsCounter -= 1
|
166
112
|
if @_uploadsCounter == 0
|
167
|
-
@module.$el.removeClass(
|
113
|
+
@module.$el.removeClass("assets-uploading")
|
168
114
|
|
169
115
|
# update data in list if it's not loft_all,
|
170
116
|
# in loft_all new objects are added automatically
|
171
|
-
if @module.activeList.name != 'loft_all'
|
172
|
-
|
117
|
+
# if @module.activeList.name != 'loft_all'
|
118
|
+
# @module.activeList.updateItems()
|
173
119
|
|
120
|
+
_clear_assets_selection: (list) ->
|
121
|
+
list.groupActions.hide()
|
122
|
+
list.$items.find(".asset-checkbox").prop("checked", false)
|
174
123
|
|
175
|
-
|
176
|
-
for name, list of @module.nestedLists
|
177
|
-
list.groupActions.hide()
|
178
|
-
list.$items.find('.asset-checkbox').prop('checked', false)
|
179
|
-
|
180
|
-
|
181
|
-
# PUBLIC ================================================
|
124
|
+
# PUBLIC ====================================================================
|
182
125
|
|
183
126
|
closeModal: ->
|
184
127
|
@selectMultipleAssets = true
|
185
|
-
@_clear_assets_selection()
|
186
|
-
@module.$el.removeClass(
|
128
|
+
@_clear_assets_selection(@module.activeList)
|
129
|
+
@module.$el.removeClass("module-modal")
|
187
130
|
@module.hide()
|
188
131
|
|
189
|
-
|
190
|
-
|
191
|
-
showModal: (assetType='all', @selectMultipleAssets=false, @onAcceptCallback=$.noop, @closeOnAccept=true) ->
|
192
|
-
# modal mode
|
193
|
-
@module.$el.addClass('module-modal')
|
194
|
-
# show module
|
132
|
+
showModal: (assetType="all", @selectMultipleAssets=false, @onAcceptCallback=$.noop, @closeOnAccept=true) ->
|
133
|
+
@module.$el.addClass("module-modal")
|
195
134
|
@module.show()
|
196
|
-
# show nested list
|
197
|
-
@module.showList("loft_#{ assetType }")
|
198
135
|
@module.activeList.updateItems()
|
199
|
-
# select active item
|
200
|
-
@module.rootList.$items.children().removeClass('active')
|
201
|
-
@module.rootList.$items.children("[href='#/loft/loft_#{ assetType }']").addClass('active')
|
@@ -1,8 +1,49 @@
|
|
1
1
|
.input-loft-image {
|
2
|
-
input { margin-bottom: .25em; }
|
3
|
-
.image img { max-height: 6em; margin: .25em .75em .25em 0; float: left; }
|
4
|
-
.remove { display: none; }
|
5
2
|
|
6
|
-
|
3
|
+
input { margin-bottom: .25em; }
|
4
|
+
.image img { max-height: 6em; margin: .25em .75em .25em 0; float: left; }
|
5
|
+
.remove { display: none; }
|
6
|
+
|
7
|
+
&.has-value { min-height: 11em; }
|
7
8
|
&.has-value .remove { display: inline; }
|
8
|
-
}
|
9
|
+
}
|
10
|
+
|
11
|
+
.input-loft-image.fullsize-preview {
|
12
|
+
@include no-bottom-border;
|
13
|
+
@include position(absolute, 3.3em null null 0.5em);
|
14
|
+
z-index: 1;
|
15
|
+
width: 5em;
|
16
|
+
height: 5em;
|
17
|
+
min-height: 5em;
|
18
|
+
background-color: $border-color;
|
19
|
+
background-size: cover;
|
20
|
+
padding: 0.15em;
|
21
|
+
|
22
|
+
input,
|
23
|
+
.label-title,
|
24
|
+
.error-message { display: none; }
|
25
|
+
|
26
|
+
button {
|
27
|
+
width: 2em;
|
28
|
+
line-height: 2;
|
29
|
+
text-align: center;
|
30
|
+
border: 0;
|
31
|
+
padding: 0;
|
32
|
+
border-radius: 1.75em;
|
33
|
+
margin: 0.15em;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
/* Tablet ------------------------------------------------------------------ */
|
38
|
+
@media #{$tablet} {
|
39
|
+
.input-loft-image.fullsize-preview {
|
40
|
+
padding: 0.25em;
|
41
|
+
width: 14em;
|
42
|
+
height: 14em;
|
43
|
+
|
44
|
+
button {
|
45
|
+
width: 2.5em;
|
46
|
+
line-height: 2.5;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
@@ -1,10 +1,11 @@
|
|
1
|
-
// -----------------------------------------------------------------------
|
2
|
-
// Basic styles for Loft Character CMS plugin, this should be included in
|
3
|
-
// admin.scss (default)
|
4
|
-
// -----------------------------------------------------------------------
|
5
1
|
@import "inputs/loft-image";
|
6
2
|
|
7
3
|
// MIXINS
|
4
|
+
@mixin loft-icon-base($bgSize, $width, $height) {
|
5
|
+
display: block; background-size: $bgSize; width: $width; height: $height;
|
6
|
+
background-image: image-url("loft/library@3x.png");
|
7
|
+
}
|
8
|
+
|
8
9
|
@mixin loft-icon-label($title) {
|
9
10
|
&:before {
|
10
11
|
display: block; margin-top: 2.6em;
|
@@ -14,51 +15,9 @@
|
|
14
15
|
}
|
15
16
|
}
|
16
17
|
|
17
|
-
@mixin loft-icon-base($bgSize, $width, $height) {
|
18
|
-
display: block; background-size: $bgSize; width: $width; height: $height;
|
19
|
-
background-image: image-url("loft/library@3x.png");
|
20
|
-
}
|
21
|
-
|
22
|
-
// ASSET TYPE ICONS
|
23
|
-
.loft .list:first-child .items {
|
24
|
-
.item {
|
25
|
-
.item-title { margin-left: 2.05em; }
|
26
|
-
&:before { @include position(absolute, null null null 1em); content: ''; display: block; }
|
27
|
-
}
|
28
|
-
|
29
|
-
.item {
|
30
|
-
&:before { @include loft-icon-base(32px 112px, 16px, 16px); }
|
31
|
-
&.active:before { background-position: 16px 0px; }
|
32
|
-
}
|
33
|
-
.item:nth-child(2) {
|
34
|
-
&:before { background-position: 0px -16px; }
|
35
|
-
&.active { &:before { background-position: 16px -16px; } }
|
36
|
-
}
|
37
|
-
.item:nth-child(3) {
|
38
|
-
&:before { background-position: 0px -32px; }
|
39
|
-
&.active { &:before { background-position: 16px -32px; } }
|
40
|
-
}
|
41
|
-
.item:nth-child(4) {
|
42
|
-
&:before { background-position: 0px -48px; }
|
43
|
-
&.active { &:before { background-position: 16px -48px; } }
|
44
|
-
}
|
45
|
-
.item:nth-child(5) {
|
46
|
-
&:before { background-position: 0px -64px; }
|
47
|
-
&.active { &:before { background-position: 16px -64px; } }
|
48
|
-
}
|
49
|
-
.item:nth-child(6) {
|
50
|
-
&:before { background-position: 0px -80px; }
|
51
|
-
&.active { &:before { background-position: 16px -80px; } }
|
52
|
-
}
|
53
|
-
.item:nth-child(7) {
|
54
|
-
&:before { background-position: 0px -96px; }
|
55
|
-
&.active { &:before { background-position: 16px -96px; } }
|
56
|
-
}
|
57
|
-
}
|
58
|
-
|
59
18
|
// LIST MODE
|
60
|
-
.loft .
|
61
|
-
padding-left: 6.25em;
|
19
|
+
.list.loft .items .item.asset {
|
20
|
+
padding-left: 6.25em;
|
62
21
|
|
63
22
|
// checkbox
|
64
23
|
.asset-checkbox {
|
@@ -116,7 +75,7 @@
|
|
116
75
|
.module-modal .assets-group-actions .delete { display: none; }
|
117
76
|
|
118
77
|
// UPLOAD BUTTON
|
119
|
-
.loft .
|
78
|
+
.list.loft .header:before {
|
120
79
|
@include position(absolute, 0px 0px null null);
|
121
80
|
@include header-icon-base;
|
122
81
|
display: block;
|
@@ -136,38 +95,80 @@
|
|
136
95
|
|
137
96
|
.list header .asset-upload + .search { @include position(absolute, 0 40px null null); }
|
138
97
|
.list.list-search header .asset-upload + .search { @include position(absolute, 0 0 null 0); }
|
139
|
-
.loft.assets-uploading .list
|
98
|
+
.loft.assets-uploading .list header .spinner { visibility: visible; }
|
140
99
|
|
141
100
|
// MODAL MODE
|
142
101
|
.loft {
|
143
102
|
.modal-close { display: none; }
|
144
103
|
&.module-modal {
|
145
|
-
@include position(absolute, 0 .5em 0 .5em);
|
104
|
+
@include position(absolute, 0 .5em 0 .5em);
|
105
|
+
z-index: 1100;
|
146
106
|
|
147
107
|
&:after { display: none; }
|
148
108
|
|
149
|
-
// dark background
|
150
109
|
&:before {
|
151
|
-
@include position(fixed, 0px 0px 0px 0px);
|
152
|
-
|
110
|
+
@include position(fixed, 0px 0px 0px 0px);
|
111
|
+
z-index: 0;
|
112
|
+
content: '';
|
113
|
+
display: block;
|
114
|
+
background: rgba(0, 0, 0, 0.25);
|
153
115
|
}
|
154
116
|
|
155
117
|
.modal-close {
|
156
118
|
@include header-icon-base;
|
157
|
-
@include position(absolute, null
|
119
|
+
@include position(absolute, null null null 0);
|
158
120
|
display: block;
|
121
|
+
color: $secondary-font-color;
|
122
|
+
|
123
|
+
&:hover {
|
124
|
+
color: $base-font-color;
|
125
|
+
}
|
159
126
|
}
|
160
|
-
.header { top: 0; left: .5em; right: .5em; width: auto; }
|
161
127
|
|
162
|
-
.
|
128
|
+
.header {
|
129
|
+
top: 0;
|
130
|
+
left: 0.5em;
|
131
|
+
right: 0.5em;
|
132
|
+
width: auto;
|
133
|
+
}
|
134
|
+
|
135
|
+
.back {
|
136
|
+
display: none;
|
137
|
+
}
|
163
138
|
}
|
164
139
|
}
|
165
140
|
|
166
|
-
/* Tablet
|
141
|
+
/* Tablet ----------------------------------------------------------------- */
|
167
142
|
@media #{$tablet} {
|
143
|
+
// LAYOUT
|
144
|
+
.list.loft {
|
145
|
+
width: initial;
|
146
|
+
background-color: $bg-color;
|
147
|
+
|
148
|
+
.header {
|
149
|
+
background-color: $bg-color;
|
150
|
+
}
|
151
|
+
|
152
|
+
.item {
|
153
|
+
background-color: $white-color;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
168
157
|
// ITEMS
|
169
|
-
.loft .
|
170
|
-
|
158
|
+
.list.loft .items {
|
159
|
+
font-size: 1em;
|
160
|
+
padding-top: 1.5em;
|
161
|
+
}
|
162
|
+
|
163
|
+
.list.loft .item {
|
164
|
+
@include no-bottom-border;
|
165
|
+
background-color: $white-color;
|
166
|
+
box-shadow: 0 0 1px rgba(0,0,0,0.15);
|
167
|
+
margin: 0 auto;
|
168
|
+
max-width: 44em;
|
169
|
+
}
|
170
|
+
|
171
|
+
.list.loft .items .item.asset {
|
171
172
|
padding-top: 19px; padding-bottom: 19px;
|
172
173
|
.item-title {
|
173
174
|
display: inline; cursor: pointer;
|
@@ -183,62 +184,66 @@
|
|
183
184
|
|
184
185
|
// GRID MODE
|
185
186
|
.assets-switch-mode {
|
186
|
-
@include position(absolute, null null null
|
187
|
-
display: inline
|
187
|
+
@include position(absolute, null 5.5em null null);
|
188
|
+
display: inline-block;
|
189
|
+
line-height: 2.5;
|
188
190
|
i:first-child { color: $border-color; }
|
189
|
-
i:last-child { color: rgba($base-font-color, .4); }
|
191
|
+
i:last-child { color: rgba($base-font-color, 0.4); }
|
190
192
|
}
|
193
|
+
|
191
194
|
.loft.grid-mode .assets-switch-mode {
|
192
|
-
i:first-child { color: rgba($base-font-color, .4); }
|
195
|
+
i:first-child { color: rgba($base-font-color, 0.4); }
|
193
196
|
i:last-child { color: $border-color; }
|
194
197
|
}
|
195
198
|
|
196
|
-
.loft.grid-mode {
|
197
|
-
|
198
|
-
padding: 1em .25em 5em .75em;
|
199
|
-
.item.asset { margin: 0 .75em 1.5em; float: left; display: inline-block; }
|
200
|
-
|
201
|
-
.item.asset {
|
202
|
-
padding-left: 1em; width: 222px; height: 222px;
|
203
|
-
@include no-bottom-border; border: 1px solid rgba($base-font-color, .2); border-radius: 4px;
|
199
|
+
.loft.grid-mode .list .items {
|
200
|
+
padding: 1em .25em 5em .75em;
|
204
201
|
|
205
|
-
|
206
|
-
.asset-checkbox {
|
207
|
-
top: 10px; left: 10px; width: 24px; height: 24px; background: white;
|
208
|
-
box-shadow: 0 1px 0 rgba(0,0,0,0.05),1px 0 0 rgba(0,0,0,0.05); border-bottom-right-radius: 4px;
|
209
|
-
}
|
202
|
+
.item.asset { margin: 0 .75em 1.5em; float: left; display: inline-block; }
|
210
203
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
}
|
204
|
+
.item.asset {
|
205
|
+
padding-left: 1em; width: 222px; height: 222px;
|
206
|
+
@include no-bottom-border;
|
207
|
+
box-shadow: 0 0 1px rgba(0,0,0,0.2);
|
208
|
+
border-radius: 4px;
|
217
209
|
|
218
|
-
|
219
|
-
|
210
|
+
// checkbox: position + decoration
|
211
|
+
.asset-checkbox {
|
212
|
+
top: 10px;
|
213
|
+
left: 10px;
|
214
|
+
width: 24px;
|
215
|
+
height: 24px;
|
216
|
+
background: $white-color;
|
217
|
+
box-shadow: 0 1px 0 rgba(0,0,0,0.05),1px 0 0 rgba(0,0,0,0.05);
|
218
|
+
border-bottom-right-radius: 4px;
|
219
|
+
}
|
220
220
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
221
|
+
// thumbnail
|
222
|
+
.asset-icon {
|
223
|
+
@include position(absolute, 10px null null 10px);
|
224
|
+
width: 200px; height: 150px; background: white; border-radius: 0;
|
225
|
+
&:after {
|
226
|
+
@include position(absolute, 0 0 0 0);
|
227
|
+
box-shadow: 0 0 1px rgba(0,0,0,0.2) inset;
|
228
|
+
content: '';
|
229
|
+
display: block;
|
230
|
+
}
|
225
231
|
}
|
226
232
|
|
227
|
-
|
228
|
-
.asset-
|
229
|
-
.asset-archive .asset-icon { @include loft-icon-label('Archive'); }
|
230
|
-
.asset-audio .asset-icon { @include loft-icon-label('Audio'); }
|
231
|
-
.asset-video .asset-icon { @include loft-icon-label('Video'); }
|
232
|
-
.asset-other .asset-icon { @include loft-icon-label('File'); }
|
233
|
-
}
|
234
|
-
}
|
235
|
-
}
|
233
|
+
.asset-thumbnail-small { display: none; }
|
234
|
+
.asset-thumbnail-medium { display: block; }
|
236
235
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
236
|
+
// name & subtitle
|
237
|
+
.item-title { display: block; margin-top: 9.45em; }
|
238
|
+
.item-subtitle { display: block; position: initial; margin-top: .3em; }
|
239
|
+
&.edit-name .asset-name { top: inherit; bottom: 27px; left: 10px; right: 10px; }
|
240
|
+
}
|
241
241
|
|
242
|
-
|
243
|
-
|
242
|
+
// asset type labels
|
243
|
+
.asset-text .asset-icon { @include loft-icon-label('Text'); }
|
244
|
+
.asset-archive .asset-icon { @include loft-icon-label('Archive'); }
|
245
|
+
.asset-audio .asset-icon { @include loft-icon-label('Audio'); }
|
246
|
+
.asset-video .asset-icon { @include loft-icon-label('Video'); }
|
247
|
+
.asset-other .asset-icon { @include loft-icon-label('File'); }
|
248
|
+
}
|
244
249
|
}
|
@@ -1,5 +1,9 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module Admin
|
2
|
+
class AssetsController < Admin::BaseController
|
3
|
+
mongosteen
|
3
4
|
|
4
|
-
|
5
|
+
has_scope :by_type
|
6
|
+
has_scope :images, type: :boolean
|
7
|
+
has_scope :not_images, type: :boolean
|
8
|
+
end
|
5
9
|
end
|
@@ -1,20 +1,13 @@
|
|
1
|
-
require 'autoinc'
|
2
|
-
|
3
1
|
module LoftAsset
|
4
2
|
extend ActiveSupport::Concern
|
5
|
-
|
6
3
|
included do
|
7
|
-
|
8
4
|
include Mongoid::Timestamps
|
9
5
|
include Mongoid::Autoinc
|
10
6
|
include Mongoid::Search
|
11
|
-
|
12
7
|
include Ants::Id
|
13
|
-
|
14
8
|
include ActionView::Helpers::DateHelper
|
15
9
|
include ActionView::Helpers::NumberHelper
|
16
10
|
|
17
|
-
|
18
11
|
## Attributes
|
19
12
|
field :name, default: ''
|
20
13
|
field :filename, default: ''
|
@@ -31,43 +24,36 @@ module LoftAsset
|
|
31
24
|
field :_number, type: Integer
|
32
25
|
increments :_number
|
33
26
|
|
34
|
-
|
35
27
|
## Uploaders
|
36
28
|
mount_uploader :file, AssetFileUploader
|
37
29
|
|
38
|
-
|
39
30
|
## Validations
|
40
31
|
validates :file, presence: true
|
41
32
|
|
42
|
-
|
43
33
|
## Search
|
44
34
|
search_in :name, :filename
|
45
35
|
|
46
|
-
|
47
36
|
## Scopes
|
48
|
-
default_scope
|
37
|
+
default_scope -> { desc(:created_at) }
|
49
38
|
scope :by_type, -> asset_type { where(type: asset_type) }
|
50
|
-
|
39
|
+
scope :images, -> { where(type: "image") }
|
40
|
+
scope :not_images, -> { where(:type.ne => "image" ) }
|
51
41
|
|
52
42
|
## Indexes
|
53
43
|
index({ created_at: -1 })
|
54
44
|
|
55
|
-
|
56
45
|
## Callbacks
|
57
46
|
before_save :update_asset_attributes
|
58
47
|
|
59
|
-
|
60
48
|
## Helpers
|
61
49
|
def _list_item_title
|
62
50
|
name
|
63
51
|
end
|
64
52
|
|
65
|
-
|
66
53
|
def _list_item_subtitle
|
67
54
|
time_ago_in_words(self.created_at) + " ago"
|
68
55
|
end
|
69
56
|
|
70
|
-
|
71
57
|
def _list_item_thumbnail
|
72
58
|
if is_image?
|
73
59
|
{ medium: file._200x150_2x.url, small: file._40x40_2x.url }
|
@@ -76,49 +62,41 @@ module LoftAsset
|
|
76
62
|
end
|
77
63
|
end
|
78
64
|
|
79
|
-
|
80
65
|
def content_type
|
81
66
|
@content_type ||= file.content_type
|
82
67
|
end
|
83
68
|
|
84
|
-
|
85
69
|
def is_image?
|
86
70
|
return false unless file?
|
87
71
|
content_type.match(/image\//) ? true : false
|
88
72
|
end
|
89
73
|
|
90
|
-
|
91
74
|
def is_text?
|
92
75
|
return false unless file?
|
93
76
|
content_type.match(/text\//) ? true : false
|
94
77
|
end
|
95
78
|
|
96
|
-
|
97
79
|
def is_pdf?
|
98
80
|
return false unless file?
|
99
81
|
content_type.match(/pdf/) ? true : false
|
100
82
|
end
|
101
83
|
|
102
|
-
|
103
84
|
def is_archive?
|
104
85
|
return false unless file?
|
105
86
|
# need to add more archive types: rar, gz, bz2, gzip
|
106
87
|
content_type.match(/zip/) ? true : false
|
107
88
|
end
|
108
89
|
|
109
|
-
|
110
90
|
def is_audio?
|
111
91
|
return false unless file?
|
112
92
|
content_type.match(/audio\//) ? true : false
|
113
93
|
end
|
114
94
|
|
115
|
-
|
116
95
|
def is_video?
|
117
96
|
return false unless file?
|
118
97
|
content_type.match(/video\//) ? true : false
|
119
98
|
end
|
120
99
|
|
121
|
-
|
122
100
|
def update_asset_attributes
|
123
101
|
if file.present? && file_changed?
|
124
102
|
|
@@ -144,6 +122,5 @@ module LoftAsset
|
|
144
122
|
self.name = self.name.empty? ? self.filename : self.name
|
145
123
|
end
|
146
124
|
private :update_asset_attributes
|
147
|
-
|
148
125
|
end
|
149
126
|
end
|
data/lib/loft.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
1
|
+
require "chr"
|
2
|
+
require "ants"
|
3
|
+
require "mongosteen"
|
4
|
+
require "mini_magick"
|
5
|
+
require "mongoid_search"
|
6
|
+
require "mongoid-grid_fs"
|
7
|
+
require "carrierwave/mongoid"
|
8
|
+
require "autoinc"
|
8
9
|
|
9
10
|
module Loft
|
10
11
|
class Engine < ::Rails::Engine
|
11
|
-
require
|
12
|
+
require "loft/engine"
|
13
|
+
require "loft/routing"
|
12
14
|
end
|
13
15
|
end
|
data/lib/loft/routing.rb
ADDED
data/lib/loft/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: loft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Kravets
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chr
|
@@ -181,6 +181,7 @@ files:
|
|
181
181
|
- app/uploaders/asset_file_uploader.rb
|
182
182
|
- lib/loft.rb
|
183
183
|
- lib/loft/engine.rb
|
184
|
+
- lib/loft/routing.rb
|
184
185
|
- lib/loft/version.rb
|
185
186
|
- loft.gemspec
|
186
187
|
homepage: http://slatestudio.com
|