Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
matrix_apply_style.inc
1 <?php
18 global $ROOT_PATH;
19 include_once($ROOT_PATH.'wysiwyg_plugin.inc');
20 
21 
33 class Matrix_Apply_Style extends WYSIWYG_Plugin
34 {
35 
41  var $_css_classes = Array();
42 
43 
49  function Matrix_Apply_Style(&$wysiwyg)
50  {
52  // WORK OUT THE STLYE LIST //
54  require_once dirname(__FILE__).'/../../../../core/include/init.inc';
55 
56  // check for any classes declared for current design
57  $classes = $this->getDesignClasses();
58  if (!empty($classes)) {
59  foreach ($classes as $class_name => $short_name) {
60  $this->_css_classes[$class_name] = $short_name;
61  }
62  }
63 
64  // if there weren't any classes defined for design, we are getting some out from ccs file
65  if (empty($this->_css_classes) && isset($GLOBALS['sq_bodycopy_cssids'])) {
66  foreach ($GLOBALS['sq_bodycopy_cssids'] as $css_assetid) {
67  $css = $GLOBALS['SQ_SYSTEM']->am->getAsset($css_assetid);
68  $css_file = $css->getExistingFile();
69  $stylesheet_src = '';
70  if (!empty($css_file)) {
71  $stylesheet_src = $css_file['path'];
72  }
73 
74  // if it exists, parse the stylesheet to get a list of styles we can use
75  if (file_exists($stylesheet_src)) {
76  require_once SQ_FUDGE_PATH.'/general/file_system.inc';
77  $stylesheet = preg_replace('%[\r\n]+%',' ',file_to_string($stylesheet_src));
78  $stylesheet = preg_replace('/\/\*.*\*\//sU', '',$stylesheet);
79  $matches = Array();
80  preg_match_all('%\s*([^\{]+)\{[^\}]+\}%', $stylesheet, $matches);
81  $class_strings = $matches[1];
82 
83  // $class_strings is now a list of all the class and their variations
84  // in the stylesheet - so we need to parse this again to get unique
85  // class names without variations
86  foreach ($class_strings as $class_string) {
87  $variations = explode(',', $class_string);
88  foreach ($variations as $class) {
89  // non-standard class begin with a '.'
90  list($class) = explode(':',trim($class));
91  if (substr($class,0,1) == '.') $this->_css_classes[trim($class,'.')] = trim($class,'.');
92  }
93  }
94  }
95 
96  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($css);
97  unset($css);
98  }
99  }
100 
101  $this->_add_button('formatblock','','','','');
102  if (!empty($this->_css_classes)) $this->_add_button('applystyle','','','','');
103 
104  }//end constructor
105 
106 
113  function print_plugin_vars()
114  {
115  ?>
116  this.config.applystyle = {
117  <?php
118 
119  if (!empty($this->_css_classes)) {
120  ?>"-- select css style --": "",<?php
121  $string = '';
122  foreach ($this->_css_classes as $class_name => $cn) echo '" '.str_replace('"', '\"', $cn).'": "'.str_replace('"', '\"', $class_name).'",';
123  ?>"-- remove css style --": "no_style"<?php
124  }
125 
126  ?>
127  };
128 
129  this.config.formatblock = {
130  "Heading 1": "h1",
131  "Heading 2": "h2",
132  "Heading 3": "h3",
133  "Heading 4": "h4",
134  "Heading 5": "h5",
135  "Heading 6": "h6",
136  "Normal" : "p",
137  "Address" : "address",
138  "Formatted": "pre"
139  };
140  <?php
141 
142  }//end print_plugin_vars()
143 
144 
152  {
153  ?>
154  case "applystyle":
155  case "formatblock":
156  options = editor.config[txt];
157  cmd = txt;
158  break;
159  <?php
160 
161  }//end print_plugin_create_select()
162 
163 
171  {
172  ?>
173  case "applystyle":
174  if (!text) {
175  var options = this.config["applystyle"];
176  var current_span = this.getClosest("span");
177  var found = false;
178  if (current_span) {
179  var value = current_span.className;
180  var k = 0;
181  for (var j in options) {
182  if (options[j].toLowerCase() == value) {
183  btn.element.selectedIndex = k;
184  found = true;
185  break;
186  }
187  ++k;
188  }
189  }
190  if (!found) { btn.element.selectedIndex = 0; }
191  }
192  break;
193 
194  case "formatblock":
195  if (!text) {
196  if (!doc) break;
197  var value = '';
198  try {
199  var value = ("" + doc.queryCommandValue(cmd)).toLowerCase();
200  } catch (e) {}
201  if (!value) {
202  break;
203  }
204  btn.element.selectedIndex = 0;
205  var options = this.config["formatblock"]; // HACK!!
206  var k = 0;
207  for (var j in options) {
208  // FIXME: the following line is scary.
209  if ((j.toLowerCase() == value) ||
210  (options[j].substr(0, value.length).toLowerCase() == value)) {
211  btn.element.selectedIndex = k;
212  break;
213  }
214  ++k;
215  }
216  }
217  break;
218  <?php
219 
220  }//end print_plugin_update_toolbar()
221 
222 
230  {
231  ?>
232  case "applystyle":
233  if (value == '') {
234  break;
235  }
236  range = this._createRange(this._getSelection());
237 
238  if (HTMLArea.is_ie) {
239 
241 
242  // if the selection is empty
243  if (range.boundingWidth == 0) {
244  break;
245  }
246 
247  // get the parent element of the range, working around an IE bug
248  // with parentELement()
249  var parent = range.parentElement();
250  while (true) {
251  var testRange = range.duplicate();
252  testRange.moveToElementText(parent);
253  if (testRange.inRange(range)) {
254  break;
255  }
256  if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
257  break;
258  }
259  parent = parent.parentElement;
260  }
261  var selectedHTML = range.htmlText.replace(/^\s+|\s+$/, '');
262 
263  if (selectedHTML == range.text.replace(/^\s+|\s+$/, '')) {
264  // we are within a single element
265  range.text = 'MYOPENSPAN'+range.text+'MYCLOSESPAN';
266  } else if ((parent.innerHTML == selectedHTML) || (parent.outerHTML.replace(/\s/g, '') == selectedHTML.replace(/\s/g, ''))) {
267  // Apply class to parent element directly
268  parent.className = value;
269  } else {
270  // Traverse children of parent
271  for (var elt = parent.firstChild; elt != null; elt = elt.nextSibling) {
272  var tempRange = document.body.createTextRange();
273  tempRange.moveToElementText(elt);
274  var overlaps =
275  (range.compareEndPoints('StartToEnd', tempRange) <= 0)
276  && (range.compareEndPoints('EndToStart', tempRange) >= 0);
277  if (overlaps) {
278  var endsAfter = (range.compareEndPoints('EndToEnd', tempRange) == -1);
279  var startsBefore = (range.compareEndPoints('StartToStart', tempRange) == 1);
280  if (startsBefore) {
281  var offset = 0;
282  while (range.compareEndPoints('StartToStart', tempRange) == 1) {
283  offset++;
284  tempRange.moveStart('character', 1);
285  }
286  tempRange.moveToElementText(elt);
287  var newText = tempRange.text.substring(0, offset)+"MYOPENSPAN"+tempRange.text.substring(offset, tempRange.text.length)+"MYCLOSESPAN";
288  if (elt.nodeType == 3) {
289  elt.data = newText;
290  } else if ((elt.childNodes.length == 1) && (elt.firstChild.nodeType == 3)) {
291  elt.innerHTML = newText;
292  } else {
293  tempRange.text = newText; // IE stuffs this up often so we avoid it
294  }
295  } else if (endsAfter) {
296  // This element extends beyond the selection
297  var offset = 0;
298  while (range.compareEndPoints('EndToEnd', tempRange) == -1) {
299  offset++;
300  tempRange.moveEnd('character', -1);
301  }
302  tempRange.moveToElementText(elt);
303  tempRange.text = "MYOPENSPAN"+tempRange.text.substring(0, tempRange.text.length-offset+1)+"MYCLOSESPAN"+tempRange.text.substring(tempRange.text.length-offset+1, tempRange.text.length);
304  } else {
305  // This element is fully within the selection
306  // Apply class to it, or wrap a classed span around it
307  if (elt.nodeType == 3) {
308  // surround it with a span
309  var newSpan = document.createElement('SPAN');
310  newSpan.innerHTML = elt.data;
311  newSpan.className = value;
312  elt.parentNode.insertBefore(newSpan, elt);
313  elt.parentNode.removeChild(elt);
314  } else {
315  elt.className = value;
316  }
317  }
318  }
319  }
320  }
321  parent.innerHTML = (parent.innerHTML.replace(new RegExp("MYOPENSPAN", "g"), '<span class="'+value+'">').replace(new RegExp("MYCLOSESPAN", "g"), '</span>'));
322 
323  } else {
324 
326  if (range.collapsed) {
327  break;
328  }
329  if ((range.startContainer.nodeType == 3) && (range.startContainer == range.endContainer)) {
330  // Selection covers only a single text node
331  if ((range.startOffset == 0) && (range.endOffset == range.endContainer.length)) {
332  // all of one text node selected
333  range.startContainer.parentNode.className = value;
334  } else {
335  // part of one text node selected
336  this.surroundHTML('<span class="'+value+'">', '</span>');
337  }
338  break;
339 
340  }//end if inside a single text node
341 
342  var effectiveStartElt = range.startContainer;
343  if ((effectiveStartElt.nodeType == 3)) {
344  effectiveStartElt = effectiveStartElt.parentNode;
345  }
346  var effectiveEndElt = range.endContainer;
347  if ((effectiveEndElt.nodeType == 3)) {
348  effectiveEndElt = effectiveEndElt.parentNode;
349  }
350 
351  if (range.startContainer == range.endContainer) {
352  if ((range.startOffset != 0) || (range.endOffset != range.childNodes.length)) {
353  effectiveStartElt = range.startContainer.childNodes.item(range.startOffset);
354  effectiveEndElt = range.startContainer.childNodes.item(range.endOffset);
355  }
356  }
357 
358  var ancestor = range.commonAncestorContainer;
359  if ((ancestor.tagName == 'UL') || (ancestor.tagName == 'OL') || (ancestor.tagName == 'TABLE') || (ancestor.tagName == 'DL') || (ancestor.tagName == 'TR')) {
360  // Do some clearing up of empty text nodes to avoid confusion
361  var elt = ancestor.firstChild;
362  while (elt != null) {
363  nextElt = elt.nextSibling;
364  if ((elt.nodeType == 3) && (elt.data.replace(/^\s+|\s+$/, '') == '')) {
365  elt.parentNode.removeChild(elt);
366  }
367  elt = nextElt;
368  }
369  }
370 
371  if ((effectiveStartElt == effectiveStartElt.parentNode.firstChild) && (effectiveEndElt == effectiveStartElt.parentNode.lastChild) && (range.startOffset == 0) && (range.endOffset == 0)) {
372  // the selection equals all of the parent container, so just apply there
373  effectiveStartElt.parentNode.className = value;
374  break;
375 
376  }//end if all of parent container selected
377 
378  if (effectiveStartElt.parentNode == effectiveEndElt.parentNode) {
379  // we can traverse
380 
381  // Do the start container
382  if (range.startContainer.nodeType == 3) {
383  if (range.startOffset == 0) {
384  effectiveStartElt.className = value;
385  } else {
386  // split it
387  var newSpan = document.createElement('SPAN');
388  newSpan.innerHTML = range.startContainer.data.substring(range.startOffset);
389  newSpan.className = value;
390  if (range.startContainer.nextSibling == null) {
391  range.startContainer.parentNode.appendChild(newSpan);
392  } else {
393  range.startContainer.parentNode.insertBefore(newSpan, range.startContainer.nextSibling);
394  }
395  range.startContainer.data = range.startContainer.data.substring(0, range.startOffset);
396  }
397  } else {
398  effectiveStartElt.className = value;
399  }
400 
401  // Do the in-between containers
402  var tempElt = effectiveStartElt.nextSibling;
403  while ((tempElt != null) && (tempElt != effectiveEndElt)) {
404  if (tempElt.nodeType == 3) {
405  // surround it with a span
406  var newSpan = document.createElement('SPAN');
407  newSpan.innerHTML = tempElt.data;
408  newSpan.className = value;
409  tempElt.parentNode.insertBefore(newSpan, tempElt);
410  tempElt.parentNode.removeChild(tempElt);
411  } else {
412  tempElt.className = value;
413  }
414  tempElt = tempElt.nextSibling;
415  }
416 
417  // Do the end container
418  if (range.endContainer.nodeType == 3) {
419  if (range.endOffset >= range.endContainer.data.length-1) {
420  effectiveEndElt.className = value;
421  } else {
422  // split it
423  var newSpan = document.createElement('SPAN');
424  newSpan.className = value;
425  newSpan.innerHTML = range.endContainer.data.substring(0, range.endOffset);
426  range.endContainer.parentNode.insertBefore(newSpan, range.endContainer);
427  range.endContainer.data = range.endContainer.data.substring(range.endOffset);
428  }
429  } else {
430  effectiveEndElt.className == value;
431  }
432  break;
433 
434  }//end if common parent
435 
436  alert('Styles cannot be applied to the current selection. Try applying styles to smaller subsections individually');
437  }
438  break;
439 
440  case "formatblock":
441  if (HTMLArea.is_ie) { // sad but true
442  value = "<" + value + ">";
443  }
444  this._execCommand(txt, false, value);
445  break;
446  <?php
447 
448  }//end print_plugin_combo_selected()
449 
450 
459  function process(&$html)
460  {
461  $html = preg_replace('|<span[^>]+class\="?no_style"?([^>]+)?>([^<]+)?</span>|i', '\\2', $html);
462  $html = preg_replace('|(<[^>]*?)class\="?no_style"?([^>]*?>)|i', '$1$2', $html);
463 
464  }//end process();
465 
466 
473  function getDesignClasses()
474  {
475  $classes = Array();
476  $am =& $GLOBALS['SQ_SYSTEM']->am;
477 
478  if (isset($GLOBALS['sq_preview_url']) && !empty($GLOBALS['sq_preview_url'])) {
479  $url = $GLOBALS['sq_preview_url'];
480  $url = preg_replace('|^http[s]?://|', '', $url);
481 
482  $designid = $am->getValueFromURL($url, 'design::%frontend%', TRUE);
483 
484  if ($designid) {
485  $design = $am->getAsset($designid);
486  $classes_list = $design->attr('wysiwyg_classes');
487  if (!empty($classes_list)) {
488  foreach ($classes_list as $key => $value) {
489  $classes[$key] = $value;
490  }
491  }
492  }
493  }
494 
495  return $classes;
496 
497  }//end getDesignClasses();
498 
499 
500 }//end class()
501 
502 ?>