rethoth 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +26 -0
  3. data/bin/thoth +233 -0
  4. data/lib/proto/config.ru +45 -0
  5. data/lib/proto/thoth.conf.sample +206 -0
  6. data/lib/thoth/cache.rb +53 -0
  7. data/lib/thoth/config.rb +158 -0
  8. data/lib/thoth/controller/admin.rb +75 -0
  9. data/lib/thoth/controller/api/comment.rb +73 -0
  10. data/lib/thoth/controller/api/page.rb +134 -0
  11. data/lib/thoth/controller/api/post.rb +122 -0
  12. data/lib/thoth/controller/api/tag.rb +59 -0
  13. data/lib/thoth/controller/archive.rb +50 -0
  14. data/lib/thoth/controller/comment.rb +173 -0
  15. data/lib/thoth/controller/main.rb +193 -0
  16. data/lib/thoth/controller/media.rb +172 -0
  17. data/lib/thoth/controller/page.rb +167 -0
  18. data/lib/thoth/controller/post.rb +310 -0
  19. data/lib/thoth/controller/search.rb +86 -0
  20. data/lib/thoth/controller/tag.rb +107 -0
  21. data/lib/thoth/controller.rb +48 -0
  22. data/lib/thoth/errors.rb +35 -0
  23. data/lib/thoth/helper/admin.rb +86 -0
  24. data/lib/thoth/helper/cookie.rb +45 -0
  25. data/lib/thoth/helper/error.rb +122 -0
  26. data/lib/thoth/helper/pagination.rb +131 -0
  27. data/lib/thoth/helper/wiki.rb +77 -0
  28. data/lib/thoth/helper/ysearch.rb +89 -0
  29. data/lib/thoth/importer/pants.rb +81 -0
  30. data/lib/thoth/importer/poseidon.rb +54 -0
  31. data/lib/thoth/importer/thoth.rb +103 -0
  32. data/lib/thoth/importer.rb +131 -0
  33. data/lib/thoth/layout/default.rhtml +47 -0
  34. data/lib/thoth/middleware/minify.rb +82 -0
  35. data/lib/thoth/migrate/001_create_schema.rb +108 -0
  36. data/lib/thoth/migrate/002_add_media_size.rb +37 -0
  37. data/lib/thoth/migrate/003_add_post_draft.rb +38 -0
  38. data/lib/thoth/migrate/004_add_comment_email.rb +37 -0
  39. data/lib/thoth/migrate/005_add_page_position.rb +37 -0
  40. data/lib/thoth/migrate/006_add_comment_close_delete.rb +43 -0
  41. data/lib/thoth/migrate/007_add_comment_summary.rb +37 -0
  42. data/lib/thoth/model/comment.rb +216 -0
  43. data/lib/thoth/model/media.rb +87 -0
  44. data/lib/thoth/model/page.rb +204 -0
  45. data/lib/thoth/model/post.rb +262 -0
  46. data/lib/thoth/model/tag.rb +80 -0
  47. data/lib/thoth/model/tags_posts_map.rb +34 -0
  48. data/lib/thoth/monkeypatch/sequel/model/errors.rb +37 -0
  49. data/lib/thoth/plugin/thoth_delicious.rb +105 -0
  50. data/lib/thoth/plugin/thoth_flickr.rb +86 -0
  51. data/lib/thoth/plugin/thoth_pinboard.rb +98 -0
  52. data/lib/thoth/plugin/thoth_tags.rb +68 -0
  53. data/lib/thoth/plugin/thoth_twitter.rb +175 -0
  54. data/lib/thoth/plugin.rb +59 -0
  55. data/lib/thoth/public/css/admin.css +223 -0
  56. data/lib/thoth/public/css/thoth.css +592 -0
  57. data/lib/thoth/public/images/admin-sprite.png +0 -0
  58. data/lib/thoth/public/images/thoth-sprite.png +0 -0
  59. data/lib/thoth/public/js/admin/comments.js +116 -0
  60. data/lib/thoth/public/js/admin/name.js +244 -0
  61. data/lib/thoth/public/js/admin/tagcomplete.js +332 -0
  62. data/lib/thoth/public/js/lazyload-min.js +4 -0
  63. data/lib/thoth/public/js/thoth.js +203 -0
  64. data/lib/thoth/public/robots.txt +5 -0
  65. data/lib/thoth/version.rb +37 -0
  66. data/lib/thoth/view/admin/index.rhtml +1 -0
  67. data/lib/thoth/view/admin/login.rhtml +23 -0
  68. data/lib/thoth/view/admin/toolbar.rhtml +117 -0
  69. data/lib/thoth/view/admin/welcome.rhtml +58 -0
  70. data/lib/thoth/view/archive/index.rhtml +24 -0
  71. data/lib/thoth/view/comment/comment.rhtml +47 -0
  72. data/lib/thoth/view/comment/delete.rhtml +15 -0
  73. data/lib/thoth/view/comment/form.rhtml +81 -0
  74. data/lib/thoth/view/comment/index.rhtml +68 -0
  75. data/lib/thoth/view/comment/list.rhtml +48 -0
  76. data/lib/thoth/view/media/delete.rhtml +15 -0
  77. data/lib/thoth/view/media/edit.rhtml +12 -0
  78. data/lib/thoth/view/media/form.rhtml +7 -0
  79. data/lib/thoth/view/media/list.rhtml +35 -0
  80. data/lib/thoth/view/media/media.rhtml +44 -0
  81. data/lib/thoth/view/media/new.rhtml +7 -0
  82. data/lib/thoth/view/page/delete.rhtml +15 -0
  83. data/lib/thoth/view/page/edit.rhtml +15 -0
  84. data/lib/thoth/view/page/form.rhtml +57 -0
  85. data/lib/thoth/view/page/index.rhtml +9 -0
  86. data/lib/thoth/view/page/list.rhtml +49 -0
  87. data/lib/thoth/view/page/new.rhtml +15 -0
  88. data/lib/thoth/view/post/comments.rhtml +12 -0
  89. data/lib/thoth/view/post/compact.rhtml +48 -0
  90. data/lib/thoth/view/post/delete.rhtml +15 -0
  91. data/lib/thoth/view/post/edit.rhtml +15 -0
  92. data/lib/thoth/view/post/form.rhtml +83 -0
  93. data/lib/thoth/view/post/index.rhtml +48 -0
  94. data/lib/thoth/view/post/list.rhtml +61 -0
  95. data/lib/thoth/view/post/new.rhtml +15 -0
  96. data/lib/thoth/view/post/tiny.rhtml +4 -0
  97. data/lib/thoth/view/search/index.rhtml +45 -0
  98. data/lib/thoth/view/tag/index.rhtml +34 -0
  99. data/lib/thoth/view/thoth/css.rhtml +9 -0
  100. data/lib/thoth/view/thoth/footer.rhtml +15 -0
  101. data/lib/thoth/view/thoth/header.rhtml +23 -0
  102. data/lib/thoth/view/thoth/index.rhtml +11 -0
  103. data/lib/thoth/view/thoth/js.rhtml +6 -0
  104. data/lib/thoth/view/thoth/sidebar.rhtml +38 -0
  105. data/lib/thoth/view/thoth/util/pager.rhtml +23 -0
  106. data/lib/thoth/view/thoth/util/simple_pager.rhtml +15 -0
  107. data/lib/thoth/view/thoth/util/table_sortheader.rhtml +20 -0
  108. data/lib/thoth.rb +394 -0
  109. metadata +409 -0
@@ -0,0 +1,244 @@
1
+ Thoth.Admin = Thoth.Admin || {};
2
+
3
+ Thoth.Admin.Name = function () {
4
+ // Shorthand.
5
+ var d = document,
6
+ Y = YAHOO,
7
+ yut = Y.util,
8
+ yuc = yut.Connect,
9
+ yud = yut.Dom,
10
+ yue = yut.Event;
11
+
12
+ // -- Private Variables ------------------------------------------------------
13
+ var conn,
14
+ data,
15
+ delay,
16
+ originalName,
17
+ regexp = /^[0-9a-z_-]+$/i,
18
+ self;
19
+
20
+ // -- Private Methods --------------------------------------------------------
21
+
22
+ /**
23
+ * Submits an Ajax request to determine if the current name is valid and not
24
+ * already taken.
25
+ *
26
+ * @method checkName
27
+ * @param {HTMLElement} el name input element
28
+ * @param {String} name name to check
29
+ * @private
30
+ */
31
+ function checkName(el, name) {
32
+ if (conn && yuc.isCallInProgress(conn)) {
33
+ yuc.abort(conn);
34
+ }
35
+
36
+ var url = '/api/' + (yud.hasClass(el, 'name-post') ? 'post' : 'page') +
37
+ '/check_name?name=' + encodeURIComponent(name);
38
+
39
+ conn = yuc.asyncRequest('GET', url, {
40
+ argument: el,
41
+ success : handleCheckResponse,
42
+ timeout : 1000
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Hides the error message associated with the specified name input element.
48
+ *
49
+ * @method hideError
50
+ * @param {HTMLElement} el name input element
51
+ * @private
52
+ */
53
+ function hideError(el) {
54
+ var errorEl = yud.get(el.id + '-error');
55
+
56
+ if (errorEl) {
57
+ errorEl.parentNode.removeChild(errorEl);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Shows an error message associated with the specified name input element.
63
+ *
64
+ * @method showError
65
+ * @param {HTMLElement} el name input element
66
+ * @param {String} message error message
67
+ * @private
68
+ */
69
+ function showError(el, message) {
70
+ var errorEl = yud.get(el.id + '-error');
71
+
72
+ if (!errorEl) {
73
+ errorEl = d.createElement('p');
74
+
75
+ errorEl.id = el.id + '-error';
76
+ errorEl.className = 'flash error';
77
+
78
+ yud.insertAfter(errorEl, el);
79
+ }
80
+
81
+ errorEl.innerHTML = '';
82
+ errorEl.appendChild(d.createTextNode(message));
83
+ }
84
+
85
+ /**
86
+ * Submits an Ajax request for a name suggestion based on the contents of the
87
+ * specified title input element.
88
+ *
89
+ * @method suggestName
90
+ * @param {HTMLElement} el title input element
91
+ * @param {String} title title string
92
+ * @private
93
+ */
94
+ function suggestName(el, title) {
95
+ if (conn && yuc.isCallInProgress(conn)) {
96
+ yuc.abort(conn);
97
+ }
98
+
99
+ var url = '/api/' + (yud.hasClass(el, 'title-post') ? 'post' : 'page') +
100
+ '/suggest_name?title=' + encodeURIComponent(title);
101
+
102
+ conn = yuc.asyncRequest('GET', url, {
103
+ argument: el,
104
+ success : handleSuggestResponse,
105
+ timeout : 1000
106
+ });
107
+ }
108
+
109
+ // -- Private Event Handlers -------------------------------------------------
110
+
111
+ /**
112
+ * Handles Ajax check_name responses.
113
+ *
114
+ * @method handleCheckResponse
115
+ * @param {Object} response response object
116
+ * @private
117
+ */
118
+ function handleCheckResponse(response) {
119
+ var data
120
+
121
+ try {
122
+ data = Y.lang.JSON.parse(response.responseText);
123
+ } catch (e) {
124
+ return;
125
+ }
126
+
127
+ if (!data.valid) {
128
+ showError(response.argument, 'Names may only contain letters, numbers, ' +
129
+ 'underscores, and dashes, and may not be entirely numeric.');
130
+ } else if (!data.unique && response.argument.value !== originalName) {
131
+ showError(response.argument, 'This name is already taken.');
132
+ } else {
133
+ hideError(response.argument);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Handles keypress events on name input fields.
139
+ *
140
+ * @method handleKeyPress
141
+ * @param {Event} e event object
142
+ * @private
143
+ */
144
+ function handleKeyPress(e) {
145
+ var c = String.fromCharCode(yue.getCharCode(e)),
146
+ el = yue.getTarget(e);
147
+
148
+ if (e.isChar) {
149
+ if (regexp.test(c)) {
150
+ if (/[A-Z]/.test(c)) {
151
+ yue.stopEvent(e);
152
+ el.value += c.toLowerCase();
153
+ }
154
+ } else {
155
+ yue.stopEvent(e);
156
+
157
+ if (c === ' ') {
158
+ el.value += '-';
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Handles keyup events on name input fields.
166
+ *
167
+ * @method handleKeyUp
168
+ * @param {Event} e event object
169
+ * @private
170
+ */
171
+ function handleKeyUp(e) {
172
+ var el = yue.getTarget(e);
173
+
174
+ clearTimeout(delay);
175
+
176
+ if (el.value) {
177
+ yue.removeListener('title', 'change', handleTitleChange);
178
+
179
+ delay = setTimeout(function () {
180
+ checkName(el, el.value);
181
+ }, 200);
182
+ } else {
183
+ hideError(el);
184
+ yue.on('title', 'change', handleTitleChange, self, true);
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Handles Ajax suggest_name responses.
190
+ *
191
+ * @method handleSuggestResponse
192
+ * @param {Object} response response object
193
+ * @private
194
+ */
195
+ function handleSuggestResponse(response) {
196
+ try {
197
+ yud.get('name').value = Y.lang.JSON.parse(response.responseText).name;
198
+ } catch (e) {}
199
+ }
200
+
201
+ /**
202
+ * Handles change events on title input fields.
203
+ *
204
+ * @method handleTitleChange
205
+ * @param {Event} e event object
206
+ * @private
207
+ */
208
+ function handleTitleChange(e) {
209
+ var el = yue.getTarget(e);
210
+
211
+ if (el.value) {
212
+ suggestName(el, el.value);
213
+ }
214
+ }
215
+
216
+ return {
217
+ // -- Public Methods -------------------------------------------------------
218
+
219
+ /**
220
+ * Initializes the Name module on this page.
221
+ *
222
+ * @method init
223
+ */
224
+ init: function () {
225
+ var name = yud.get('name');
226
+
227
+ self = this;
228
+
229
+ // Listen for keys on name input fields.
230
+ yue.on(name, 'keypress', handleKeyPress, self, true);
231
+ yue.on(name, 'keyup', handleKeyUp, self, true);
232
+
233
+ if (!name.value.length) {
234
+ // Listen for changes to title input fields.
235
+ yue.on('title', 'change', handleTitleChange, self, true);
236
+ } else {
237
+ originalName = name.value;
238
+ }
239
+ },
240
+ };
241
+ }();
242
+
243
+ YAHOO.util.Event.onContentReady('doc', Thoth.Admin.Name.init, Thoth.Admin.Name,
244
+ true);
@@ -0,0 +1,332 @@
1
+ Thoth.Admin = Thoth.Admin || {};
2
+
3
+ Thoth.Admin.TagComplete = function () {
4
+ // Shorthand.
5
+ var d = document,
6
+ Y = YAHOO,
7
+ yut = Y.util,
8
+ yuc = yut.Connect,
9
+ yud = yut.Dom,
10
+ yue = yut.Event;
11
+
12
+ // -- Private Variables ------------------------------------------------------
13
+ var cache = {},
14
+ conn,
15
+ data;
16
+
17
+ // -- Private Methods --------------------------------------------------------
18
+
19
+ /**
20
+ * Gets the last tag in the specified input element, or <i>null</i> if there
21
+ * are no tags.
22
+ *
23
+ * @method getLastTag
24
+ * @param {HTMLElement|String} el input element
25
+ * @return {String|null} last tag or <i>null</i> if there are no tags
26
+ * @private
27
+ */
28
+ function getLastTag(el) {
29
+ if (tag = getTags(el).pop()) {
30
+ return tag;
31
+ }
32
+
33
+ return null;
34
+ }
35
+
36
+ /**
37
+ * Returns a reference to the suggestion list element associated with the
38
+ * specified tag input element. If the specified element is a suggestion list
39
+ * element, it will be returned.
40
+ *
41
+ * @method getSuggestEl
42
+ * @param {HTMLElement|String} el input element
43
+ * @return {HTMLElement} suggestion list element or <i>null</i> if not found
44
+ * @private
45
+ */
46
+ function getSuggestEl(el) {
47
+ el = yud.get(el);
48
+
49
+ if (yud.hasClass(el, 'suggested-tags')) {
50
+ return el;
51
+ }
52
+
53
+ return yud.getNextSiblingBy(el, function (node) {
54
+ return yud.hasClass(node, 'suggested-tags');
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Parses a comma-separated list of tags in the specified input element and
60
+ * returns it as an Array.
61
+ *
62
+ * @method getTags
63
+ * @param {HTMLElement|String} el tag input element
64
+ * @return {Array} tags
65
+ * @private
66
+ */
67
+ function getTags(el) {
68
+ var value = (el = yud.get(el)) ? el.value : null;
69
+
70
+ if (value && value.length) {
71
+ return value.split(/,\s*/);
72
+ }
73
+
74
+ return [];
75
+ }
76
+
77
+ /**
78
+ * Replaces the last tag in the specified tag input element with the specified
79
+ * tag.
80
+ *
81
+ * @method replaceLastTag
82
+ * @param {HTMLElement|String} el tag input element
83
+ * @param {String} tag tag text
84
+ * @private
85
+ */
86
+ function replaceLastTag(el, tag) {
87
+ var tags = getTags(el = yud.get(el));
88
+
89
+ tags.pop()
90
+ tags.push(tag);
91
+
92
+ el.value = tags.join(', ');
93
+ }
94
+
95
+ /**
96
+ * Sends an Ajax request for tags matching the specified query.
97
+ *
98
+ * @method requestTags
99
+ * @param {HTMLElement} el input element that triggered the request
100
+ * @param {String} query query string
101
+ * @param {Number} (optional) limit maximum number of tags to return
102
+ * @private
103
+ */
104
+ function requestTags(el, query, limit) {
105
+ if (conn && yuc.isCallInProgress(conn)) {
106
+ yuc.abort(conn);
107
+ }
108
+
109
+ var url = '/api/tag/suggest?q=' + encodeURIComponent(query);
110
+
111
+ if (limit) {
112
+ url += '&limit=' + encodeURIComponent(limit);
113
+ }
114
+
115
+ if (cache[url]) {
116
+ handleResponse(cache[url]);
117
+ return;
118
+ }
119
+
120
+ conn = yuc.asyncRequest('GET', url, {
121
+ argument: [url, el],
122
+ success : handleResponse,
123
+ timeout : 1000
124
+ });
125
+ }
126
+
127
+ // -- Private Event Handlers -------------------------------------------------
128
+
129
+ /**
130
+ * Handles keydown events on tag input fields.
131
+ *
132
+ * @method handleKeyDown
133
+ * @param {Event} e event object
134
+ * @private
135
+ */
136
+ function handleKeyDown(e) {
137
+ var charCode = yue.getCharCode(e),
138
+ el = yue.getTarget(e),
139
+ suggestEl;
140
+
141
+ // Autocomplete the current tag when the tab key is pressed.
142
+ if (charCode === 9 && !e.shiftKey && !e.ctrlKey && !e.altKey &&
143
+ !e.metaKey && data.length && this.isVisible(el)) {
144
+ yue.preventDefault(e);
145
+ replaceLastTag(el, data[0][0]);
146
+ this.hide(el);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Handles keyup events on tag input fields.
152
+ *
153
+ * @method handleKeyUp
154
+ * @param {Event} e event object
155
+ * @private
156
+ */
157
+ function handleKeyUp(e) {
158
+ var charCode = yue.getCharCode(e),
159
+ el = yue.getTarget(e),
160
+ tag;
161
+
162
+ if (charCode < 46 && charCode !== 8 && charCode !== 32) {
163
+ return;
164
+ }
165
+
166
+ tag = getLastTag(el);
167
+
168
+ if (tag) {
169
+ requestTags(el, tag);
170
+ } else {
171
+ this.hide(el);
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Handles Ajax responses.
177
+ *
178
+ * @method handleResponse
179
+ * @param {Object} response response object
180
+ * @private
181
+ */
182
+ function handleResponse(response) {
183
+ if (!cache[response.argument[0]]) {
184
+ cache[response.argument[0]] = response;
185
+ }
186
+
187
+ try {
188
+ data = Y.lang.JSON.parse(response.responseText);
189
+ Thoth.Admin.TagComplete.refresh(response.argument[1], data);
190
+ } catch (e) {}
191
+ }
192
+
193
+ /**
194
+ * Handles clicks on the tag suggestions.
195
+ *
196
+ * @method handleTagClick
197
+ * @param {Event} e event object
198
+ * @param {HTMLElement} el tag input element
199
+ * @private
200
+ */
201
+ function handleTagClick(e, el) {
202
+ var a = yue.getTarget(e);
203
+
204
+ if (a.tagName.toLowerCase() !== 'a') {
205
+ return;
206
+ }
207
+
208
+ yue.preventDefault(e);
209
+ replaceLastTag(el, a.getAttribute('tagText'));
210
+ el.focus();
211
+
212
+ Thoth.Admin.TagComplete.hide(el);
213
+ }
214
+
215
+ return {
216
+ // -- Public Methods -------------------------------------------------------
217
+
218
+ /**
219
+ * Initializes the TagComplete module on this page.
220
+ *
221
+ * @method init
222
+ */
223
+ init: function () {
224
+ var self = this,
225
+ list;
226
+
227
+ yud.getElementsByClassName('tags-input', 'input', 'doc', function (el) {
228
+ // Listen for keys on tag input fields.
229
+ yue.on(el, 'keydown', handleKeyDown, self, true);
230
+ yue.on(el, 'keyup', handleKeyUp, self, true);
231
+
232
+ // Turn off browser autocomplete to avoid excess annoyingness.
233
+ el.setAttribute('autocomplete', 'off');
234
+
235
+ // Create a suggestion list element under the input element.
236
+ list = d.createElement('ol');
237
+ list.className = 'suggested-tags hidden';
238
+
239
+ yud.insertAfter(list, el);
240
+ yue.on(list, 'click', handleTagClick, el);
241
+ });
242
+ },
243
+
244
+ /**
245
+ * Clears the suggestions for the specified element.
246
+ *
247
+ * @method clear
248
+ * @param {HTMLElement|String} el input element or suggestion list
249
+ */
250
+ clear: function (el) {
251
+ if (el = getSuggestEl(el)) {
252
+ el.innerHTML = '';
253
+ }
254
+ },
255
+
256
+ /**
257
+ * Hides the suggestions for the specified element.
258
+ *
259
+ * @method hide
260
+ * @param {HTMLElement|String} el input element or suggestion list
261
+ */
262
+ hide: function (el) {
263
+ if (el = getSuggestEl(el)) {
264
+ yud.addClass(el, 'hidden');
265
+ }
266
+ },
267
+
268
+ /**
269
+ * Returns <i>true</i> if suggestions for the specified element are visible,
270
+ * <i>false</i> otherwise.
271
+ *
272
+ * @method isVisible
273
+ * @param {HTMLElement|String} el input element or suggestion list
274
+ * @return <i>true</i> if suggestions are visible, <i>false</i> otherwise
275
+ */
276
+ isVisible: function (el) {
277
+ return !yud.hasClass(getSuggestEl(el), 'hidden');
278
+ },
279
+
280
+ /**
281
+ * Refreshes the tag suggestions for the specified element.
282
+ *
283
+ * @method refresh
284
+ * @param {HTMLElement|String} el input element or suggestion list
285
+ * @param {Array} (optional) tags suggested tags
286
+ */
287
+ refresh: function (el, tags) {
288
+ var a, i, li, tag;
289
+
290
+ if (!(el = getSuggestEl(el))) {
291
+ return;
292
+ }
293
+
294
+ this.clear(el);
295
+
296
+ if (!tags || tags.length === 0) {
297
+ this.hide(el);
298
+ return;
299
+ }
300
+
301
+ for (i = 0; i < tags.length; ++i) {
302
+ tag = tags[i];
303
+ li = d.createElement('li');
304
+ a = d.createElement('a');
305
+
306
+ a.href = '#';
307
+ a.appendChild(d.createTextNode(tag[0] + ' (' + tag[1] + ')'));
308
+ a.setAttribute('tagText', tag[0]);
309
+
310
+ li.appendChild(a);
311
+ el.appendChild(li);
312
+ }
313
+
314
+ this.show(el);
315
+ },
316
+
317
+ /**
318
+ * Shows the suggestions for the specified element.
319
+ *
320
+ * @method show
321
+ * @param {HTMLElement|String} el input element or suggestion list
322
+ */
323
+ show: function (el) {
324
+ if (el = getSuggestEl(el)) {
325
+ yud.removeClass(el, 'hidden');
326
+ }
327
+ }
328
+ };
329
+ }();
330
+
331
+ YAHOO.util.Event.onContentReady('doc', Thoth.Admin.TagComplete.init,
332
+ Thoth.Admin.TagComplete, true);
@@ -0,0 +1,4 @@
1
+ LazyLoad=function(){function r(c,b){c=i.createElement(c);var a;for(a in b)b.hasOwnProperty(a)&&c.setAttribute(a,b[a]);return c}function k(c){var b=h[c],a,e;if(b){a=b.callback;e=b.urls;e.shift();l=0;if(!e.length){if(a)a.call(b.context||window,b.obj);h[c]=null;j[c].length&&m(c)}}}function w(){if(!d){var c=navigator.userAgent,b=parseFloat,a;d={gecko:0,ie:0,opera:0,webkit:0};if((a=c.match(/AppleWebKit\/(\S*)/))&&a[1])d.webkit=b(a[1]);else if((a=c.match(/MSIE\s([^;]*)/))&&a[1])d.ie=b(a[1]);else if(/Gecko\/(\S*)/.test(c)){d.gecko=
2
+ 1;if((a=c.match(/rv:([^\s\)]*)/))&&a[1])d.gecko=b(a[1])}else if(a=c.match(/Opera\/(\S*)/))d.opera=b(a[1])}}function m(c,b,a,e,s){var n=function(){k(c)},o=c==="css",f,g,p;w();if(b){b=b.constructor===Array?b:[b];if(o||d.gecko&&d.gecko<2||d.opera)j[c].push({urls:[].concat(b),callback:a,obj:e,context:s});else{f=0;for(g=b.length;f<g;++f)j[c].push({urls:[b[f]],callback:f===g-1?a:null,obj:e,context:s})}}if(!(h[c]||!(p=h[c]=j[c].shift()))){q=q||i.head||i.getElementsByTagName("head")[0];b=p.urls;f=0;for(g=
3
+ b.length;f<g;++f){a=b[f];a=o?r("link",{charset:"utf-8","class":"lazyload",href:a,rel:"stylesheet",type:"text/css"}):r("script",{charset:"utf-8","class":"lazyload",src:a});if(d.ie)a.onreadystatechange=function(){var t=this.readyState;if(t==="loaded"||t==="complete"){this.onreadystatechange=null;n()}};else if(o&&(d.gecko||d.webkit))if(d.webkit){p.urls[f]=a.href;u()}else setTimeout(n,50*g);else a.onload=a.onerror=n;q.appendChild(a)}}}function u(){var c=h.css,b;if(c){for(b=v.length;--b;)if(v[b].href===
4
+ c.urls[0]){k("css");break}l+=1;if(c)l<200?setTimeout(u,50):k("css")}}var i=document,q,h={},l=0,j={css:[],js:[]},v=i.styleSheets,d;return{css:function(c,b,a,e){m("css",c,b,a,e)},js:function(c,b,a,e){m("js",c,b,a,e)}}}();