Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
search_and_replace.inc
1 <?php
18 global $ROOT_PATH;
19 include_once($ROOT_PATH.'wysiwyg_plugin.inc');
20 
29 class Search_And_Replace extends WYSIWYG_Plugin
30 {
31 
32 
40  function Search_And_Replace()
41  {
42  $this->_add_button('searchandreplace','SearchAndReplace','Search And Replace','false','69');
43 
44  }//end Search_And_Replace()
45 
46 
53  function paint_generic()
54  {
55  ?>
56  <script type="text/javascript" language="Javascript">
57 
58  // Global variable to store the text nodes in the document
59  var global_text_nodes = Array();
60 
61  // Selects the given text in the supplied textnode
62  HTMLArea.prototype._selectTextContent = function(node, keyword, index, mcase, moption, direction, find_only) {
63 
64  textNode = this._getTextNode(node);
65  if (textNode === null || textNode.nodeType != 3) return -1;
66 
67  var text = textNode.nodeValue;
68  if (text === null || keyword === null || !text.length || !keyword.length) return -1;
69 
70  // Case sensitive match
71  text = mcase ? text : text.toLowerCase();
72  keyword = mcase ? keyword : keyword.toLowerCase();
73 
74  // Check if we are at end of or the start of the text content
75  if (direction === undefined) direction = true;
76  if ((direction && (index + keyword.length) > text) || (!direction && (index - keyword.length) < 0)) return -1;
77 
78  if (index === undefined || index < 0) index = 0;
79 
80  keyword = this._escapeRegexChars(keyword);
81 
82  var word_pos;
83  var correct_match = false;
84  var left_char_empty;
85  var right_char_empty;
86 
87  // Search the keyword
88  do {
89  word_pos = direction ? text.indexOf(keyword, index) : text.lastIndexOf(keyword, index - keyword.length);
90  if (word_pos > -1) {
91  // So we found the keyword. Now make sure it adheres the partial/whole word condition thing
92  left_char_empty = text.charAt(word_pos - 1).match(/[a-z0-9]/i) === null ? true : false;
93  right_char_empty = text.charAt(word_pos + keyword.length).match(/[a-z0-9]/i) === null ? true: false;
94 
95  correct_match = (moption == 'whole' && left_char_empty && right_char_empty) ||
96  (moption == 'start' && left_char_empty) ||
97  (moption == 'end' && right_char_empty) ||
98  (moption == 'none');
99  }
100  // Move the index
101  index = direction ? index + keyword.length : index - keyword.length;
102  } while (!correct_match && index < text.length && index >= 0);
103 
104 
105  // If text was not found then we needn't select anything
106  if (!correct_match) return -1;
107 
108  // If we just need the word position then we needn't proceed further
109  if (find_only !== undefined && find_only) return word_pos;
110 
111  var range;
112  if (HTMLArea.is_ie) {
113  range = this._createTextRange();
114  // Can't move range to the individual text nodes in IE for some reasons
115  // So to workarond this, create and place an empty span element right before the text node
116  // and then move the range to this span element
117  var span_el = document.createElement("span");
118  textNode.parentNode.insertBefore(span_el, textNode);
119  range.moveToElementText(span_el);
120 
121  range.collapse(true);
122  range.moveStart("character", word_pos);
123  range.moveEnd("character", keyword.length);
124  range.select();
125  range.scrollIntoView();
126 
127  // Once done selecting, get rid of the span element created above
128  textNode.parentNode.removeChild(span_el);
129  } else {
130  var sel = this._getSelection();
131  range = this._doc.createRange();
132  range.setStart(textNode, word_pos);
133  range.setEnd(textNode, word_pos + keyword.length);
134  sel.removeAllRanges();
135  sel.addRange(range);
136  textNode.parentNode.scrollIntoView();
137  }
138  return direction ? word_pos + keyword.length : word_pos;
139  };
140 
141 
142  // Get the number of occurance of given regular expression
143  HTMLArea.prototype._countWord = function(text, regex){
144  var count = 0;
145 
146  text.replace(
147  regex,
148  function(){
149  count++;
150  }
151  );
152 
153  return count;
154  }
155 
156 
157  // DOM tree walker to get all the text nodes
158  HTMLArea.prototype._getTextNodes = function(refresh) {
159 
160  refresh = refresh === undefined || !refresh ? false : true;
161  // If the we have parsed the DOM tree before for the text nodes
162  if (!refresh && global_text_nodes.length > 0) {
163  return global_text_nodes;
164  }
165 
166  var root = !HTMLArea.is_ie ? this._iframe.contentWindow.document.body : this._docContent;
167 
168  var text_nodes = Array();
169  var node = root.childNodes[0];
170  var count = 0;
171  while(node != null) {
172  if (node.nodeType == 3) {
173  text_nodes[count++] = node;
174  }
175 
176  if (node.hasChildNodes()) {
177  node = node.firstChild;
178  }
179  else {
180  while(node.nextSibling == null && node != root) {
181  node = node.parentNode;
182  }
183  node = node.nextSibling;
184  }
185  }
186 
187  // Store the value in the global variable for reuse
188  global_text_nodes = text_nodes;
189  return text_nodes;
190  }
191 
192 
193  // Get the text node from the WYSIWYG body
194  HTMLArea.prototype._getTextNode = function(index){
195 
196  if (index < 0) return null;
197 
198  var node = null;
199  var count = 0;
200 
201  var child_nodes = this._getTextNodes();
202  while(node = child_nodes[count]) {
203 
204  if (count == index) break;
205  count++;
206  }
207  return node;
208  }
209 
210 
211  // Returns the index of the last text node from the WYSIWYG body
212  HTMLArea.prototype._getLastTextNodeIndex = function(){
213  var count = 0;
214  var text_nodes = this._getTextNodes();
215 
216  return text_nodes.length - 1;
217  }
218 
219 
220  // Empty the global variable storing all the text nodes in the document
221  HTMLArea.prototype._refreshStoredTextNodes = function(){
222  global_text_nodes = Array();
223  }
224 
225 
226  // Escapes all the regular expression characters in the given text
227  HTMLArea.prototype._escapeRegexChars = function(text){
228 
229  var regex_chars = Array('/', '{', '}', '[', ']', '(', ')', '-', '+', '*', '^', '$', '?', '.');
230  arguments.callee.regex = new RegExp('(\\'+regex_chars.join('|\\') + ')', 'g');
231 
232  return text.replace(arguments.callee.regex, '\\$1');
233  }
234 
235  </script>
236  <?php
237 
238  }//end paint_generic()
239 
240 
248  {
249  ?>
250  case "searchandreplace":
251 
252  // Global var to store the start search position in WYSIWYG
253  var start_pos = 0;
254  // Global var to keep track of textnodes in the body
255  var current_node = 0;
256  // Global var to check if the search text exists after complete document search
257  var search_text_not_found = false;
258 
259 
260  this._popupDialog("SearchAndReplace", "<?php echo $this->get_popup_href('search_and_replace.php')?>", 380, 300, false, function(types){
261 
262  if (types == null) return false;
263 
264  var action_type = types[0];
265  var search_str = types[1];
266  var replace_str = types[2];
267 
268  var match_case = false;
269  if (types[3]) {
270  match_case = true;
271  }
272 
273  var match_option = 'none';
274  if (types[4]) {
275  match_option = 'whole';
276  } else if (types[5]) {
277  match_option = 'start';
278  } else if (types[6]) {
279  match_option = 'end';
280  } else if (types[7]) {
281  match_option = 'regex';
282  }
283 
284  selected_content = editor.getSelectedHTML();
285  whole_content = editor.getHTML();
286 
287  //Replace direction, "forward" by default
288  var direction = true;
289 
290  switch (action_type) {
291  case "search_next":
292  break;
293 
294  case "search_previous":
295  direction = false;
296  break;
297 
298  case "replace_current":
299  if (start_pos != -1 && selected_content.length > 0) {
300  editor.insertHTML(replace_str);
301  editor._refreshStoredTextNodes();
302  }
303  break;
304 
305  case "replace_all":
306 
307  case "replace_selection":
308  if (selected_content.length > 0 || action_type === "replace_all") {
309 
310  var match_count = 0;
311 
312  if (match_option != 'regex') {
313  var text_nodes = editor._getTextNodes(true);
314  var node_count = 0
315  var index = 0;
316  var node_value;
317 
318  // If selection is made then only include the textnodes inside the selection
319  if (action_type === "replace_selection") {
320 
321  if (selected_content.length > 0) {
322  var sel = editor._getSelection();
323  var selected_nodes = Array();
324  var start_selection = false;
325  alert(sel.anchorNode.nodeValue);
326  for(var count=0; count < text_nodes.length; count++) {
327  if (!start_selection && sel.anchorNode == text_nodes[count])
328  start_selection = true;
329 
330  selected_nodes[count] = start_selection ? text_nodes[count] : null;
331  if (sel.focusNode == text_nodes[count]) break;
332  }
333  text_nodes = selected_nodes;
334  // Start and end offset of anchor and focus node respectively
335  index = sel.anchorOffset;
336  var endOffset = sel.focusOffset;
337 
338  } else {
339  text_nodes = Array();
340  }
341 
342  }//end if replace_selection
343 
344  while(node_count < text_nodes.length) {
345 
346  if (text_nodes[node_count] === null) {
347  node_count++;
348  continue;
349  }
350  index = editor._selectTextContent(node_count, search_str, index, match_case, match_option, true, true);
351 
352  // If text was selected, make sure we dont get across the unselected portion of the focus node
353  if (endOffset !== undefined && index+search_str.length > endOffset && node_count == text_nodes.length - 1) break;
354 
355  if (index > -1) {
356  node_value = text_nodes[node_count].nodeValue;
357  node_value = (index > 0 ? node_value.substring(0, index) : '')
358  + replace_str
359  + node_value.substring(index+search_str.length, node_value.length);
360  // Update the node value with the replacement
361  text_nodes[node_count].nodeValue = node_value
362  index += replace_str.length;
363  match_count++;
364  } else {
365  // No more match in this node, move to the next
366  node_count++;
367  index = 0;
368  }
369  }
370 
371  alert(match_count+" occurrences of the given text have been replaced");
372 
373  } else {
374  var wysiwyg_content = (action_type === "replace_all") ? whole_content : selected_content;
375  search_regex = new RegExp(search_str, match_case ? 'g' : 'gi');
376  match_count = editor._countWord(wysiwyg_content, search_regex);
377  wysiwyg_content = wysiwyg_content.replace(search_regex, replace_str);
378 
379  if (action_type === "replace_all") {
380  editor.setHTML(wysiwyg_content);
381  } else {
382  editor.insertHTML(wygiwyg_content);
383  }
384 
385  alert(match_count+" number of matching occurrences for given regular expression have been replaced");
386  }
387 
388  if (match_count > 0) {
389  editor._refreshStoredTextNodes();
390  }
391 
392  return false;
393  }
394  break;
395 
396  }//end switch
397 
398 
399  // Select and highlight the selected text in the document body
400  var node;
401  var last_node_index = editor._getLastTextNodeIndex();
402  while(current_node >= -1 && current_node <= (last_node_index + 1)) {
403  node = editor._getTextNode(current_node);
404 
405  // Node is null, we have reach at either end of node list,
406  if (!node) {
407 
408  if (search_text_not_found) {
409  alert("The given text was not found");
410  search_text_not_found = false;
411  break;
412  }
413 
414  if (direction) {
415  alert("Reached the end of the WYSIWYG content. Starting from the beginning");
416  start_pos = 0;
417  current_node = 0;
418  } else {
419  alert("Reached the start of the WYSIWYG content. Starting from the end");
420  last_node = editor._getTextNode(last_node_index);
421  start_pos = last_node.nodeValue.length + search_str.length;
422  current_node = last_node_index;
423  }
424 
425  // if this flag is still true by the next time we reach the either end of document
426  // then it will mean that search text is not there in the document body
427  search_text_not_found = true;
428  }
429 
430  node = !node ? editor._getTextNode(current_node) : node;
431  if (node) {
432  // If the backward search in previous node (actaully in node at right side) failed,
433  // then we need to start the search from the end of the next node
434  if (!direction && start_pos < 0) {
435  start_pos = node.nodeValue.length + search_str.length;
436  }
437 
438  // Search and highlight the text in the current text node
439  start_pos = editor._selectTextContent(current_node, search_str, start_pos, match_case, match_option, direction);
440 
441  }
442 
443  // Continue searchin to next textnode
444  if (start_pos < 0) {
445  direction ? current_node++ : current_node--;
446  continue;
447  } else {
448  // Search text was found hence, ...
449  search_text_not_found = false;
450  }
451 
452  break;
453  }//end while
454 
455  }, null);
456 
457  break;
458  <?php
459 
460  }//end print_plugin_button_click()
461 
462 
463 }//end class
464 
465 ?>