can3p (can3p) wrote in changelog,
can3p
can3p
changelog

[livejournal] r21190: LJSUP-11260: Add search text functionali...

Committer: dpetrov
LJSUP-11260: Add search text functionality in the layers editor.
U   trunk/bin/js/doc/docs_source_livejournal.txt
U   trunk/cgi-bin/LJ/Widget/LayerEditor.pm
U   trunk/htdocs/js/ace/mode-s2.js
A   trunk/htdocs/js/jquery/jquery.lj.s2editSearchBox.js
U   trunk/htdocs/js/s2edit/s2edit.js
U   trunk/htdocs/stc/s2edit.css
U   trunk/templates/Widgets/layereditor.tmpl
Modified: trunk/bin/js/doc/docs_source_livejournal.txt
===================================================================
--- trunk/bin/js/doc/docs_source_livejournal.txt	2012-02-15 09:15:04 UTC (rev 21189)
+++ trunk/bin/js/doc/docs_source_livejournal.txt	2012-02-15 09:33:38 UTC (rev 21190)
@@ -1 +1,2 @@
   jquery/jquery.lj.lazyLoadable.js
+  jquery/jquery.lj.s2editSearchBox.js

Modified: trunk/cgi-bin/LJ/Widget/LayerEditor.pm
===================================================================
--- trunk/cgi-bin/LJ/Widget/LayerEditor.pm	2012-02-15 09:15:04 UTC (rev 21189)
+++ trunk/cgi-bin/LJ/Widget/LayerEditor.pm	2012-02-15 09:33:38 UTC (rev 21190)
@@ -5,7 +5,22 @@
 use Carp qw(croak);
 sub ajax { 1 }
 sub authas { 1 }
-sub need_res { qw( js/jquery/jquery.storage.js js/ace/ace.js js/ace/theme-textmate.js js/ace/mode-perl.js js/ace/mode-s2.js stc/s2edit.css stc/widgets/layereditor.css js/s2edit/xlib.js js/s2edit/s2edit.js js/s2edit/s2gui.js js/s2edit/s2parser.js js/s2edit/s2sense.js js/s2edit/s2library.js) }
+sub need_res { qw( js/jquery/jquery.storage.js
+                    js/jquery/jquery.hotkeys.js
+                    js/jquery/jquery.lj.s2editSearchBox.js
+                    js/ace/ace.js
+                    js/ace/theme-textmate.js
+                    js/ace/mode-perl.js
+                    js/ace/mode-s2.js
+                    stc/s2edit.css
+                    stc/widgets/layereditor.css
+                    js/s2edit/xlib.js
+                    js/s2edit/s2edit.js
+                    js/s2edit/s2gui.js
+                    js/s2edit/s2parser.js
+                    js/s2edit/s2sense.js
+                    js/s2edit/s2library.js
+                    ) }
 
 =head2 template_filename
 # path to template file,

Modified: trunk/htdocs/js/ace/mode-s2.js
===================================================================
--- trunk/htdocs/js/ace/mode-s2.js	2012-02-15 09:15:04 UTC (rev 21189)
+++ trunk/htdocs/js/ace/mode-s2.js	2012-02-15 09:33:38 UTC (rev 21190)
@@ -145,4 +145,32 @@
 	}
 });
 
+canon.addCommand({
+	name: "findnext",
+	bindKey: bindKey("Ctrl-G", "Command-G"),
+	exec: function(env, args, request) { env.editor.findNext(); }
 });
+
+canon.addCommand({
+	name: "findprevious",
+	bindKey: bindKey("Ctrl-Shift-G", "Command-Shift-G"),
+	exec: function(env, args, request) { env.editor.findPrevious(); }
+});
+
+canon.addCommand({
+	name: "find",
+	bindKey: bindKey("Ctrl-F", "Command-F"),
+	exec: function(env, args, request) {
+		jQuery('#searchbox').s2editSearchBox('show');
+	}
+});
+
+canon.addCommand({
+	name: "Esc",
+	bindKey: bindKey("Esc", "Esc"),
+	exec: function(env, args, request) {
+		jQuery('#searchbox').s2editSearchBox('hide');
+	}
+});
+
+});

Added: trunk/htdocs/js/jquery/jquery.lj.s2editSearchBox.js
===================================================================
--- trunk/htdocs/js/jquery/jquery.lj.s2editSearchBox.js	                        (rev 0)
+++ trunk/htdocs/js/jquery/jquery.lj.s2editSearchBox.js	2012-02-15 09:33:38 UTC (rev 21190)
@@ -0,0 +1,261 @@
+ /**
+ * @author dmitry.petrov@sup.com (Dmitry Petrov)
+ * @fileoverview LiveJournal search box widget for s2 layers editor
+ */
+
+/**
+ * @name $.lj.s2editSearchBox
+ * @requires $.ui.core, $.ui.widget, $.lj.basicWidget
+ * @class Widget represents search box on the top left corner of editor.
+ *     For correct operation widget requires ace editor to be initialized.
+ *
+ */
+(function($,window) {
+
+	/** @lends $.lj.s2editSearchBox.prototype */
+	$.widget('lj.s2editSearchBox.js', $.lj.basicWidget, {
+		options: {
+			classNames: {
+				showResults: 'searchbox-done',
+				preshow: 'searchbox-preshow',
+				show: 'searchbox-show'
+			},
+
+			selectors: {
+				input: '[name=search-input]',
+				close: '.searchbox-close',
+				prev: '.searchbox-prev',
+				next: '.searchbox-next',
+				gotoline: '.searchbox-info-gotoline',
+				counter: '.searchbox-info-counter',
+				current: '.searchbox-info-counter .current',
+				total: '.searchbox-info-counter .total'
+			}
+		},
+
+		// private methods
+		_create: function() {
+			$.lj.basicWidget.prototype._create.apply(this);
+			this._searchWord = '';
+			this._currentRange;
+
+			this._visible = false;
+			this._bindControls();
+		},
+
+		_getEditor: function() {
+			return window.aceEditor && aceEditor.env.editor || null;
+		},
+
+		_bindControls: function() {
+			var self = this;
+			$.lj.basicWidget.prototype._bindControls.apply(this);
+
+			this.element.bind('keydown', function(ev) {
+				//handle esc
+				if (ev.keyCode === 27) { self.hide(); }
+
+				//handle (shift)-enter
+				//if shift is pressed we search backwards
+				if (ev.keyCode === 13) {
+					ev.preventDefault();
+
+					var word = self._el('input').val().trim(),
+						backwards = ev.shiftKey === true,
+						editor = self._getEditor();
+
+					if (word.length === 0 || !editor) { return; }
+
+					//ei try to go to the line, if ctrl is pressed
+					if (ev.ctrlKey === true && word.match(/^\d+$/)) {
+						editor.gotoLine(+word);
+						return;
+					}
+
+					self.search(word, backwards);
+				}
+			});
+
+			this._el('input')
+				.labeledPlaceholder()
+				.input(function(ev) {
+					var val = this.value.trim();
+					self.element.removeClass(self._cl('showResults'));
+					self._el('gotoline').toggle(!/[^0-9]/.test(val) && val.length > 0);
+				});
+
+			this._input.input();
+
+			var searchHandler = function(backwards, ev) {
+				ev.preventDefault();
+				self.search(self._searchWord, backwards);
+			};
+
+			this.element
+				.on('click', this._s('close'), this.hide.bind(this))
+				.on('click', this._s('next'), searchHandler.bind(this, false))
+				.on('click', this._s('prev'), searchHandler.bind(this, true)); //prev direction
+		},
+
+		/**
+		 * Search a text for a word.
+		 *
+		 * @param {string} word A word to search.
+		 * @param {boolean} backwards Whether to search backwards
+		 */
+		search: function(word, backwards) {
+			if (word.length === 0) { return; }
+
+			var editor = this._getEditor(),
+				options = {
+					backwards: !!backwards,
+					//search will be case sensitive if the words contains letters in uppercase
+					caseSensitive: /[A-Z]/.test(word)
+				};
+
+			if (!editor) { return; }
+
+			if (word !== this._searchWord) {
+				editor.find(word, options);
+				this._searchWord = word;
+			} else {
+				if (backwards) {
+					editor.findPrevious();
+				} else {
+					editor.findNext();
+				}
+			}
+
+			this._currentRange = editor.selection.getRange();
+			this._updateSearchResultsCounter();
+		},
+
+		/**
+		 * Update the the number of current highlighted word in search results.
+		 */
+		_updateSearchResultsCounter: function() {
+			var editor = this._getEditor();
+
+			if (!editor) { return; }
+
+			var ranges = editor.$search.findAll(editor.getSession());
+			if (!ranges || !ranges.length) {
+				this._el('current').html(0);
+				this._el('total').html(0);
+				return;
+			}
+
+			var crtIdx = -1;
+			var cur = this._currentRange;
+			if (cur) {
+				//compareRange function confuses array sort, so we replace
+				//it with our own naive implementation
+				ranges.sort(function(r1, r2) {
+					return r1.start.row < r2.start.row ? -1 : 
+									r1.start.row > r2.start.row ? 1 : 
+										r1.start.column - r2.start.column;
+				});
+
+				var range;
+				var start = cur.start;
+				var end = cur.end;
+				for (var i = 0, l = ranges.length; i < l; ++i) {
+					range = ranges[i];
+					if (range.isStart(start.row, start.column) && range.isEnd(end.row, end.column)) {
+						crtIdx = i;
+						break;
+					}
+				}
+			}
+
+			this.element.addClass(this._cl('showResults'));
+			this._el('current').html(++crtIdx);
+			this._el('total').html(ranges.length);
+		},
+
+		/**
+		 * Show dialog.
+		 */
+		show: function() {
+			if (!this._enabled) { return; }
+
+			if (!this._visible) {
+				this.element
+					.addClass(this._cl('preshow'))
+					.addClass(this._cl('show'));
+			}
+
+			this._el('input').focus().select();
+			this._visible = true;
+		},
+
+		/**
+		 * Hide dialog.
+		 */
+		hide: function() {
+			this.element
+					.removeClass(this._cl('preshow'))
+					.removeClass(this._cl('show'));
+
+			var editor = this._getEditor();
+			if (editor) {
+				editor.focus();
+			}
+
+			this._visible = false;
+		},
+
+		/**
+		 * Enable the widget. Only enabled widget can be shown.
+		 */
+		enable: function() {
+			this._enabled = true;
+
+			$.lj.basicWidget.prototype.enable.apply(this);
+		},
+
+		/**
+		 * Disable the widget.
+		 */
+		disable: function() {
+			this._enabled = false;
+			this.hide();
+			$.lj.basicWidget.prototype.disable.apply(this);
+		},
+
+		/**
+		 * Find element inside the widget and return it. Note, that function caches the elements
+		 * and assigns them ti the widget object with the name _{name}
+		 *
+		 * @param {string} name Name of the selector to search in this.options.selectors
+		 */
+		_el: function(name) {
+			var method = '_' + name;
+
+			if (!this[method]) { this[method] = this.element.find(this.options.selectors[name]); };
+
+			return this[method];
+		},
+
+		/**
+		 * Fetch the class name from the options.
+		 *
+		 * @param {string} name Name of the class name to search in this.options.classNames.
+		 */
+		_cl: function(name) {
+				return this.options.classNames[name];
+		},
+
+		/**
+		 * Fetch the selector from the options.
+		 *
+		 * @param {string} name Name of the selector to search in this.options.selectors
+		 */
+		_s: function(name) {
+				return this.options.selectors[name];
+		}
+	});
+
+})(jQuery, window);
+
+

Modified: trunk/htdocs/js/s2edit/s2edit.js
===================================================================
--- trunk/htdocs/js/s2edit/s2edit.js	2012-02-15 09:15:04 UTC (rev 21189)
+++ trunk/htdocs/js/s2edit/s2edit.js	2012-02-15 09:33:38 UTC (rev 21190)
@@ -65,9 +65,9 @@
 				turboButton = jQuery('.turbo-mode'),
 				toggleTurboButton = function(enable) {
 					if (enable) {
-						turboButton.val('Back to old editor');
+						turboButton.html('Back to old editor');
 					} else {
-						turboButton.val('Show new editor');
+						turboButton.html('Show new editor');
 					}
 				};
 
@@ -91,7 +91,7 @@
 		},
 
 		isAceSupported: function() {
-			return !(jQuery.browser.opera || (jQuery.browser.msie && jQuery.browser.version));
+			return !(jQuery.browser.opera || (jQuery.browser.msie && +jQuery.browser.version < 9));
 		},
 
 		aceInit: function() {
@@ -109,7 +109,8 @@
 		toggleEditor: function() {
 			if (!this.isAceSupported()) { return; }
 			var textarea = jQuery('#main'),
-				editorEl = jQuery('#editor');
+				editorEl = jQuery('#editor'),
+				searchbox = jQuery('#searchbox');
 
 			if (!s2isAceActive()) {
 				textarea.hide();
@@ -129,14 +130,20 @@
 							s2sense(e.data.text.charCodeAt(0));
 						}
 					});
+
+					searchbox.s2editSearchBox({
+						textarea: textarea
+					});
 				}
 
 				aceEditor.getSession().setValue(textarea.val());
+				searchbox.s2editSearchBox('enable');
 				s2settings.save('useAce', true);
 			} else {
 				textarea.val(aceEditor.getSession().getValue());
 				textarea.show();
 				editorEl.hide();
+				searchbox.s2editSearchBox('disable');
 				s2settings.save('useAce', false);
 			}
 		},

Modified: trunk/htdocs/stc/s2edit.css
===================================================================
--- trunk/htdocs/stc/s2edit.css	2012-02-15 09:15:04 UTC (rev 21189)
+++ trunk/htdocs/stc/s2edit.css	2012-02-15 09:33:38 UTC (rev 21190)
@@ -279,3 +279,79 @@
 	white-space: nowrap;
 }
 
+.searchbox {
+	visibility: hidden;
+	opacity: 0;
+	position: absolute;
+	top: 1px;
+	right: 20px;
+	width: 300px;
+	margin: 0;
+	padding: 10px 15px 7px;
+	-moz-border-radius-bottomleft: 10px;
+	border-bottom-left-radius: 10px;
+	-webkit-box-shadow: -3px 3px 5px rgba(128, 128, 128, 0.5);
+	-moz-box-shadow: -3px 3px 5px rgba(128, 128, 128, 0.5);
+	box-shadow: -3px 3px 5px rgba(128, 128, 128, 0.5);
+	background: #E8E8E8;
+	z-index: 5;
+	}
+.searchbox-preshow {
+	visibility: visible;
+	opacity: 0;
+	}
+.searchbox-show {
+	visibility: visible;
+	opacity: 1;
+	-webkit-transition: opacity 0.3s ease;
+	-moz-transition: opacity 0.3s ease;
+	-o-transition: opacity 0.3s ease;
+	transition: opacity 0.3s ease;
+	}
+	.searchbox-close {
+		position: absolute;
+		top: 3px;
+		right: 5px;
+		width: 16px;
+		height: 16px;
+		margin: 0;
+		padding: 0;
+		background: url("/img/icons/popup-close.png?v=1") no-repeat 0 0;
+		font: 0/0 serif;
+		cursor: pointer;
+		}
+	.searchbox-close:hover {
+		background-position: 0 -16px;
+		}
+	.searchbox input[name="search-input"] {
+		width: 200px;
+		margin: 0 5px 0 0;
+		padding: 2px 1px 2px 1px;
+		}
+	.searchbox-done input[name="search-input"] {
+		width: 150px;
+		padding: 2px 51px 2px 1px;
+		}
+	.searchbox-wrapper {
+		position: relative;
+		}
+		.searchbox-wrapper .placeholder-label {
+			top: 3px;
+			}
+	.searchbox-info {
+		display: block;
+		margin: 0;
+		padding: 3px 0 0;
+		font-size: smaller;
+		color: #000;
+		}
+	.searchbox-info-counter {
+		display: none;
+		position: absolute;
+		top: 0;
+		right: 15px;
+		color: #808080;
+		}
+	.searchbox-done .searchbox-info-counter {
+		display: block;
+		}

Modified: trunk/templates/Widgets/layereditor.tmpl
===================================================================
--- trunk/templates/Widgets/layereditor.tmpl	2012-02-15 09:15:04 UTC (rev 21189)
+++ trunk/templates/Widgets/layereditor.tmpl	2012-02-15 09:33:38 UTC (rev 21190)
@@ -8,13 +8,20 @@
             <a href="<TMPL_VAR s2doc>" target="_blank">Documentation</a>
         </div>
         <input type="submit" value="Save & Compile" class="compilelink" />
-        <input type="button" value="Show new editor" class="compilelink turbo-mode" />
+        <a href="javascript:void(0);" class="compilelink turbo-mode">Show new editor</a>
     </div>
     <div class="main" id="maindiv">
         <div class="maincontainer">
         <textarea id="main" class="maintext" name="<TMPL_VAR prefix>_s2code" onKeyPress="s2keyPressed(event)" wrap="off"
                   onKeyDown="s2IETabKeyPressedHandler(event)"><TMPL_VAR s2code></textarea>
         </div>
+        <div class="searchbox" id="searchbox">
+            <span class="searchbox-wrapper"><input type="text" name="search-input" value="" size="30" placeholder="Search" /><span class="searchbox-info-counter"><span class="current"></span> of <span class="total"></span></span></span>&nbsp;<button class="searchbox-prev" title="Prev">&uarr;</button>&nbsp;<button class="searchbox-next" title="Next">&darr;</button>
+            <span class="searchbox-info">
+                <span class="searchbox-info-gotoline">Press Ctrl-Enter to go to the line with that number</span>
+            </span>
+            <i class="searchbox-close"></i>
+        </div>
     </div>
         <div class="divider" id="outputdivider" onMouseDown="s2startDrag(event)"
                 onMouseUp="s2endDrag(event)"

Tags: can3p, css, dpetrov, js, livejournal, pm, tmpl, txt
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 0 comments