c885c857132d8e87a18bf6abb4cd5d51dda3da5a
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / htmlarea / plugins / MicrodataSchema / microdata-schema.js
1 /**
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13 /**
14 * Microdata Schema Plugin for TYPO3 htmlArea RTE
15 */
16 HTMLArea.MicrodataSchema = Ext.extend(HTMLArea.Plugin, {
17 /*
18 * This function gets called by the class constructor
19 */
20 configurePlugin: function (editor) {
21 /*
22 * Registering plugin "About" information
23 */
24 var pluginInformation = {
25 version : '1.0',
26 developer : 'Stanislas Rolland',
27 developerUrl : 'http://www.sjbr.ca/',
28 copyrightOwner : 'Stanislas Rolland',
29 sponsor : 'SJBR',
30 sponsorUrl : 'http://www.sjbr.ca/',
31 license : 'GPL'
32 };
33 this.registerPluginInformation(pluginInformation);
34 /*
35 * Registering the buttons
36 */
37 var buttonList = this.buttonList, buttonId;
38 for (var i = 0, n = buttonList.length; i < n; ++i) {
39 var button = buttonList[i];
40 buttonId = button[0];
41 var buttonConfiguration = {
42 id : buttonId,
43 tooltip : this.localize(buttonId + '-Tooltip'),
44 iconCls : 'htmlarea-action-' + button[2],
45 action : 'onButtonPress',
46 context : button[1]
47 };
48 this.registerButton(buttonConfiguration);
49 }
50 },
51 /*
52 * The list of buttons added by this plugin
53 */
54 buttonList: [
55 ['ShowMicrodata', null, 'microdata-show']
56 ],
57 /*
58 * Default configuration values for dialogue form fields
59 */
60 configDefaults: {
61 combo: {
62 editable: true,
63 selectOnFocus: true,
64 typeAhead: true,
65 triggerAction: 'all',
66 forceSelection: true,
67 mode: 'local',
68 valueField: 'name',
69 displayField: 'label',
70 helpIcon: true,
71 tpl: '<tpl for="."><div ext:qtip="{comment}" style="text-align:left;font-size:11px;" class="x-combo-list-item">{label}</div></tpl>'
72 }
73 },
74 /*
75 * This function gets called when the editor is generated
76 */
77 onGenerate: function () {
78 // Create the types and properties stores
79 this.typeStore = Ext.StoreMgr.lookup('itemtype');
80 if (!this.typeStore) {
81 this.typeStore = new Ext.data.JsonStore({
82 autoDestroy: false,
83 autoLoad: true,
84 fields: [{ name: 'label'}, { name: 'name'}, { name: 'comment'}, { name: 'subClassOf'}],
85 listeners: {
86 load: {
87 fn: this.addMicrodataMarkingRules,
88 scope: this
89 }
90 },
91 root: 'types',
92 storeId: 'itemtype',
93 url: this.editorConfiguration.schemaUrl
94 });
95 } else {
96 this.addMicrodataMarkingRules(this.typeStore);
97 }
98 this.typeStore = Ext.StoreMgr.lookup('itemprop');
99 if (!this.propertyStore) {
100 this.propertyStore = new Ext.data.JsonStore({
101 autoDestroy: false,
102 autoLoad: true,
103 fields: [{ name: 'label'}, { name: 'name'}, { name: 'comment'}, { name: 'domain'}, { name: 'range'}],
104 listeners: {
105 load: {
106 fn: this.addMicrodataMarkingRules,
107 scope: this
108 }
109 },
110 root: 'properties',
111 storeId: 'itemprop',
112 url: this.editorConfiguration.schemaUrl
113 });
114 } else {
115 this.addMicrodataMarkingRules(this.propertyStore);
116 }
117 },
118 /*
119 * This function adds rules to the stylesheet for language mark highlighting
120 * Model: body.htmlarea-show-language-marks *[lang=en]:before { content: "en: "; }
121 * Works in IE8, but not in earlier versions of IE
122 */
123 addMicrodataMarkingRules: function (store) {
124 var styleSheet = this.editor.document.styleSheets[0];
125 store.each(function (option) {
126 var selector = 'body.htmlarea-show-microdata *[' + store.storeId + '="' + option.get('name') + '"]:before';
127 var style = 'content: "' + option.get('label') + ': "; font-variant: small-caps;';
128 var rule = selector + ' { ' + style + ' }';
129 if (!HTMLArea.isIEBeforeIE9) {
130 try {
131 styleSheet.insertRule(rule, styleSheet.cssRules.length);
132 } catch (e) {
133 this.appendToLog('onGenerate', 'Error inserting css rule: ' + rule + ' Error text: ' + e, 'warn');
134 }
135 } else {
136 styleSheet.addRule(selector, style);
137 }
138 return true;
139 }, this);
140 },
141 /*
142 * This function gets called when a button was pressed.
143 *
144 * @param object editor: the editor instance
145 * @param string id: the button id or the key
146 *
147 * @return boolean false if action is completed
148 */
149 onButtonPress: function (editor, id, target) {
150 // Could be a button or its hotkey
151 var buttonId = this.translateHotKey(id);
152 buttonId = buttonId ? buttonId : id;
153
154 switch (buttonId) {
155 case 'ShowMicrodata':
156 this.toggleMicrodata();
157 break;
158 default :
159 break;
160 }
161 return false;
162 },
163 /*
164 * Toggles the display of microdata
165 *
166 * @param boolean forceMicrodata: if set, microdata is displayed whatever the current state
167 *
168 * @return void
169 */
170 toggleMicrodata: function (forceMicrodata) {
171 var body = this.editor.document.body;
172 if (!HTMLArea.DOM.hasClass(body, 'htmlarea-show-microdata')) {
173 HTMLArea.DOM.addClass(body,'htmlarea-show-microdata');
174 } else if (!forceMicrodata) {
175 HTMLArea.DOM.removeClass(body,'htmlarea-show-microdata');
176 }
177 },
178 /*
179 * This function builds the configuration object for the Microdata fieldset
180 *
181 * @param object element: the element being edited, if any
182 * @param object configured properties for the microdata fields
183 *
184 * @return object the fieldset configuration object
185 */
186 buildMicrodataFieldsetConfig: function (element, properties) {
187 var typeStore = Ext.StoreMgr.lookup('itemtype');
188 var propertyStore = Ext.StoreMgr.lookup('itemprop');
189 var itemsConfig = [];
190 this.inheritedType = 'none';
191 var parent = element.parentNode;
192 while (parent && !/^(body)$/i.test(parent.nodeName)) {
193 if (parent.getAttribute('itemtype')) {
194 this.inheritedType = parent.getAttribute('itemtype');
195 break;
196 } else {
197 parent = parent.parentNode;
198 }
199 }
200 var selectedType = element && element.getAttribute('itemtype') ? element.getAttribute('itemtype') : 'none';
201 var selectedProperty = element && element.getAttribute('itemprop') ? element.getAttribute('itemprop') : 'none';
202 itemsConfig.push({
203 xtype: 'displayfield',
204 itemId: 'currentItemType',
205 fieldLabel: this.getHelpTip('currentItemType', 'currentItemType'),
206 style: {
207 fontWeight: 'bold'
208 },
209 value: this.inheritedType
210 });
211 itemsConfig.push(Ext.applyIf({
212 xtype: 'combo',
213 fieldLabel: this.getHelpTip('itemprop', 'itemprop'),
214 hidden: this.inheritedType === 'none',
215 itemId: 'itemprop',
216 store: propertyStore,
217 value: selectedProperty,
218 width: ((properties['itemprop'] && properties['itemprop'].width) ? properties['itemprop'].width : 300)
219 }, this.configDefaults['combo']));
220 itemsConfig.push({
221 itemId: 'itemscope',
222 fieldLabel: this.getHelpTip('itemscope', 'itemscope'),
223 listeners: {
224 check: {
225 fn: this.onItemScopeChecked,
226 scope: this
227 }
228 },
229 style: {
230 marginBottom: '5px'
231 },
232 checked: element ? (element.getAttribute('itemscope') === 'itemscope') : false,
233 xtype: 'checkbox'
234 });
235 itemsConfig.push(Ext.applyIf({
236 xtype: 'combo',
237 fieldLabel: this.getHelpTip('itemtype', 'itemtype'),
238 hidden: element && !element.getAttribute('itemscope'),
239 hideMode: 'visibility',
240 itemId: 'itemtype',
241 store: typeStore,
242 value: selectedType,
243 width: ((properties['itemtype'] && properties['itemtype'].width) ? properties['itemtype'].width : 300)
244 }, this.configDefaults['combo']));
245 return {
246 xtype: 'fieldset',
247 itemId: 'microdataFieldset',
248 title: this.getHelpTip('', 'microdata'),
249 defaultType: 'textfield',
250 defaults: {
251 labelSeparator: ':'
252 },
253 items: itemsConfig,
254 labelWidth: 100,
255 listeners: {
256 afterrender: {
257 fn: this.onMicroDataRender,
258 scope: this
259 }
260 }
261 };
262 },
263 /*
264 * Handler invoked when the Microdata fieldset is rendered
265 */
266 onMicroDataRender: function (fieldset) {
267 this.fieldset = fieldset;
268 var typeStore = Ext.StoreMgr.lookup('itemtype');
269 var index = typeStore.findExact('name', this.inheritedType);
270 if (index !== -1) {
271 // If there is an inherited type, set the label
272 var inheritedTypeName = typeStore.getAt(index).get('label');
273 this.fieldset.find('itemId', 'currentItemType')[0].setValue(inheritedTypeName);
274 // Filter the properties by the inherited type, if any
275 var propertyCombo = this.fieldset.find('itemId', 'itemprop')[0];
276 var selectedProperty = propertyCombo.getValue();
277 // Filter the properties by the inherited type, if any
278 this.filterPropeties(this.inheritedType, selectedProperty);
279 }
280 },
281 /*
282 * Handler invoked when the itemscope checkbox is checked/unchecked
283 *
284 */
285 onItemScopeChecked: function (checkbox, checked) {
286 this.fieldset.find('itemId', 'itemtype')[0].setVisible(checked);
287 this.synch
288 },
289 /*
290 * Filter out properties not part of the selected type
291 */
292 filterPropeties: function (type, selectedProperty) {
293 var typeStore = Ext.StoreMgr.lookup('itemtype');
294 var propertyStore = Ext.StoreMgr.lookup('itemprop');
295 if (propertyStore.realSnapshot) {
296 propertyStore.snapshot = propertyStore.realSnapshot;
297 delete propertyStore.realSnapshot;
298 propertyStore.clearFilter(true);
299 }
300 var index,
301 superType = type,
302 superTypes = [];
303 while (superType) {
304 superTypes.push(superType);
305 index = typeStore.findExact('name', superType);
306 if (index !== -1) {
307 superType = typeStore.getAt(index).get('subClassOf');
308 } else {
309 superType = null;
310 }
311 }
312 var superTypes = new RegExp( '^(' + superTypes.join('|') + ')$', 'i');
313 propertyStore.filterBy(function (property) {
314 // Filter out properties not part of the type
315 return superTypes.test(property.get('domain')) || property.get('name') === 'none';
316 });
317 // Make sure the combo list is filtered
318 propertyStore.realSnapshot = propertyStore.snapshot;
319 propertyStore.snapshot = propertyStore.data;
320 var propertyCombo = this.fieldset.find('itemId', 'itemprop')[0];
321 propertyCombo.clearValue();
322 propertyCombo.setValue(selectedProperty);
323 },
324 /*
325 * Set microdata attributes of the element
326 */
327 setMicrodataAttributes: function (element) {
328 var comboFields = this.fieldset.findByType('combo');
329 Ext.each(comboFields, function (field) {
330 var itemId = field.getItemId();
331 var value = field.getValue();
332 switch (itemId) {
333 case 'itemprop':
334 case 'itemtype':
335 element.setAttribute(itemId, (value === 'none') ? '' : value);
336 break;
337 }
338 }, this);
339 var itemScopeField = this.fieldset.find('itemId', 'itemscope')[0];
340 if (itemScopeField.getValue()) {
341 element.setAttribute('itemscope', 'itemscope');
342 } else {
343 element.removeAttribute('itemscope');
344 element.removeAttribute('itemtype');
345 }
346 },
347 /*
348 * This function gets called when the toolbar is updated
349 */
350 onUpdateToolbar: function (button, mode, selectionEmpty, ancestors, endPointsInSameBlock) {
351 if (this.getEditorMode() === 'wysiwyg' && this.editor.isEditable()) {
352 switch (button.itemId) {
353 case 'ShowMicrodata':
354 button.setInactive(!HTMLArea.DOM.hasClass(this.editor.document.body, 'htmlarea-show-microdata'));
355 break;
356 default:
357 break;
358 }
359 }
360 }
361 });