forked from GetmeUK/ContentTools
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinspector.coffee
More file actions
230 lines (171 loc) · 7.36 KB
/
Copy pathinspector.coffee
File metadata and controls
230 lines (171 loc) · 7.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
class ContentTools.InspectorUI extends ContentTools.WidgetUI
# The inspector provides a breadcrumb style navigation tool for the
# currently selected element in the document and it's chain of parent
# elements.
constructor: () ->
super()
# A list of TagUI elements displayed in the inspector
@_tagUIs = []
mount: () ->
# Mount the widget to the DOM
# Inspector
@_domElement = @constructor.createDiv(['ct-widget', 'ct-inspector'])
@parent().domElement().appendChild(@_domElement)
# Tags
@_domTags = @constructor.createDiv([
'ct-inspector__tags',
'ct-tags'
])
@_domElement.appendChild(@_domTags)
# Add interaction handlers
@_addDOMEventListeners()
# Listen for changes in focus at which point we need to update the tool
@_handleFocusChange = () =>
@updateTags()
ContentEdit.Root.get().bind('blur', @_handleFocusChange)
ContentEdit.Root.get().bind('focus', @_handleFocusChange)
# We use mount here to catch instances where the tag name is changed, in
# which case the focus wont be affected but the element will be
# remounted in the DOM.
ContentEdit.Root.get().bind('mount', @_handleFocusChange)
unmount: () ->
# Unmount the widget from the DOM
super()
@_domTags = null
# Remove listeners for the inspector
ContentEdit.Root.get().unbind('blur', @_handleFocusChange)
ContentEdit.Root.get().unbind('focus', @_handleFocusChange)
ContentEdit.Root.get().unbind('mount', @_handleFocusChange)
updateTags: () ->
# Update the tags based on the current selection
element = ContentEdit.Root.get().focused()
# Clear the current list of tags
for tag in @_tagUIs
tag.unmount()
@_tagUIs = []
# If there's no element selected we're done
if not element
return
# Get a list of parent elements for the currently selected element
elements = element.parents()
elements.reverse()
elements.push(element)
for element in elements
# Certain tags are ignored as attributes cannot be safely set
# against them.
if ContentTools.INSPECTOR_IGNORED_ELEMENTS.indexOf(
element.type()) != -1
continue
# Convert each element to a UI tag
tag = new ContentTools.TagUI(element)
@_tagUIs.push(tag)
tag.mount(@_domTags)
class ContentTools.TagUI extends ContentTools.AnchoredComponentUI
# A tag displayed in the inspector representing the selected element or one
# of it's parents.
constructor: (@element) ->
super()
# Methods
mount: (domParent, before=null) ->
# Mount the component to the DOM
@_domElement = @constructor.createDiv(['ct-tag'])
@_domElement.textContent = @element.tagName()
super(domParent, before)
# Private methods
_addDOMEventListeners: () ->
# Add DOM event listeners for the widget
@_domElement.addEventListener('mousedown', @_onMouseDown)
_onMouseDown: (ev) =>
# Open a properties dialog for the associated element
# We don't want to lose selected elements focus so prevent the event's
# default behaviour.
ev.preventDefault()
# If supported allow store the state for restoring once the dialog is
# cancelled.
if @element.storeState
@element.storeState()
# Set-up the dialog
app = ContentTools.EditorApp.get()
# Modal
modal = new ContentTools.ModalUI()
# Dialog
dialog = new ContentTools.PropertiesDialog(@element)
# Support cancelling the dialog
dialog.addEventListener 'cancel', () =>
modal.hide()
dialog.hide()
if @element.restoreState
@element.restoreState()
# Support saving the dialog
dialog.addEventListener 'save', (ev) =>
detail = ev.detail()
attributes = detail.changedAttributes
styles = detail.changedStyles
innerHTML = detail.innerHTML
# Apply the attribute changes
for name, value of attributes
# The `class` attribute has to be handled differently from other
# attributes as CSS classes should only be set using the
# add/removeCSSClass methods.
if name == 'class'
# If the value is null (marked for remove) default it to an
# empty string so all classes will be safely removed.
if value == null
value = ''
# Apply any new classes (and build a map of all classes for
# the element so we know which ones, if any, to remove).
classNames = {}
for className in value.split(' ')
className = className.trim()
if not className
continue
classNames[className] = true
# If the element doesn't have the class add it
if not @element.hasCSSClass(className)
@element.addCSSClass(className)
# Remove any classes no longer present
for className in @element.attr('class').split(' ')
className = className.trim()
# If the class is no longer assocated with the element
# remove it.
if classNames[className] == undefined
@element.removeCSSClass(className)
else
if value == null
@element.removeAttr(name)
else
@element.attr(name, value)
# Apply the style changes
for cssClass, applied of styles
if applied
@element.addCSSClass(cssClass)
else
@element.removeCSSClass(cssClass)
# Apply any inner HTML changes
unless innerHTML is null
# Check there has been a change
if innerHTML != dialog.getElementInnerHTML()
# Update the elements inner HTML
element = @element
unless element.content
element = element.children[0]
element.content = new HTMLString.String(
innerHTML,
element.content.preserveWhitespace()
)
element.updateInnerHTML()
element.taint()
# If we've changed the inner HTML just place the caret at
# the start of the selection.
element.selection(new ContentSelect.Range(0, 0))
element.storeState()
modal.hide()
dialog.hide()
# Restore any previous state
if @element.restoreState
@element.restoreState()
# Show the dialog
app.attach(modal)
app.attach(dialog)
modal.show()
dialog.show()