xml.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Distributed under the BSD license:
  3. *
  4. * Copyright (c) 2010, Ajax.org B.V.
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * * Neither the name of Ajax.org B.V. nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. * ***** END LICENSE BLOCK ***** */
  30. define(function(require, exports, module) {
  31. "use strict";
  32. var oop = require("../../lib/oop");
  33. var lang = require("../../lib/lang");
  34. var Range = require("../../range").Range;
  35. var BaseFoldMode = require("./fold_mode").FoldMode;
  36. var TokenIterator = require("../../token_iterator").TokenIterator;
  37. var FoldMode = exports.FoldMode = function(voidElements) {
  38. BaseFoldMode.call(this);
  39. this.voidElements = voidElements || {};
  40. };
  41. oop.inherits(FoldMode, BaseFoldMode);
  42. (function() {
  43. this.getFoldWidget = function(session, foldStyle, row) {
  44. var tag = this._getFirstTagInLine(session, row);
  45. if (tag.closing)
  46. return foldStyle == "markbeginend" ? "end" : "";
  47. if (!tag.tagName || this.voidElements[tag.tagName.toLowerCase()])
  48. return "";
  49. if (tag.selfClosing)
  50. return "";
  51. if (tag.value.indexOf("/" + tag.tagName) !== -1)
  52. return "";
  53. return "start";
  54. };
  55. this._getFirstTagInLine = function(session, row) {
  56. var tokens = session.getTokens(row);
  57. var value = "";
  58. for (var i = 0; i < tokens.length; i++) {
  59. var token = tokens[i];
  60. if (token.type.lastIndexOf("meta.tag", 0) === 0)
  61. value += token.value;
  62. else
  63. value += lang.stringRepeat(" ", token.value.length);
  64. }
  65. return this._parseTag(value);
  66. };
  67. this.tagRe = /^(\s*)(<?(\/?)([-_a-zA-Z0-9:!]*)\s*(\/?)>?)/;
  68. this._parseTag = function(tag) {
  69. var match = tag.match(this.tagRe);
  70. var column = 0;
  71. return {
  72. value: tag,
  73. match: match ? match[2] : "",
  74. closing: match ? !!match[3] : false,
  75. selfClosing: match ? !!match[5] || match[2] == "/>" : false,
  76. tagName: match ? match[4] : "",
  77. column: match[1] ? column + match[1].length : column
  78. };
  79. };
  80. /*
  81. * reads a full tag and places the iterator after the tag
  82. */
  83. this._readTagForward = function(iterator) {
  84. var token = iterator.getCurrentToken();
  85. if (!token)
  86. return null;
  87. var value = "";
  88. var start;
  89. do {
  90. if (token.type.lastIndexOf("meta.tag", 0) === 0) {
  91. if (!start) {
  92. var start = {
  93. row: iterator.getCurrentTokenRow(),
  94. column: iterator.getCurrentTokenColumn()
  95. };
  96. }
  97. value += token.value;
  98. if (value.indexOf(">") !== -1) {
  99. var tag = this._parseTag(value);
  100. tag.start = start;
  101. tag.end = {
  102. row: iterator.getCurrentTokenRow(),
  103. column: iterator.getCurrentTokenColumn() + token.value.length
  104. };
  105. iterator.stepForward();
  106. return tag;
  107. }
  108. }
  109. } while(token = iterator.stepForward());
  110. return null;
  111. };
  112. this._readTagBackward = function(iterator) {
  113. var token = iterator.getCurrentToken();
  114. if (!token)
  115. return null;
  116. var value = "";
  117. var end;
  118. do {
  119. if (token.type.lastIndexOf("meta.tag", 0) === 0) {
  120. if (!end) {
  121. end = {
  122. row: iterator.getCurrentTokenRow(),
  123. column: iterator.getCurrentTokenColumn() + token.value.length
  124. };
  125. }
  126. value = token.value + value;
  127. if (value.indexOf("<") !== -1) {
  128. var tag = this._parseTag(value);
  129. tag.end = end;
  130. tag.start = {
  131. row: iterator.getCurrentTokenRow(),
  132. column: iterator.getCurrentTokenColumn()
  133. };
  134. iterator.stepBackward();
  135. return tag;
  136. }
  137. }
  138. } while(token = iterator.stepBackward());
  139. return null;
  140. };
  141. this._pop = function(stack, tag) {
  142. while (stack.length) {
  143. var top = stack[stack.length-1];
  144. if (!tag || top.tagName == tag.tagName) {
  145. return stack.pop();
  146. }
  147. else if (this.voidElements[tag.tagName]) {
  148. return;
  149. }
  150. else if (this.voidElements[top.tagName]) {
  151. stack.pop();
  152. continue;
  153. } else {
  154. return null;
  155. }
  156. }
  157. };
  158. this.getFoldWidgetRange = function(session, foldStyle, row) {
  159. var firstTag = this._getFirstTagInLine(session, row);
  160. if (!firstTag.match)
  161. return null;
  162. var isBackward = firstTag.closing || firstTag.selfClosing;
  163. var stack = [];
  164. var tag;
  165. if (!isBackward) {
  166. var iterator = new TokenIterator(session, row, firstTag.column);
  167. var start = {
  168. row: row,
  169. column: firstTag.column + firstTag.tagName.length + 2
  170. };
  171. while (tag = this._readTagForward(iterator)) {
  172. if (tag.selfClosing) {
  173. if (!stack.length) {
  174. tag.start.column += tag.tagName.length + 2;
  175. tag.end.column -= 2;
  176. return Range.fromPoints(tag.start, tag.end);
  177. } else
  178. continue;
  179. }
  180. if (tag.closing) {
  181. this._pop(stack, tag);
  182. if (stack.length == 0)
  183. return Range.fromPoints(start, tag.start);
  184. }
  185. else {
  186. stack.push(tag)
  187. }
  188. }
  189. }
  190. else {
  191. var iterator = new TokenIterator(session, row, firstTag.column + firstTag.match.length);
  192. var end = {
  193. row: row,
  194. column: firstTag.column
  195. };
  196. while (tag = this._readTagBackward(iterator)) {
  197. if (tag.selfClosing) {
  198. if (!stack.length) {
  199. tag.start.column += tag.tagName.length + 2;
  200. tag.end.column -= 2;
  201. return Range.fromPoints(tag.start, tag.end);
  202. } else
  203. continue;
  204. }
  205. if (!tag.closing) {
  206. this._pop(stack, tag);
  207. if (stack.length == 0) {
  208. tag.start.column += tag.tagName.length + 2;
  209. return Range.fromPoints(tag.start, end);
  210. }
  211. }
  212. else {
  213. stack.push(tag)
  214. }
  215. }
  216. }
  217. };
  218. }).call(FoldMode.prototype);
  219. });