PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/AtomicWiki/webapp/scripts/blog.js

http://atomicwiki.googlecode.com/
JavaScript | 1225 lines | 1162 code | 52 blank | 11 comment | 48 complexity | 810f4c232e96f0b6f542a0d9737e2109 MD5 | raw file
  1. var Dom = YAHOO.util.Dom,
  2. Event = YAHOO.util.Event;
  3. var statusInd;
  4. // the feed currently displayed
  5. var feed = '';
  6. var contextPath = '';
  7. var linkDialog = null;
  8. var moveDialog = null;
  9. /*
  10. * Initalize page controls
  11. */
  12. Event.onDOMReady(function () {
  13. // the XSLT passes some metadata in META tags,
  14. // including feed and context path
  15. Dom.getElementsBy(function (element) {
  16. var name = element.getAttribute('name');
  17. switch (name) {
  18. case 'feed':
  19. feed = element.getAttribute('content');
  20. break;
  21. case 'context':
  22. contextPath = element.getAttribute('content');
  23. break;
  24. }
  25. }, 'meta', document);
  26. initActivityIndicator();
  27. // Init controls for editing a feed
  28. if (document.getElementById('e_title')) {
  29. new Editable('e_title').change.subscribe(modifyFeed);
  30. new Editable('e_subtitle').change.subscribe(modifyFeed);
  31. Event.addListener('upload-images', 'click', function() { new ImageUpload().show(); });
  32. Event.addListener(Dom.getElementsByClassName('del_entry', 'a', document.getElementById('weblog')), 'click', confirmDelete);
  33. Event.addListener('delete-feed', 'click', confirmDelete);
  34. Event.addListener('create-feed-form', 'submit', function () {
  35. window.location = location.pathname + '/' + document.forms['create-feed'].elements['add-feed'].value;
  36. });
  37. }
  38. if (document.getElementById('feedback-block')) {
  39. Event.addListener('feedback-close', 'click', function (ev) {
  40. Dom.setStyle('feedback-block', 'display', 'none');
  41. });
  42. setTimeout(function () {
  43. Dom.setStyle('feedback-block', 'display', 'none');
  44. }, 3000);
  45. }
  46. // In edit mode, initialize the main editor
  47. if (document.getElementById('edit-tabs'))
  48. new BlogEditor();
  49. // Encapsulate all entries in a BlogEntry object
  50. var entries = Dom.getElementsByClassName('entry', 'div', document.getElementById('weblog'));
  51. for (var i = 0; i < entries.length; i++) {
  52. new BlogEntry(feed, entries[i]);
  53. }
  54. YAHOO.widget.Logger.enableBrowserConsole();
  55. SyntaxHighlighter.defaults['auto-links'] = false;
  56. SyntaxHighlighter.all();
  57. initToc();
  58. if (document.getElementById('edit-tabs'))
  59. linkDialog = new LinkEditor();
  60. moveDialog = new MoveDialog();
  61. });
  62. function modifyFeed(type, args) {
  63. reportActivity('Changing feed title');
  64. var callback = {
  65. success: function (response) {
  66. endActivity();
  67. if (response.status != 204) {
  68. reportError("Server reported an error while changing feed title: " + response.status);
  69. }
  70. },
  71. failure: function (response) {
  72. endActivity();
  73. if (response.status == 401) {
  74. reportError("Permission denied. You are not authorized to change this feed.");
  75. } else
  76. reportError("Server reported an error while changing feed title: " + response.responseText);
  77. this.resetValue();
  78. },
  79. scope: this
  80. };
  81. var path = location.pathname;
  82. if (path.charAt(path.length - 1) != '/')
  83. path = path.replace(/^(.*)\/[^\/]+/, '$1/');
  84. var params = 'action=edit-feed&quiet=true&title=' + document.getElementById('e_title').value +
  85. '&subtitle=' + document.getElementById('e_subtitle').value;
  86. YAHOO.util.Connect.asyncRequest('POST', path, callback, params);
  87. }
  88. function toggleBox(ev) {
  89. Event.stopEvent(ev);
  90. var link = Event.getTarget(ev);
  91. var body = Dom.getElementsByClassName('body', 'div', link.parentNode.parentNode)[0];
  92. if (Dom.getStyle(body, 'display') != 'none') {
  93. var animOut = new YAHOO.util.Anim(body, { opacity: { to: 0 } }, 0.5, YAHOO.util.Easing.easeNone);
  94. animOut.onComplete.subscribe(function () {
  95. Dom.setStyle(body, 'display', 'none');
  96. link.innerHTML = 'show';
  97. });
  98. animOut.animate();
  99. } else {
  100. Dom.setStyle(body, 'display', '');
  101. var animIn = new YAHOO.util.Anim( body, { opacity: { to: 1 } }, 0.5, YAHOO.util.Easing.easeNone);
  102. animIn.animate();
  103. link.innerHTML = 'hide';
  104. }
  105. }
  106. function initActivityIndicator() {
  107. statusInd = document.createElement('div');
  108. statusInd.className = 'status-indicator';
  109. statusInd.style.position = 'absolute';
  110. statusInd.style.top = '0';
  111. statusInd.style.right = '0';
  112. statusInd.style.display = 'none';
  113. document.body.appendChild(statusInd);
  114. }
  115. function reportActivity(message) {
  116. Dom.setStyle(statusInd, 'display', '');
  117. statusInd.innerHTML = message;
  118. }
  119. function endActivity() {
  120. Dom.setStyle(statusInd, 'display', 'none');
  121. statusInd.innerHTML = '';
  122. }
  123. function confirmDelete(ev) {
  124. Event.stopEvent(ev);
  125. var link = Event.getTarget(ev);
  126. var href = link.href;
  127. var msg;
  128. if (link.id == 'delete-feed')
  129. msg = 'Are you sure you want to delete the entire feed, including all ' +
  130. 'entries it contains?';
  131. else
  132. msg = 'Are you sure you want to delete this entry?';
  133. var dialog = new YAHOO.widget.SimpleDialog(Dom.generateId(), {
  134. width: '20em',
  135. fixedcenter: true,
  136. effect: { effect:YAHOO.widget.ContainerEffect.FADE, duration: 0.25},
  137. modal: true,
  138. visible: false,
  139. draggable: false
  140. });
  141. var buttons = [
  142. { text: 'Yes', handler: function () { dialog.hide(); dialog.destroy(); window.location = href; } },
  143. { text: 'No', handler: function () { dialog.hide(); dialog.destroy(); } }
  144. ];
  145. dialog.cfg.queueProperty("buttons", buttons);
  146. dialog.setHeader('Confirm Delete');
  147. dialog.setBody(msg);
  148. dialog.cfg.setProperty("icon",YAHOO.widget.SimpleDialog.ICON_WARN);
  149. dialog.render(document.body);
  150. dialog.show();
  151. }
  152. function initToc() {
  153. YAHOO.log('Initializing ToC');
  154. var toc = document.getElementById('toc');
  155. if (toc) {
  156. new YAHOO.util.DDTarget('toc');
  157. var trl = toc.getElementsByTagName('tr');
  158. for (var i = 0; i < trl.length; i++) {
  159. if (trl[i].id)
  160. new YAHOO.example.DDList(trl[i].id);
  161. }
  162. var form = document.forms['reorder-form'];
  163. Event.addListener(form, 'submit', changeEntryOrder);
  164. }
  165. }
  166. function changeEntryOrder() {
  167. YAHOO.log('Saving entry order ...');
  168. var xml = '<order>\n';
  169. var toc = document.getElementById('toc');
  170. var trl = toc.getElementsByTagName('tr');
  171. for (var i = 0; i < trl.length; i++) {
  172. if (trl[i].id)
  173. xml += '<entry id="' + trl[i].id.substring(1) + '"/>';
  174. }
  175. xml += '</order>';
  176. document.forms['reorder-form'].elements['data'].value = xml;
  177. return true;
  178. }
  179. function loadBoxAsync(id, feed, entryId) {
  180. var callback = {
  181. success: function (response) {
  182. document.getElementById(id).innerHTML = response.responseText;
  183. },
  184. failure: function (response) {
  185. YAHOO.log("Loading box failed: " + response.responseText);
  186. }
  187. };
  188. var params = 'feed=' + encodeURIComponent(feed) + '&id=' +
  189. encodeURIComponent(entryId);
  190. YAHOO.log('Loading async box: ' + feed + '#' + id);
  191. YAHOO.util.Connect.asyncRequest('POST', 'async-box.xql', callback, params);
  192. }
  193. function BlogEntry(feed, element) {
  194. if (feed.charAt(feed.length - 1) != '/')
  195. feed = feed.replace(/^(.*)\/[^\/]+/, '$1');
  196. this.feed = feed;
  197. this.path = location.pathname;
  198. if (this.path.charAt(this.path.length - 1) != '/')
  199. this.path = this.path.replace(/^(.*)\/[^\/]+/, '$1');
  200. this.id = element.id;
  201. var moveLinks = Dom.getElementsByClassName('move_entry', 'a', element);
  202. for (var i = 0; i < moveLinks.length; i++) {
  203. Event.addListener(moveLinks[i], 'click', this.move, this, true);
  204. }
  205. this.comments = Dom.getElementsByClassName('comment-panel', 'div', document.getElementById(this.id))[0];
  206. var addComments = Dom.getElementsByClassName('add-comment', 'a', element);
  207. for (var i = 0; i < addComments.length; i++) {
  208. Event.addListener(addComments[i], 'click', this.addComment, this, true);
  209. }
  210. var displayComments = Dom.getElementsByClassName('display-comment', 'a', element);
  211. for (var i = 0; i < displayComments.length; i++) {
  212. Event.addListener(displayComments[i], 'click', this.loadComments, this, true);
  213. }
  214. var commentForm = Dom.getElementsByClassName('comment-form', 'div', element);
  215. if (commentForm.length > 0) {
  216. var button = Dom.getElementsByClassName('comment-send', 'button', commentForm[0])[0];
  217. Event.addListener(button, 'click', this.storeComment, this, true);
  218. button = Dom.getElementsByClassName('comment-cancel', 'button', commentForm[0])[0];
  219. Event.addListener(button, 'click', this.toggleCommentForm, this, true);
  220. }
  221. }
  222. BlogEntry.prototype.move = function (ev) {
  223. Event.stopEvent(ev);
  224. var link = Event.getTarget(ev);
  225. if (!moveDialog)
  226. moveDialog = new MoveDialog();
  227. moveDialog.move.subscribe(function (type, args) {
  228. document.getElementById('mid').value = this.id;
  229. document.forms['moveForm'].submit();
  230. }, this, true);
  231. moveDialog.show();
  232. };
  233. BlogEntry.prototype.loadComments = function (ev) {
  234. if (ev)
  235. Event.stopEvent(ev);
  236. if (Dom.getStyle(this.comments, 'display') != 'none') {
  237. var animOut = new YAHOO.util.Anim(this.comments, { opacity: { to: 0 } }, 0.5, YAHOO.util.Easing.easeNone);
  238. var comments = this.comments;
  239. animOut.onComplete.subscribe(function () { Dom.setStyle(comments, 'display', 'none'); });
  240. animOut.animate();
  241. return;
  242. }
  243. if (!this.comments.hasChildNodes()) {
  244. var callback = {
  245. success: this.commentLoaded,
  246. failure: this.commentLoaded,
  247. scope: this
  248. };
  249. var params = 'action=load-comment&id=' + encodeURIComponent(this.id);
  250. YAHOO.log('Loading comments: ' + params);
  251. YAHOO.util.Connect.asyncRequest('POST', this.path, callback, params);
  252. } else
  253. this.toggleComments();
  254. };
  255. BlogEntry.prototype.reloadComments = function () {
  256. this.comments.innerHTML = '';
  257. this.loadComments(null);
  258. };
  259. BlogEntry.prototype.commentLoaded = function (response) {
  260. if (response.status != 200) {
  261. reportError("Server reported an error while loading comments: " + response.responseText);
  262. return;
  263. }
  264. this.comments.innerHTML = response.responseText;
  265. this.toggleComments();
  266. };
  267. BlogEntry.prototype.toggleComments = function () {
  268. Dom.setStyle(this.comments, 'display', '');
  269. var animIn = new YAHOO.util.Anim( this.comments, { opacity: { to: 1 } }, 0.5, YAHOO.util.Easing.easeNone);
  270. animIn.animate();
  271. };
  272. BlogEntry.prototype.addComment = function (ev) {
  273. Event.stopEvent(ev);
  274. var element = document.getElementById(this.id);
  275. var commentForm = Dom.getElementsByClassName('comment-form', 'div', element);
  276. if (commentForm.length > 0) {
  277. if (!commentForm[0].editor) {
  278. var editor = Dom.getElementsByClassName('comment-editor', 'div', commentForm[0]);
  279. commentForm[0].editor = new WikiEditor(editor[0]);
  280. }
  281. Dom.setStyle(commentForm[0], 'display', '');
  282. var animIn = new YAHOO.util.Anim( commentForm[0], { opacity: { to: 1 } }, 0.25, YAHOO.util.Easing.easeOut);
  283. animIn.animate();
  284. }
  285. };
  286. BlogEntry.prototype.storeComment = function (ev) {
  287. Event.stopEvent(ev);
  288. var callback = {
  289. success: this.commentResponse,
  290. failure: this.commentResponse,
  291. scope: this
  292. };
  293. var form = document.forms['comment-form-' + this.id];
  294. var params = 'action=store-comment&id=' + this.id +
  295. '&name=' + form.elements['comment-name'].value +
  296. '&email=' + form.elements['comment-email'].value +
  297. '&content=' + form.elements['comment-text'].value;
  298. YAHOO.util.Connect.asyncRequest('POST', this.path, callback, params);
  299. };
  300. BlogEntry.prototype.commentResponse = function (response) {
  301. if (response.status != 200) {
  302. reportError("Server reported error while storing comment: " + response.responseText);
  303. return;
  304. }
  305. // hide comment form
  306. this.toggleCommentForm();
  307. // increase comment count
  308. var element = document.getElementById(this.id);
  309. var commentCount = Dom.getElementsByClassName('comment-count', 'span', element);
  310. if (commentCount.length > 0) {
  311. var count = commentCount[0].innerHTML;
  312. commentCount[0].innerHTML = ++count;
  313. }
  314. this.reloadComments();
  315. };
  316. BlogEntry.prototype.toggleCommentForm = function () {
  317. var element = document.getElementById(this.id);
  318. var commentForm = Dom.getElementsByClassName('comment-form', 'div', element);
  319. if (commentForm.length > 0) {
  320. var animOut = new YAHOO.util.Anim( commentForm[0], { opacity: { to: 0 } }, 0.25, YAHOO.util.Easing.easeOut);
  321. animOut.onComplete.subscribe(function () { Dom.setStyle(commentForm[0], 'display', 'none'); });
  322. animOut.animate();
  323. }
  324. };
  325. function BlogEditor() {
  326. this.tabs = new YAHOO.widget.TabView('edit-tabs');
  327. var previewTab = this.tabs.getTab(1);
  328. previewTab.on('click', this.preview, null, this);
  329. var textarea = document.getElementById('msgpost');
  330. this.mode = 'wiki';
  331. this.initWikiEditor();
  332. var btnStore = new YAHOO.widget.Button('msgstore');
  333. btnStore.on('click', this.store, null, this);
  334. var btnSave = new YAHOO.widget.Button('msgsave');
  335. btnSave.on('click', this.saveAndContinue, null, this);
  336. var btnCancel = new YAHOO.widget.Button('msgcancel');
  337. btnCancel.on('click', function () {
  338. window.location = window.location.pathname;
  339. }, null, this);
  340. this.title = new Editable('msgtitle');
  341. this.wikiId = new Editable('wikititle');
  342. Event.addListener('edit-tabs', 'keypress', this.keyPressed, this, true);
  343. Event.addListener('toggle-summary', 'click', function () {
  344. var summary = document.getElementById('summaryeditor');
  345. if (Dom.getStyle(summary, 'display') == 'none') {
  346. Dom.setStyle(summary, 'display', '');
  347. document.getElementById('toggle-summary').innerHTML = 'Hide Summary';
  348. } else {
  349. Dom.setStyle(summary, 'display', 'none');
  350. document.getElementById('toggle-summary').innerHTML = 'Edit Summary';
  351. }
  352. }, this, true);
  353. }
  354. BlogEditor.prototype.initWikiEditor = function () {
  355. new WikiEditor(document.getElementById('wikieditor'));
  356. new WikiEditor(document.getElementById('summaryeditor'));
  357. };
  358. BlogEditor.prototype.checkWikiLink = function () {
  359. var id = this.wikiId.getValue();
  360. if (id != '')
  361. return;
  362. var currentTitle = this.title.getValue();
  363. var words = currentTitle.split(/[\s.,:;!?'"]+/, 3);
  364. var newId = '';
  365. for (var i = 0; i < words.length; i++) {
  366. newId += words[i];
  367. }
  368. this.wikiId.setValue(newId);
  369. };
  370. BlogEditor.prototype.preview = function(ev) {
  371. Event.stopEvent(ev);
  372. this.checkAndPreview();
  373. };
  374. BlogEditor.prototype.store = function (ev) {
  375. Event.stopEvent(ev);
  376. this.post(false);
  377. };
  378. BlogEditor.prototype.checkAndPreview = function () {
  379. this.checkWikiLink();
  380. var callback = {
  381. success: this.previewResponse,
  382. failure: this.previewResponse,
  383. scope: this
  384. };
  385. var params = 'action=preview&' + this.encodeData();
  386. YAHOO.util.Connect.asyncRequest('POST', location.pathname, callback, params);
  387. };
  388. BlogEditor.prototype.saveAndContinue = function () {
  389. this.checkWikiLink();
  390. if (!this.checkForm())
  391. return;
  392. var action = document.forms['msgform'].elements['action'].value;
  393. var params = 'action=' + action + '&' + this.encodeData() + '&quiet=true';
  394. var callback = {
  395. success: function (response) {
  396. Dom.setStyle('save-status-ind', 'visibility', 'hidden');
  397. document.getElementById('save-status-text').innerHTML = '';
  398. document.forms['msgform'].elements['action'].value = 'update';
  399. document.forms['msgform'].elements['id'].value = response.responseText;
  400. },
  401. failure: function (response) {
  402. var previewTab = this.tabs.getTab(1);
  403. previewTab.get('contentEl').innerHTML =
  404. '<div class="entry snip last-entry">' +
  405. ' <div class="content error">' +
  406. ' <h2 class="entrytitle">An error occurred</h2>' +
  407. ' <div>' + response.responseText + '</div>' +
  408. ' </div>' +
  409. '</div';
  410. this.tabs.set('activeIndex', 1);
  411. Dom.setStyle('save-status-ind', 'visibility', 'hidden');
  412. document.getElementById('save-status-text').innerHTML = 'Error, not saved ...';
  413. },
  414. scope: this
  415. };
  416. Dom.setStyle('save-status-ind', 'visibility', 'visible');
  417. document.getElementById('save-status-text').innerHTML = 'Saving ...';
  418. YAHOO.util.Connect.asyncRequest('POST', location.pathname, callback, params);
  419. };
  420. BlogEditor.prototype.checkForm = function () {
  421. var title = document.getElementById('msgtitle').value;
  422. if (title == '') {
  423. reportError('You need to provide a title for the entry!');
  424. return false;
  425. }
  426. return true;
  427. };
  428. /**
  429. * Save the entry. To handle errors, the function first sends
  430. * a preview request. If that returns ok, the form will be
  431. * submitted.
  432. */
  433. BlogEditor.prototype.post = function () {
  434. this.checkWikiLink();
  435. if (!this.checkForm())
  436. return;
  437. var params = 'action=preview&' + this.encodeData();
  438. var callback = {
  439. success: function () {
  440. // all ok, submit the form
  441. document.forms['msgform'].submit();
  442. },
  443. failure: function (response) {
  444. var previewTab = this.tabs.getTab(1);
  445. previewTab.get('contentEl').innerHTML =
  446. '<div class="entry snip last-entry">' +
  447. ' <div class="content error">' +
  448. ' <h2 class="entrytitle">An error occurred</h2>' +
  449. ' <div>' + response.responseText + '</div>' +
  450. ' </div>' +
  451. '</div';
  452. this.tabs.set('activeIndex', 1);
  453. },
  454. scope: this
  455. };
  456. YAHOO.util.Connect.asyncRequest('POST', location.pathname, callback, params);
  457. };
  458. BlogEditor.prototype.previewResponse = function (response) {
  459. var previewTab = this.tabs.getTab(1);
  460. YAHOO.log('Status ' + response.status + ': ' + response.statusText);
  461. if (response.status != 200) {
  462. previewTab.get('contentEl').innerHTML =
  463. '<div class="entry snip last-entry">' +
  464. ' <div class="content error">' +
  465. ' <h2 class="entrytitle">An error occurred</h2>' +
  466. ' <div>' + response.responseText + '</div>' +
  467. ' </div>' +
  468. '</div';
  469. } else {
  470. previewTab.get('contentEl').innerHTML = response.responseText;
  471. SyntaxHighlighter.all();
  472. }
  473. };
  474. BlogEditor.prototype.encodeData = function () {
  475. var title = document.getElementById('msgtitle').value;
  476. var wikiLink = document.forms['msgform'].elements['wikititle'].value;
  477. var content = document.getElementById('msgpost').value;
  478. var summary = document.getElementById('msgsummary').value;
  479. return 'id=' + document.forms['msgform'].elements['id'].value +
  480. '&content=' + encodeURIComponent(content) +
  481. '&summary=' + encodeURIComponent(summary) +
  482. '&title=' + encodeURIComponent(title) +
  483. '&wikititle=' + encodeURIComponent(wikiLink) +
  484. '&content-type=' + document.forms['msgform'].elements['content-type'].value;
  485. };
  486. BlogEditor.prototype.keyPressed = function (ev) {
  487. if (ev.ctrlKey) {
  488. var ch = Event.getCharCode(ev);
  489. if (ch == 115) {
  490. Event.stopEvent(ev);
  491. this.saveAndContinue();
  492. } else if (ch == 113) {
  493. this.store(ev);
  494. }
  495. }
  496. };
  497. function WikiEditor(container) {
  498. var interestingNodes = ['input', 'button', 'img', 'select'];
  499. var controls = new Array();
  500. for (var i = 0; i < interestingNodes.length; i++) {
  501. var nl = container.getElementsByTagName(interestingNodes[i]);
  502. for (var j = 0; j < nl.length; j++) {
  503. controls.push(nl[j]);
  504. }
  505. }
  506. for (var i = 0; i < controls.length; i++) {
  507. var node = controls[i];
  508. if (Dom.hasClass(node, 'wikiedit-bold')) {
  509. Event.addListener(node, 'click', function () {
  510. this.surroundWith('**');
  511. }, this, true);
  512. } else if (Dom.hasClass(node, 'wikiedit-em')) {
  513. Event.addListener(node, 'click', function () {
  514. this.surroundWith('__');
  515. }, this, true);
  516. } else if (Dom.hasClass(node, 'wikiedit-code')) {
  517. Event.addListener(node, 'click', function () {
  518. this.surroundWith('$$');
  519. }, this, true);
  520. } else if (Dom.hasClass(node, 'wikiedit-img')) {
  521. Event.addListener(node, 'click', function () {
  522. var dialog = new ImageSelect();
  523. dialog.selected.subscribe(function (type, args) {
  524. this.insertCode('$image(src=' + args[0] + ')');
  525. }, this, true);
  526. dialog.show();
  527. }, this, true);
  528. } else if (Dom.hasClass(node, 'wikiedit-link')) {
  529. Event.addListener(node, 'click', function () {
  530. var sel = this.getSelection();
  531. linkDialog.show(sel);
  532. linkDialog.insert.subscribe(function (type, args) {
  533. this.insertCode('[' + args[1] + '|' + args[0] + ']');
  534. }, this, true);
  535. }, this, true);
  536. } else if (Dom.hasClass(node, 'wikiedit-select-headers')) {
  537. Event.addListener(node, 'change', function (ev) {
  538. var select = Event.getTarget(ev);
  539. var selected = select.options[select.selectedIndex].value;
  540. if (selected != '')
  541. this.formatHeader(selected);
  542. select.selectedIndex = 0;
  543. }, this, true);
  544. } else if (Dom.hasClass(node, 'wikiedit-select-code')) {
  545. Event.addListener(node, 'change', function (ev) {
  546. var select = Event.getTarget(ev);
  547. var selected = select.options[select.selectedIndex].value;
  548. if (selected != '')
  549. this.formatCode(selected);
  550. select.selectedIndex = 0;
  551. }, this, true);
  552. }
  553. }
  554. this.input = container.getElementsByTagName('textarea')[0];
  555. }
  556. WikiEditor.prototype.getSelection = function () {
  557. this.input.focus();
  558. if (typeof document.selection != 'undefined') {
  559. var range = document.selection.createRange();
  560. return range.text;
  561. } else if (typeof this.input.selectionStart != undefined) {
  562. var start = this.input.selectionStart;
  563. var end = this.input.selectionEnd;
  564. return this.input.value.substring(start, end);
  565. }
  566. };
  567. WikiEditor.prototype.surroundWith = function(codeStart, codeEnd) {
  568. if (!codeEnd)
  569. codeEnd = codeStart;
  570. this.input.focus();
  571. if (typeof document.selection != 'undefined') {
  572. var range = document.selection.createRange();
  573. var insText = range.text;
  574. range.text = codeStart + insText + codeEnd;
  575. range = document.selection.createRange();
  576. if (insText.length == 0) {
  577. range.move('character', -codeStart.length);
  578. } else {
  579. range.moveStart('character', codeStart.length + insText.length + codeEnd.length);
  580. }
  581. range.select();
  582. } else if (typeof this.input.selectionStart != undefined) {
  583. var start = this.input.selectionStart;
  584. var end = this.input.selectionEnd;
  585. var insText = this.input.value.substring(start, end);
  586. this.input.value = this.input.value.substr(0, start) + codeStart + insText + codeEnd + this.input.value.substr(end);
  587. var pos;
  588. if (insText.length == 0) {
  589. pos = start + codeStart.length;
  590. } else {
  591. pos = start + codeStart.length + insText.length + codeEnd.length;
  592. }
  593. this.input.selectionStart = pos;
  594. this.input.selectionEnd = pos;
  595. }
  596. };
  597. WikiEditor.prototype.insertCode = function(code) {
  598. this.input.focus();
  599. if (typeof document.selection != 'undefined') {
  600. var range = document.selection.createRange();
  601. range.text = code;
  602. range = document.selection.createRange();
  603. range.move('character', -code.length);
  604. range.select();
  605. } else if (typeof this.input.selectionStart != undefined) {
  606. var start = this.input.selectionStart;
  607. var end = this.input.selectionEnd;
  608. this.input.value = this.input.value.substr(0, start) + code + this.input.value.substr(end);
  609. var pos = start + code.length;
  610. this.input.selectionStart = pos;
  611. this.input.selectionEnd = pos;
  612. }
  613. };
  614. WikiEditor.prototype.formatHeader = function (type) {
  615. switch (type) {
  616. case 'h1':
  617. this.surroundWith('=');
  618. break;
  619. case 'h2':
  620. this.surroundWith('==');
  621. break;
  622. case 'h3':
  623. this.surroundWith('===');
  624. break;
  625. case 'h4':
  626. this.surroundWith('====');
  627. break;
  628. }
  629. };
  630. WikiEditor.prototype.formatCode = function (type) {
  631. if (type.indexOf('--') > -1)
  632. return;
  633. if (type == 'other')
  634. this.surroundWith('{code}\n', '\n{/code}');
  635. else
  636. this.surroundWith('{code lang="' + type + '"}\n', '\n{/code}');
  637. };
  638. /**
  639. * Image upload dialog. This is a singleton object.
  640. *
  641. * @param msg
  642. */
  643. function ImageUpload() {
  644. var dialog = null;
  645. var imageList = null;
  646. var loadIndicator = null;
  647. this.startUpload = function() {
  648. Dom.setStyle(loadIndicator, 'visibility', 'visible');
  649. var uploadHandler = {
  650. upload: function () {
  651. Dom.setStyle(loadIndicator, 'visibility', 'hidden');
  652. imageList.load();
  653. },
  654. failure: function (response) {
  655. alert(response.responseText);
  656. }
  657. };
  658. YAHOO.util.Connect.setForm('uploadForm', true);
  659. YAHOO.util.Connect.asyncRequest('POST', 'upload.xql', uploadHandler);
  660. };
  661. this.show = function() {
  662. dialog.show();
  663. };
  664. dialog = new YAHOO.widget.Dialog(Dom.generateId(), {
  665. width: "500px",
  666. close: true,
  667. fixedcenter: true,
  668. draggable: true,
  669. visible: false,
  670. constraintoviewport: true,
  671. buttons: [ { text: "Done", handler: function () { this.hide(); this.destroy();}, isDefault: true } ]
  672. });
  673. dialog.setHeader('Upload Images');
  674. loadIndicator = Dom.generateId();
  675. var gallery = Dom.generateId();
  676. var uploadBtnId = Dom.generateId();
  677. dialog.setBody(
  678. '<form name="uploadForm" enctype="multipart/form-data" method="POST" action="upload.xql">' +
  679. ' <div class="upload-controls">' +
  680. ' <img id="' + loadIndicator + '" src="' + contextPath + '/images/loading.gif" style="visibility: hidden"/>' +
  681. ' <input class="fileupload" type="file" name="uploadedFile"/>' +
  682. ' <button type="button" id="' + uploadBtnId + '">Upload</button>' +
  683. ' <input type="hidden" name="feed" value="' + feed + '"/>' +
  684. ' </div>' +
  685. ' <div class="gallery" id="' + gallery + '">' +
  686. '</div>' +
  687. '</form>'
  688. );
  689. dialog.render(document.body);
  690. var uploadBtn = document.getElementById(uploadBtnId);
  691. Event.addListener(uploadBtn, 'click', this.startUpload, this);
  692. Dom.setStyle(gallery, 'height', '200px');
  693. imageList = new ImageList(gallery, feed);
  694. dialog.showEvent.subscribe(imageList.load, null, imageList);
  695. };
  696. function ImageSelect() {
  697. this.dialog = null;
  698. this.selectedImage = null;
  699. this.selected = new YAHOO.util.CustomEvent('insert image');
  700. this.show = function() {
  701. this.dialog.show();
  702. };
  703. this.insertImage = function () {
  704. if (this.selectedImage == null)
  705. return;
  706. this.dialog.hide();
  707. this.dialog.destroy();
  708. YAHOO.log('Fire selected event: ' + this.selected);
  709. this.selected.fire(this.selectedImage);
  710. };
  711. var dialogId = Dom.generateId();
  712. this.dialog = new YAHOO.widget.Dialog(dialogId, {
  713. width: "500px",
  714. close: true,
  715. fixedcenter: true,
  716. draggable: true,
  717. visible: false,
  718. constraintoviewport: true,
  719. buttons: [
  720. { text: "Insert", handler: { fn: this.insertImage, scope: this}, isDefault: true },
  721. { text: "Cancel", handler: function () { this.hide(); this.destroy(); }, isDefault: false }
  722. ]
  723. });
  724. var imageListId = Dom.generateId();
  725. this.dialog.setHeader('Insert Image');
  726. this.dialog.setBody('<div class="gallery" id="' + imageListId + '"></div>');
  727. this.dialog.render(document.body);
  728. var imageList = new ImageList(imageListId, feed);
  729. imageList.select.subscribe(function (type, args) { this.selectedImage = args[0]; }, null, this);
  730. Dom.setStyle(imageList.container, 'height', '200px');
  731. this.dialog.showEvent.subscribe(imageList.load, null, imageList);
  732. };
  733. /**
  734. * Queries the database for available images associated with this feed and
  735. * displays them as thumbnails in a div.
  736. *
  737. * @param elem
  738. * @param feed
  739. */
  740. function ImageList(elem, feed) {
  741. if (typeof elem == 'string') {
  742. this.container = document.getElementById(elem);
  743. } else {
  744. this.container = elem;
  745. }
  746. this.feed = feed;
  747. this.selected = null;
  748. this.select = new YAHOO.util.CustomEvent('image selected');
  749. }
  750. ImageList.MIME_TYPES = {
  751. 'image': [ 'image/jpeg', 'image/gif' ]
  752. };
  753. ImageList.checkMimeType = function (mime) {
  754. for (var type in ImageList.MIME_TYPES) {
  755. var list = ImageList.MIME_TYPES[type];
  756. for (var i = 0; i < list.length; i++) {
  757. if (mime == list[i])
  758. return type;
  759. }
  760. }
  761. return 'unknown';
  762. };
  763. ImageList.prototype.load = function () {
  764. this.container.innerHTML = '';
  765. var callback = {
  766. success: this.loaded,
  767. failure: this.loaded,
  768. scope: this
  769. };
  770. var params = 'feed=' + encodeURIComponent(this.feed);
  771. YAHOO.log('Loading images: ' + params);
  772. YAHOO.util.Connect.asyncRequest('POST', 'list-resources.xql', callback, params);
  773. };
  774. ImageList.prototype.loaded = function (response) {
  775. var xml = response.responseXML;
  776. var root = xml.documentElement;
  777. var contextPath = root.getAttribute('context');
  778. var node = root.firstChild;
  779. while (node != null) {
  780. if (node.nodeName == 'resource') {
  781. var path = node.getAttribute('path');
  782. var p = path.lastIndexOf('/');
  783. var name = path.substring(p + 1);
  784. var img = document.createElement('img');
  785. Event.addListener(img, 'load', function () {
  786. if (this.height < 80)
  787. this.style.paddingTop = ((80 - this.height) / 2) + 'px';
  788. }, img, true);
  789. img.setAttribute('title', path);
  790. YAHOO.log('thumbnail path = ' + path);
  791. if (ImageList.checkMimeType(node.getAttribute('mime')) == 'image')
  792. img.setAttribute('src', contextPath + '/thumbs?path=' + path + '&height=64');
  793. else
  794. img.setAttribute('src', contextPath + '/images/misc.png');
  795. img.name = name;
  796. var div = document.createElement('div');
  797. div.className = 'container';
  798. var innerDiv = document.createElement('div');
  799. innerDiv.className = 'thumb';
  800. div.appendChild(innerDiv);
  801. innerDiv.appendChild(img);
  802. var span = document.createElement('div');
  803. span.appendChild(document.createTextNode(name))
  804. innerDiv.appendChild(span);
  805. this.container.appendChild(div);
  806. Event.addListener(img, 'click', this.selectImage, this, true);
  807. }
  808. node = node.nextSibling;
  809. }
  810. };
  811. ImageList.prototype.selectImage = function(ev) {
  812. var img = Event.getTarget(ev);
  813. if (this.selected != null)
  814. Dom.removeClass(this.selected, 'selected');
  815. this.selected = img.parentNode;
  816. Dom.addClass(this.selected, 'selected');
  817. this.select.fire(img.name);
  818. };
  819. function LinkEditor() {
  820. this.dialog = null;
  821. this.insert = new YAHOO.util.CustomEvent('insert link');
  822. this.show = function (selection) {
  823. document.forms['linkForm'].elements['text1'].value = selection;
  824. document.forms['linkForm'].elements['text2'].value = selection;
  825. this.dialog.show();
  826. };
  827. this.insertLink = function () {
  828. this.dialog.hide();
  829. var tab = this.tabs.get('activeIndex');
  830. if (tab == 0) {
  831. var select = document.forms['linkForm'].elements['page-select'];
  832. this.insert.fire(document.forms['linkForm'].elements['text1'].value,
  833. select.options[select.selectedIndex].value);
  834. } else {
  835. this.insert.fire(document.forms['linkForm'].elements['text2'].value,
  836. document.forms['linkForm'].elements['url'].value);
  837. }
  838. this.insert.unsubscribeAll();
  839. };
  840. this.tabs = new YAHOO.widget.TabView('insert-link-tabs');
  841. this.dialog = new YAHOO.widget.Dialog('insertLink', {
  842. width: "500px",
  843. close: true,
  844. fixedcenter: true,
  845. draggable: true,
  846. visible: false,
  847. constraintoviewport: true,
  848. buttons: [
  849. { text: "Insert", handler: { fn: this.insertLink, scope: this}, isDefault: true },
  850. { text: "Cancel", handler: function () { this.hide(); }, isDefault: false }
  851. ]
  852. });
  853. this.dialog.render();
  854. var callback = {
  855. success: function (response) {
  856. var select = document.getElementById('page-select');
  857. select.parentNode.innerHTML = response.responseText;
  858. }
  859. };
  860. YAHOO.util.Connect.asyncRequest('GET', 'list-entries.xql', callback);
  861. };
  862. function MoveDialog() {
  863. this.dialog = null;
  864. this.move = new YAHOO.util.CustomEvent('move entry');
  865. this.show = function () {
  866. if (!this.loaded)
  867. this.load();
  868. this.dialog.show();
  869. };
  870. this.doMove = function () {
  871. var select = document.getElementById('destination-select');
  872. this.move.fire(select.options[select.selectedIndex].value);
  873. this.move.unsubscribeAll();
  874. this.dialog.hide();
  875. };
  876. this.loaded = false;
  877. this.load = function () {
  878. this.loaded = true;
  879. var callback = {
  880. success: function (response) {
  881. var select = document.getElementById('destination-select');
  882. select.parentNode.innerHTML = response.responseText;
  883. }
  884. };
  885. YAHOO.util.Connect.asyncRequest('GET', 'list-feeds.xql', callback);
  886. };
  887. this.dialog = new YAHOO.widget.Dialog('move-dialog', {
  888. width: "500px",
  889. close: true,
  890. fixedcenter: true,
  891. draggable: true,
  892. visible: false,
  893. constraintoviewport: true,
  894. buttons: [
  895. { text: "Move", handler: { fn: this.doMove, scope: this}, isDefault: true },
  896. { text: "Cancel", handler: function () { this.hide(); }, isDefault: false }
  897. ]
  898. });
  899. this.dialog.render();
  900. };
  901. function Editable(input) {
  902. this.input = typeof input == 'string' ? document.getElementById(input) : input;
  903. Dom.setStyle(this.input, 'display', 'none');
  904. var div = document.createElement('div');
  905. div.className = this.input.className;
  906. this.input.parentNode.replaceChild(div, this.input);
  907. this.span = document.createElement('span');
  908. var txt = document.createTextNode(this.input.value.length == 0 ? 'Click to edit' : this.input.value);
  909. this.span.appendChild(txt);
  910. this.toggle = document.createElement('a');
  911. this.toggle.href = '#';
  912. this.toggle.className = 'editable-toggle';
  913. txt = document.createTextNode('edit');
  914. this.toggle.appendChild(txt);
  915. div.appendChild(this.input);
  916. div.appendChild(this.span);
  917. div.appendChild(this.toggle);
  918. Event.addListener(this.span, 'click', this.toggleEditable, this, true);
  919. Event.addListener(this.toggle, 'click', this.toggleEditable, this, true);
  920. Event.addListener(this.input, 'keypress', this.keyPressed, this, true);
  921. this.value = this.input.value;
  922. this.change = new YAHOO.util.CustomEvent('content changed', this);
  923. }
  924. Editable.prototype.setValue = function (value) {
  925. this.span.innerHTML = value;
  926. this.input.value = value;
  927. };
  928. Editable.prototype.getValue = function () {
  929. return this.input.value;
  930. };
  931. Editable.prototype.resetValue = function () {
  932. this.setValue(this.value);
  933. };
  934. Editable.prototype.keyPressed = function (ev) {
  935. var ch = Event.getCharCode(ev);
  936. if (ch == 27 || ch == 10 || ch == 13) {
  937. Event.stopEvent(ev);
  938. this.toggleEditable();
  939. }
  940. };
  941. Editable.prototype.toggleEditable = function () {
  942. if (Dom.getStyle(this.input, 'display') == 'none') {
  943. Dom.setStyle(this.span, 'display', 'none');
  944. Dom.setStyle(this.input, 'display', '');
  945. this.toggle.innerHTML = 'close';
  946. this.input.focus();
  947. this.value = this.input.value;
  948. } else {
  949. this.span.innerHTML = this.input.value.length == 0 ? 'Click to edit' : this.input.value;
  950. Dom.setStyle(this.span, 'display', '');
  951. Dom.setStyle(this.input, 'display', 'none');
  952. this.toggle.innerHTML = 'edit';
  953. YAHOO.log(this.input.value + ' -> ' + this.value);
  954. if (this.input.value != this.value) {
  955. YAHOO.log('Firing...');
  956. this.change.fire(this.input.value);
  957. }
  958. }
  959. };
  960. function reportError(msg) {
  961. YAHOO.log('error: ' + msg);
  962. var dialog = new YAHOO.widget.SimpleDialog(Dom.generateId(), {
  963. width: '300px',
  964. fixedcenter: true,
  965. effect: { effect:YAHOO.widget.ContainerEffect.FADE, duration: 0.25},
  966. modal: true,
  967. visible: false,
  968. draggable: false
  969. });
  970. var buttons = [
  971. { text: 'Ok', handler: function () { dialog.hide(); dialog.destroy(); } }
  972. ];
  973. dialog.cfg.queueProperty("buttons", buttons);
  974. dialog.cfg.setProperty("icon",YAHOO.widget.SimpleDialog.ICON_WARN);
  975. dialog.setHeader('Error');
  976. dialog.setBody(msg);
  977. dialog.render(document.body);
  978. dialog.show();
  979. }
  980. function checkId(id) {
  981. if (id.charAt(0) == '?')
  982. return id.substring(1);
  983. else
  984. return id;
  985. }
  986. var DDM = YAHOO.util.DragDropMgr;
  987. YAHOO.example.DDList = function(id, sGroup, config) {
  988. YAHOO.example.DDList.superclass.constructor.call(this, id, sGroup, config);
  989. this.logger = this.logger || YAHOO;
  990. var el = this.getDragEl();
  991. Dom.setStyle(el, "opacity", 0.67); // The proxy is slightly transparent
  992. this.goingUp = false;
  993. this.lastY = 0;
  994. };
  995. YAHOO.extend(YAHOO.example.DDList, YAHOO.util.DDProxy, {
  996. startDrag: function(x, y) {
  997. this.logger.log(this.id + " startDrag");
  998. // make the proxy look like the source element
  999. var dragEl = this.getDragEl();
  1000. var clickEl = this.getEl();
  1001. Dom.setStyle(clickEl, "visibility", "hidden");
  1002. dragEl.innerHTML = clickEl.innerHTML;
  1003. Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color"));
  1004. Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor"));
  1005. Dom.setStyle(dragEl, "border", "2px solid gray");
  1006. },
  1007. endDrag: function(e) {
  1008. var srcEl = this.getEl();
  1009. var proxy = this.getDragEl();
  1010. // Show the proxy element and animate it to the src element's location
  1011. Dom.setStyle(proxy, "visibility", "");
  1012. var a = new YAHOO.util.Motion(
  1013. proxy, {
  1014. points: {
  1015. to: Dom.getXY(srcEl)
  1016. }
  1017. },
  1018. 0.2,
  1019. YAHOO.util.Easing.easeOut
  1020. )
  1021. var proxyid = proxy.id;
  1022. var thisid = this.id;
  1023. // Hide the proxy and show the source element when finished with the animation
  1024. a.onComplete.subscribe(function() {
  1025. Dom.setStyle(proxyid, "visibility", "hidden");
  1026. Dom.setStyle(thisid, "visibility", "");
  1027. });
  1028. a.animate();
  1029. },
  1030. onDragDrop: function(e, id) {
  1031. // If there is one drop interaction, the li was dropped either on the list,
  1032. // or it was dropped on the current location of the source element.
  1033. if (DDM.interactionInfo.drop.length === 1) {
  1034. // The position of the cursor at the time of the drop (YAHOO.util.Point)
  1035. var pt = DDM.interactionInfo.point;
  1036. // The region occupied by the source element at the time of the drop
  1037. var region = DDM.interactionInfo.sourceRegion;
  1038. // Check to see if we are over the source element's location. We will
  1039. // append to the bottom of the list once we are sure it was a drop in
  1040. // the negative space (the area of the list without any list items)
  1041. if (!region.intersect(pt)) {
  1042. var destEl = Dom.get(id);
  1043. var destDD = DDM.getDDById(id);
  1044. destEl.appendChild(this.getEl());
  1045. destDD.isEmpty = false;
  1046. DDM.refreshCache();
  1047. }
  1048. }
  1049. },
  1050. onDrag: function(e) {
  1051. // Keep track of the direction of the drag for use during onDragOver
  1052. var y = Event.getPageY(e);
  1053. if (y < this.lastY) {
  1054. this.goingUp = true;
  1055. } else if (y > this.lastY) {
  1056. this.goingUp = false;
  1057. }
  1058. this.lastY = y;
  1059. },
  1060. onDragOver: function(e, id) {
  1061. var srcEl = this.getEl();
  1062. var destEl = Dom.get(id);
  1063. // We are only concerned with list items, we ignore the dragover
  1064. // notifications for the list.
  1065. if (destEl.nodeName.toLowerCase() == "tr") {
  1066. var orig_p = srcEl.parentNode;
  1067. var p = destEl.parentNode;
  1068. if (this.goingUp) {
  1069. p.insertBefore(srcEl, destEl); // insert above
  1070. } else {
  1071. p.insertBefore(srcEl, destEl.nextSibling); // insert below
  1072. }
  1073. DDM.refreshCache();
  1074. }
  1075. }
  1076. });