vapid 0.1.0

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 (433) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.gitmodules +6 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +133 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +10 -0
  8. data/README.md +37 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/vapid +7 -0
  13. data/lib/vapid.rb +24 -0
  14. data/lib/vapid/assets/javascripts/vapid/admin.coffee +2 -0
  15. data/lib/vapid/assets/stylesheets/vapid/admin.scss +72 -0
  16. data/lib/vapid/builder.rb +70 -0
  17. data/lib/vapid/cli.rb +96 -0
  18. data/lib/vapid/controllers/admin_controller.rb +282 -0
  19. data/lib/vapid/controllers/project_controller.rb +41 -0
  20. data/lib/vapid/db/migrate/20170202182310_create_groups.rb +12 -0
  21. data/lib/vapid/db/migrate/20170202183017_create_records.rb +11 -0
  22. data/lib/vapid/db/migrate/20170202185150_create_users.rb +11 -0
  23. data/lib/vapid/directive.rb +168 -0
  24. data/lib/vapid/directives.rb +30 -0
  25. data/lib/vapid/directives/audio.rb +27 -0
  26. data/lib/vapid/directives/group.rb +71 -0
  27. data/lib/vapid/directives/hide.rb +11 -0
  28. data/lib/vapid/directives/href.rb +11 -0
  29. data/lib/vapid/directives/image.rb +14 -0
  30. data/lib/vapid/directives/richtext.rb +15 -0
  31. data/lib/vapid/directives/show.rb +11 -0
  32. data/lib/vapid/directives/text.rb +24 -0
  33. data/lib/vapid/directives/video.rb +28 -0
  34. data/lib/vapid/generator_template/.gitignore +21 -0
  35. data/lib/vapid/generator_template/Gemfile.tt +13 -0
  36. data/lib/vapid/generator_template/README.md +0 -0
  37. data/lib/vapid/generator_template/assets/favicon.ico +0 -0
  38. data/lib/vapid/generator_template/assets/images/.keep +0 -0
  39. data/lib/vapid/generator_template/assets/javascripts/.keep +0 -0
  40. data/lib/vapid/generator_template/assets/stylesheets/.keep +0 -0
  41. data/lib/vapid/generator_template/assets/stylesheets/application.css +120 -0
  42. data/lib/vapid/generator_template/config.ru +3 -0
  43. data/lib/vapid/generator_template/config/database.yml +12 -0
  44. data/lib/vapid/generator_template/config/settings.yml.tt +11 -0
  45. data/lib/vapid/generator_template/templates/index.html.erb +25 -0
  46. data/lib/vapid/generator_template/templates/layouts/default.html.erb +22 -0
  47. data/lib/vapid/models.rb +18 -0
  48. data/lib/vapid/models/group.rb +36 -0
  49. data/lib/vapid/models/record.rb +30 -0
  50. data/lib/vapid/models/user.rb +16 -0
  51. data/lib/vapid/server.rb +93 -0
  52. data/lib/vapid/settings.rb +13 -0
  53. data/lib/vapid/template.rb +79 -0
  54. data/lib/vapid/template/node.rb +91 -0
  55. data/lib/vapid/template/parser.rb +50 -0
  56. data/lib/vapid/vendor/semantic-ui/.versions +4 -0
  57. data/lib/vapid/vendor/semantic-ui/LICENSE +22 -0
  58. data/lib/vapid/vendor/semantic-ui/README.md +7 -0
  59. data/lib/vapid/vendor/semantic-ui/components/accordion.css +252 -0
  60. data/lib/vapid/vendor/semantic-ui/components/accordion.js +610 -0
  61. data/lib/vapid/vendor/semantic-ui/components/accordion.min.css +9 -0
  62. data/lib/vapid/vendor/semantic-ui/components/accordion.min.js +10 -0
  63. data/lib/vapid/vendor/semantic-ui/components/ad.css +275 -0
  64. data/lib/vapid/vendor/semantic-ui/components/ad.min.css +10 -0
  65. data/lib/vapid/vendor/semantic-ui/components/api.js +1167 -0
  66. data/lib/vapid/vendor/semantic-ui/components/api.min.js +10 -0
  67. data/lib/vapid/vendor/semantic-ui/components/breadcrumb.css +124 -0
  68. data/lib/vapid/vendor/semantic-ui/components/breadcrumb.min.css +9 -0
  69. data/lib/vapid/vendor/semantic-ui/components/button.css +3450 -0
  70. data/lib/vapid/vendor/semantic-ui/components/button.min.css +9 -0
  71. data/lib/vapid/vendor/semantic-ui/components/card.css +964 -0
  72. data/lib/vapid/vendor/semantic-ui/components/card.min.css +9 -0
  73. data/lib/vapid/vendor/semantic-ui/components/checkbox.css +625 -0
  74. data/lib/vapid/vendor/semantic-ui/components/checkbox.js +831 -0
  75. data/lib/vapid/vendor/semantic-ui/components/checkbox.min.css +9 -0
  76. data/lib/vapid/vendor/semantic-ui/components/checkbox.min.js +10 -0
  77. data/lib/vapid/vendor/semantic-ui/components/colorize.js +274 -0
  78. data/lib/vapid/vendor/semantic-ui/components/colorize.min.js +11 -0
  79. data/lib/vapid/vendor/semantic-ui/components/comment.css +270 -0
  80. data/lib/vapid/vendor/semantic-ui/components/comment.min.css +9 -0
  81. data/lib/vapid/vendor/semantic-ui/components/container.css +147 -0
  82. data/lib/vapid/vendor/semantic-ui/components/container.min.css +9 -0
  83. data/lib/vapid/vendor/semantic-ui/components/dimmer.css +200 -0
  84. data/lib/vapid/vendor/semantic-ui/components/dimmer.js +708 -0
  85. data/lib/vapid/vendor/semantic-ui/components/dimmer.min.css +9 -0
  86. data/lib/vapid/vendor/semantic-ui/components/dimmer.min.js +10 -0
  87. data/lib/vapid/vendor/semantic-ui/components/divider.css +260 -0
  88. data/lib/vapid/vendor/semantic-ui/components/divider.min.css +9 -0
  89. data/lib/vapid/vendor/semantic-ui/components/dropdown.css +1442 -0
  90. data/lib/vapid/vendor/semantic-ui/components/dropdown.js +3767 -0
  91. data/lib/vapid/vendor/semantic-ui/components/dropdown.min.css +9 -0
  92. data/lib/vapid/vendor/semantic-ui/components/dropdown.min.js +11 -0
  93. data/lib/vapid/vendor/semantic-ui/components/embed.css +166 -0
  94. data/lib/vapid/vendor/semantic-ui/components/embed.js +696 -0
  95. data/lib/vapid/vendor/semantic-ui/components/embed.min.css +9 -0
  96. data/lib/vapid/vendor/semantic-ui/components/embed.min.js +10 -0
  97. data/lib/vapid/vendor/semantic-ui/components/feed.css +296 -0
  98. data/lib/vapid/vendor/semantic-ui/components/feed.min.css +9 -0
  99. data/lib/vapid/vendor/semantic-ui/components/flag.css +1031 -0
  100. data/lib/vapid/vendor/semantic-ui/components/flag.min.css +9 -0
  101. data/lib/vapid/vendor/semantic-ui/components/form.css +1067 -0
  102. data/lib/vapid/vendor/semantic-ui/components/form.js +1603 -0
  103. data/lib/vapid/vendor/semantic-ui/components/form.min.css +9 -0
  104. data/lib/vapid/vendor/semantic-ui/components/form.min.js +10 -0
  105. data/lib/vapid/vendor/semantic-ui/components/grid.css +2002 -0
  106. data/lib/vapid/vendor/semantic-ui/components/grid.min.css +9 -0
  107. data/lib/vapid/vendor/semantic-ui/components/header.css +721 -0
  108. data/lib/vapid/vendor/semantic-ui/components/header.min.css +9 -0
  109. data/lib/vapid/vendor/semantic-ui/components/icon.css +3292 -0
  110. data/lib/vapid/vendor/semantic-ui/components/icon.min.css +9 -0
  111. data/lib/vapid/vendor/semantic-ui/components/image.css +306 -0
  112. data/lib/vapid/vendor/semantic-ui/components/image.min.css +9 -0
  113. data/lib/vapid/vendor/semantic-ui/components/input.css +510 -0
  114. data/lib/vapid/vendor/semantic-ui/components/input.min.css +9 -0
  115. data/lib/vapid/vendor/semantic-ui/components/item.css +486 -0
  116. data/lib/vapid/vendor/semantic-ui/components/item.min.css +9 -0
  117. data/lib/vapid/vendor/semantic-ui/components/label.css +1307 -0
  118. data/lib/vapid/vendor/semantic-ui/components/label.min.css +9 -0
  119. data/lib/vapid/vendor/semantic-ui/components/list.css +951 -0
  120. data/lib/vapid/vendor/semantic-ui/components/list.min.css +9 -0
  121. data/lib/vapid/vendor/semantic-ui/components/loader.css +347 -0
  122. data/lib/vapid/vendor/semantic-ui/components/loader.min.css +9 -0
  123. data/lib/vapid/vendor/semantic-ui/components/menu.css +2002 -0
  124. data/lib/vapid/vendor/semantic-ui/components/menu.min.css +1 -0
  125. data/lib/vapid/vendor/semantic-ui/components/message.css +477 -0
  126. data/lib/vapid/vendor/semantic-ui/components/message.min.css +9 -0
  127. data/lib/vapid/vendor/semantic-ui/components/modal.css +502 -0
  128. data/lib/vapid/vendor/semantic-ui/components/modal.js +913 -0
  129. data/lib/vapid/vendor/semantic-ui/components/modal.min.css +9 -0
  130. data/lib/vapid/vendor/semantic-ui/components/modal.min.js +10 -0
  131. data/lib/vapid/vendor/semantic-ui/components/nag.css +147 -0
  132. data/lib/vapid/vendor/semantic-ui/components/nag.js +507 -0
  133. data/lib/vapid/vendor/semantic-ui/components/nag.min.css +9 -0
  134. data/lib/vapid/vendor/semantic-ui/components/nag.min.js +10 -0
  135. data/lib/vapid/vendor/semantic-ui/components/popup.css +733 -0
  136. data/lib/vapid/vendor/semantic-ui/components/popup.js +1475 -0
  137. data/lib/vapid/vendor/semantic-ui/components/popup.min.css +9 -0
  138. data/lib/vapid/vendor/semantic-ui/components/popup.min.js +10 -0
  139. data/lib/vapid/vendor/semantic-ui/components/progress.css +516 -0
  140. data/lib/vapid/vendor/semantic-ui/components/progress.js +931 -0
  141. data/lib/vapid/vendor/semantic-ui/components/progress.min.css +9 -0
  142. data/lib/vapid/vendor/semantic-ui/components/progress.min.js +10 -0
  143. data/lib/vapid/vendor/semantic-ui/components/rail.css +152 -0
  144. data/lib/vapid/vendor/semantic-ui/components/rail.min.css +9 -0
  145. data/lib/vapid/vendor/semantic-ui/components/rating.css +263 -0
  146. data/lib/vapid/vendor/semantic-ui/components/rating.js +508 -0
  147. data/lib/vapid/vendor/semantic-ui/components/rating.min.css +9 -0
  148. data/lib/vapid/vendor/semantic-ui/components/rating.min.js +10 -0
  149. data/lib/vapid/vendor/semantic-ui/components/reset.css +424 -0
  150. data/lib/vapid/vendor/semantic-ui/components/reset.min.css +9 -0
  151. data/lib/vapid/vendor/semantic-ui/components/reveal.css +284 -0
  152. data/lib/vapid/vendor/semantic-ui/components/reveal.min.css +9 -0
  153. data/lib/vapid/vendor/semantic-ui/components/search.css +408 -0
  154. data/lib/vapid/vendor/semantic-ui/components/search.js +1442 -0
  155. data/lib/vapid/vendor/semantic-ui/components/search.min.css +9 -0
  156. data/lib/vapid/vendor/semantic-ui/components/search.min.js +10 -0
  157. data/lib/vapid/vendor/semantic-ui/components/segment.css +798 -0
  158. data/lib/vapid/vendor/semantic-ui/components/segment.min.css +9 -0
  159. data/lib/vapid/vendor/semantic-ui/components/shape.css +157 -0
  160. data/lib/vapid/vendor/semantic-ui/components/shape.js +921 -0
  161. data/lib/vapid/vendor/semantic-ui/components/shape.min.css +9 -0
  162. data/lib/vapid/vendor/semantic-ui/components/shape.min.js +10 -0
  163. data/lib/vapid/vendor/semantic-ui/components/sidebar.css +634 -0
  164. data/lib/vapid/vendor/semantic-ui/components/sidebar.js +1036 -0
  165. data/lib/vapid/vendor/semantic-ui/components/sidebar.min.css +9 -0
  166. data/lib/vapid/vendor/semantic-ui/components/sidebar.min.js +10 -0
  167. data/lib/vapid/vendor/semantic-ui/components/site.css +160 -0
  168. data/lib/vapid/vendor/semantic-ui/components/site.js +487 -0
  169. data/lib/vapid/vendor/semantic-ui/components/site.min.css +9 -0
  170. data/lib/vapid/vendor/semantic-ui/components/site.min.js +10 -0
  171. data/lib/vapid/vendor/semantic-ui/components/state.js +708 -0
  172. data/lib/vapid/vendor/semantic-ui/components/state.min.js +10 -0
  173. data/lib/vapid/vendor/semantic-ui/components/statistic.css +569 -0
  174. data/lib/vapid/vendor/semantic-ui/components/statistic.min.css +9 -0
  175. data/lib/vapid/vendor/semantic-ui/components/step.css +623 -0
  176. data/lib/vapid/vendor/semantic-ui/components/step.min.css +9 -0
  177. data/lib/vapid/vendor/semantic-ui/components/sticky.css +78 -0
  178. data/lib/vapid/vendor/semantic-ui/components/sticky.js +942 -0
  179. data/lib/vapid/vendor/semantic-ui/components/sticky.min.css +9 -0
  180. data/lib/vapid/vendor/semantic-ui/components/sticky.min.js +10 -0
  181. data/lib/vapid/vendor/semantic-ui/components/tab.css +91 -0
  182. data/lib/vapid/vendor/semantic-ui/components/tab.js +952 -0
  183. data/lib/vapid/vendor/semantic-ui/components/tab.min.css +9 -0
  184. data/lib/vapid/vendor/semantic-ui/components/tab.min.js +10 -0
  185. data/lib/vapid/vendor/semantic-ui/components/table.css +1108 -0
  186. data/lib/vapid/vendor/semantic-ui/components/table.min.css +9 -0
  187. data/lib/vapid/vendor/semantic-ui/components/transition.css +1964 -0
  188. data/lib/vapid/vendor/semantic-ui/components/transition.js +1095 -0
  189. data/lib/vapid/vendor/semantic-ui/components/transition.min.css +9 -0
  190. data/lib/vapid/vendor/semantic-ui/components/transition.min.js +10 -0
  191. data/lib/vapid/vendor/semantic-ui/components/video.css +125 -0
  192. data/lib/vapid/vendor/semantic-ui/components/video.js +532 -0
  193. data/lib/vapid/vendor/semantic-ui/components/video.min.css +10 -0
  194. data/lib/vapid/vendor/semantic-ui/components/video.min.js +11 -0
  195. data/lib/vapid/vendor/semantic-ui/components/visibility.js +1288 -0
  196. data/lib/vapid/vendor/semantic-ui/components/visibility.min.js +10 -0
  197. data/lib/vapid/vendor/semantic-ui/components/visit.js +517 -0
  198. data/lib/vapid/vendor/semantic-ui/components/visit.min.js +11 -0
  199. data/lib/vapid/vendor/semantic-ui/package.js +34 -0
  200. data/lib/vapid/vendor/semantic-ui/package.json +20 -0
  201. data/lib/vapid/vendor/semantic-ui/semantic.css +36844 -0
  202. data/lib/vapid/vendor/semantic-ui/semantic.js +22617 -0
  203. data/lib/vapid/vendor/semantic-ui/semantic.min.css +364 -0
  204. data/lib/vapid/vendor/semantic-ui/semantic.min.js +19 -0
  205. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.eot +0 -0
  206. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.otf +0 -0
  207. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.svg +2671 -0
  208. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.ttf +0 -0
  209. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.woff +0 -0
  210. data/lib/vapid/vendor/semantic-ui/themes/default/assets/fonts/icons.woff2 +0 -0
  211. data/lib/vapid/vendor/semantic-ui/themes/default/assets/images/flags.png +0 -0
  212. data/lib/vapid/vendor/trix/.blade.yml +45 -0
  213. data/lib/vapid/vendor/trix/.github/ISSUE_TEMPLATE.md +13 -0
  214. data/lib/vapid/vendor/trix/.gitignore +3 -0
  215. data/lib/vapid/vendor/trix/.ruby-version +1 -0
  216. data/lib/vapid/vendor/trix/.travis.yml +6 -0
  217. data/lib/vapid/vendor/trix/CONDUCT.md +79 -0
  218. data/lib/vapid/vendor/trix/Gemfile +17 -0
  219. data/lib/vapid/vendor/trix/Gemfile.lock +152 -0
  220. data/lib/vapid/vendor/trix/LICENSE +20 -0
  221. data/lib/vapid/vendor/trix/README.md +382 -0
  222. data/lib/vapid/vendor/trix/assets/index.html +55 -0
  223. data/lib/vapid/vendor/trix/assets/trix-core.coffee +2 -0
  224. data/lib/vapid/vendor/trix/assets/trix.coffee +3 -0
  225. data/lib/vapid/vendor/trix/assets/trix.scss +5 -0
  226. data/lib/vapid/vendor/trix/assets/trix/banner/index.coffee.erb +3 -0
  227. data/lib/vapid/vendor/trix/assets/trix/banner/index.scss.erb +3 -0
  228. data/lib/vapid/vendor/trix/assets/trix/banner/index.txt.erb +3 -0
  229. data/lib/vapid/vendor/trix/assets/trix/images/README.md +6 -0
  230. data/lib/vapid/vendor/trix/assets/trix/images/attach.svg +5 -0
  231. data/lib/vapid/vendor/trix/assets/trix/images/bold.svg +5 -0
  232. data/lib/vapid/vendor/trix/assets/trix/images/bullets.svg +5 -0
  233. data/lib/vapid/vendor/trix/assets/trix/images/code.svg +5 -0
  234. data/lib/vapid/vendor/trix/assets/trix/images/heading_1.svg +5 -0
  235. data/lib/vapid/vendor/trix/assets/trix/images/italic.svg +5 -0
  236. data/lib/vapid/vendor/trix/assets/trix/images/link.svg +6 -0
  237. data/lib/vapid/vendor/trix/assets/trix/images/nesting_level_decrease.svg +5 -0
  238. data/lib/vapid/vendor/trix/assets/trix/images/nesting_level_increase.svg +5 -0
  239. data/lib/vapid/vendor/trix/assets/trix/images/numbers.svg +5 -0
  240. data/lib/vapid/vendor/trix/assets/trix/images/quote.svg +5 -0
  241. data/lib/vapid/vendor/trix/assets/trix/images/redo.svg +5 -0
  242. data/lib/vapid/vendor/trix/assets/trix/images/remove.svg +4 -0
  243. data/lib/vapid/vendor/trix/assets/trix/images/strike.svg +6 -0
  244. data/lib/vapid/vendor/trix/assets/trix/images/trash.svg +5 -0
  245. data/lib/vapid/vendor/trix/assets/trix/images/undo.svg +5 -0
  246. data/lib/vapid/vendor/trix/assets/trix/stylesheets/attachments.scss +112 -0
  247. data/lib/vapid/vendor/trix/assets/trix/stylesheets/content.scss +88 -0
  248. data/lib/vapid/vendor/trix/assets/trix/stylesheets/editor.scss +8 -0
  249. data/lib/vapid/vendor/trix/assets/trix/stylesheets/icons.scss +14 -0
  250. data/lib/vapid/vendor/trix/assets/trix/stylesheets/media-queries.scss +7 -0
  251. data/lib/vapid/vendor/trix/assets/trix/stylesheets/toolbar.scss +194 -0
  252. data/lib/vapid/vendor/trix/bin/blade +16 -0
  253. data/lib/vapid/vendor/trix/bin/ci +21 -0
  254. data/lib/vapid/vendor/trix/bin/rackup +16 -0
  255. data/lib/vapid/vendor/trix/bin/rake +16 -0
  256. data/lib/vapid/vendor/trix/bin/release +139 -0
  257. data/lib/vapid/vendor/trix/bin/setup +69 -0
  258. data/lib/vapid/vendor/trix/bin/update-status-image +100 -0
  259. data/lib/vapid/vendor/trix/bower.json +34 -0
  260. data/lib/vapid/vendor/trix/config.ru +32 -0
  261. data/lib/vapid/vendor/trix/dist/trix-core.js +11 -0
  262. data/lib/vapid/vendor/trix/dist/trix.css +286 -0
  263. data/lib/vapid/vendor/trix/dist/trix.js +20 -0
  264. data/lib/vapid/vendor/trix/package.json +29 -0
  265. data/lib/vapid/vendor/trix/polyfills/polyfills.coffee +3 -0
  266. data/lib/vapid/vendor/trix/polyfills/set.coffee +24 -0
  267. data/lib/vapid/vendor/trix/polyfills/vendor/CustomElements.js +1029 -0
  268. data/lib/vapid/vendor/trix/polyfills/vendor/README.md +2 -0
  269. data/lib/vapid/vendor/trix/polyfills/vendor/promise.js +228 -0
  270. data/lib/vapid/vendor/trix/src/trix/VERSION +1 -0
  271. data/lib/vapid/vendor/trix/src/trix/config/block_attributes.coffee +37 -0
  272. data/lib/vapid/vendor/trix/src/trix/config/css.coffee +12 -0
  273. data/lib/vapid/vendor/trix/src/trix/config/file_size_formatting.coffee +22 -0
  274. data/lib/vapid/vendor/trix/src/trix/config/index.coffee +9 -0
  275. data/lib/vapid/vendor/trix/src/trix/config/lang.coffee +26 -0
  276. data/lib/vapid/vendor/trix/src/trix/config/selection_elements.coffee +26 -0
  277. data/lib/vapid/vendor/trix/src/trix/config/serialization.coffee +63 -0
  278. data/lib/vapid/vendor/trix/src/trix/config/text_attributes.coffee +25 -0
  279. data/lib/vapid/vendor/trix/src/trix/config/toolbar.coffee +41 -0
  280. data/lib/vapid/vendor/trix/src/trix/config/undo_interval.coffee +7 -0
  281. data/lib/vapid/vendor/trix/src/trix/controllers/attachment_editor_controller.coffee +104 -0
  282. data/lib/vapid/vendor/trix/src/trix/controllers/composition_controller.coffee +117 -0
  283. data/lib/vapid/vendor/trix/src/trix/controllers/controller.coffee +3 -0
  284. data/lib/vapid/vendor/trix/src/trix/controllers/editor_controller.coffee +369 -0
  285. data/lib/vapid/vendor/trix/src/trix/controllers/input/composition_input.coffee +57 -0
  286. data/lib/vapid/vendor/trix/src/trix/controllers/input_controller.coffee +473 -0
  287. data/lib/vapid/vendor/trix/src/trix/controllers/toolbar_controller.coffee +173 -0
  288. data/lib/vapid/vendor/trix/src/trix/core/basic_object.coffee +44 -0
  289. data/lib/vapid/vendor/trix/src/trix/core/collections/element_store.coffee +21 -0
  290. data/lib/vapid/vendor/trix/src/trix/core/collections/hash.coffee +98 -0
  291. data/lib/vapid/vendor/trix/src/trix/core/collections/index.coffee +4 -0
  292. data/lib/vapid/vendor/trix/src/trix/core/collections/object_group.coffee +37 -0
  293. data/lib/vapid/vendor/trix/src/trix/core/collections/object_map.coffee +10 -0
  294. data/lib/vapid/vendor/trix/src/trix/core/helpers/arrays.coffee +34 -0
  295. data/lib/vapid/vendor/trix/src/trix/core/helpers/config.coffee +23 -0
  296. data/lib/vapid/vendor/trix/src/trix/core/helpers/custom_elements.coffee +45 -0
  297. data/lib/vapid/vendor/trix/src/trix/core/helpers/dom.coffee +185 -0
  298. data/lib/vapid/vendor/trix/src/trix/core/helpers/extend.coffee +4 -0
  299. data/lib/vapid/vendor/trix/src/trix/core/helpers/functions.coffee +3 -0
  300. data/lib/vapid/vendor/trix/src/trix/core/helpers/global.coffee +23 -0
  301. data/lib/vapid/vendor/trix/src/trix/core/helpers/index.coffee +10 -0
  302. data/lib/vapid/vendor/trix/src/trix/core/helpers/objects.coffee +11 -0
  303. data/lib/vapid/vendor/trix/src/trix/core/helpers/ranges.coffee +33 -0
  304. data/lib/vapid/vendor/trix/src/trix/core/helpers/selection.coffee +25 -0
  305. data/lib/vapid/vendor/trix/src/trix/core/helpers/strings.coffee +46 -0
  306. data/lib/vapid/vendor/trix/src/trix/core/index.coffee +4 -0
  307. data/lib/vapid/vendor/trix/src/trix/core/object.coffee +33 -0
  308. data/lib/vapid/vendor/trix/src/trix/core/utilities/index.coffee +2 -0
  309. data/lib/vapid/vendor/trix/src/trix/core/utilities/operation.coffee +37 -0
  310. data/lib/vapid/vendor/trix/src/trix/core/utilities/utf16_string.coffee +93 -0
  311. data/lib/vapid/vendor/trix/src/trix/elements/trix_editor_element.coffee +166 -0
  312. data/lib/vapid/vendor/trix/src/trix/elements/trix_toolbar_element.coffee +28 -0
  313. data/lib/vapid/vendor/trix/src/trix/index.coffee.erb +14 -0
  314. data/lib/vapid/vendor/trix/src/trix/inspector/control_element.coffee +60 -0
  315. data/lib/vapid/vendor/trix/src/trix/inspector/debugger.coffee +104 -0
  316. data/lib/vapid/vendor/trix/src/trix/inspector/element.coffee +86 -0
  317. data/lib/vapid/vendor/trix/src/trix/inspector/index.coffee +17 -0
  318. data/lib/vapid/vendor/trix/src/trix/inspector/polyfills/vendor/details-element-polyfill.js +5 -0
  319. data/lib/vapid/vendor/trix/src/trix/inspector/templates/debug.jst.eco +17 -0
  320. data/lib/vapid/vendor/trix/src/trix/inspector/templates/document.jst.eco +33 -0
  321. data/lib/vapid/vendor/trix/src/trix/inspector/templates/performance.jst.eco +10 -0
  322. data/lib/vapid/vendor/trix/src/trix/inspector/templates/render.jst.eco +1 -0
  323. data/lib/vapid/vendor/trix/src/trix/inspector/templates/selection.jst.eco +5 -0
  324. data/lib/vapid/vendor/trix/src/trix/inspector/templates/undo.jst.eco +13 -0
  325. data/lib/vapid/vendor/trix/src/trix/inspector/view.coffee +58 -0
  326. data/lib/vapid/vendor/trix/src/trix/inspector/views/debug_view.coffee +33 -0
  327. data/lib/vapid/vendor/trix/src/trix/inspector/views/document_view.coffee +12 -0
  328. data/lib/vapid/vendor/trix/src/trix/inspector/views/performance_view.coffee +51 -0
  329. data/lib/vapid/vendor/trix/src/trix/inspector/views/render_view.coffee +21 -0
  330. data/lib/vapid/vendor/trix/src/trix/inspector/views/selection_view.coffee +33 -0
  331. data/lib/vapid/vendor/trix/src/trix/inspector/views/undo_view.coffee +12 -0
  332. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/deserializer.coffee +55 -0
  333. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/index.coffee +5 -0
  334. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/player.coffee +60 -0
  335. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/player_controller.coffee +43 -0
  336. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/player_element.coffee +32 -0
  337. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/player_view.coffee +133 -0
  338. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/recorder.coffee +82 -0
  339. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/recording.coffee +54 -0
  340. data/lib/vapid/vendor/trix/src/trix/inspector/watchdog/serializer.coffee +61 -0
  341. data/lib/vapid/vendor/trix/src/trix/models/attachment.coffee +157 -0
  342. data/lib/vapid/vendor/trix/src/trix/models/attachment_manager.coffee +24 -0
  343. data/lib/vapid/vendor/trix/src/trix/models/attachment_piece.coffee +47 -0
  344. data/lib/vapid/vendor/trix/src/trix/models/block.coffee +230 -0
  345. data/lib/vapid/vendor/trix/src/trix/models/composition.coffee +521 -0
  346. data/lib/vapid/vendor/trix/src/trix/models/document.coffee +538 -0
  347. data/lib/vapid/vendor/trix/src/trix/models/editor.coffee +141 -0
  348. data/lib/vapid/vendor/trix/src/trix/models/html_parser.coffee +335 -0
  349. data/lib/vapid/vendor/trix/src/trix/models/line_break_insertion.coffee +31 -0
  350. data/lib/vapid/vendor/trix/src/trix/models/location_mapper.coffee +156 -0
  351. data/lib/vapid/vendor/trix/src/trix/models/managed_attachment.coffee +28 -0
  352. data/lib/vapid/vendor/trix/src/trix/models/piece.coffee +95 -0
  353. data/lib/vapid/vendor/trix/src/trix/models/point_mapper.coffee +28 -0
  354. data/lib/vapid/vendor/trix/src/trix/models/selection_manager.coffee +129 -0
  355. data/lib/vapid/vendor/trix/src/trix/models/splittable_list.coffee +168 -0
  356. data/lib/vapid/vendor/trix/src/trix/models/string_piece.coffee +49 -0
  357. data/lib/vapid/vendor/trix/src/trix/models/text.coffee +168 -0
  358. data/lib/vapid/vendor/trix/src/trix/models/undo_manager.coffee +40 -0
  359. data/lib/vapid/vendor/trix/src/trix/observers/mutation_observer.coffee +131 -0
  360. data/lib/vapid/vendor/trix/src/trix/observers/selection_change_observer.coffee +56 -0
  361. data/lib/vapid/vendor/trix/src/trix/operations/file_verification_operation.coffee +15 -0
  362. data/lib/vapid/vendor/trix/src/trix/operations/image_preload_operation.coffee +15 -0
  363. data/lib/vapid/vendor/trix/src/trix/views/attachment_view.coffee +99 -0
  364. data/lib/vapid/vendor/trix/src/trix/views/block_view.coffee +37 -0
  365. data/lib/vapid/vendor/trix/src/trix/views/document_view.coffee +68 -0
  366. data/lib/vapid/vendor/trix/src/trix/views/object_group_view.coffee +23 -0
  367. data/lib/vapid/vendor/trix/src/trix/views/object_view.coffee +86 -0
  368. data/lib/vapid/vendor/trix/src/trix/views/piece_view.coffee +97 -0
  369. data/lib/vapid/vendor/trix/src/trix/views/previewable_attachment_view.coffee +49 -0
  370. data/lib/vapid/vendor/trix/src/trix/views/text_view.coffee +30 -0
  371. data/lib/vapid/vendor/trix/test/.gitignore +1 -0
  372. data/lib/vapid/vendor/trix/test/src/system/attachment_test.coffee +70 -0
  373. data/lib/vapid/vendor/trix/test/src/system/basic_input_test.coffee +68 -0
  374. data/lib/vapid/vendor/trix/test/src/system/block_formatting_test.coffee +602 -0
  375. data/lib/vapid/vendor/trix/test/src/system/caching_test.coffee +21 -0
  376. data/lib/vapid/vendor/trix/test/src/system/composition_input_test.coffee +138 -0
  377. data/lib/vapid/vendor/trix/test/src/system/cursor_movement_test.coffee +54 -0
  378. data/lib/vapid/vendor/trix/test/src/system/custom_element_test.coffee +314 -0
  379. data/lib/vapid/vendor/trix/test/src/system/html_loading_test.coffee +62 -0
  380. data/lib/vapid/vendor/trix/test/src/system/html_replacement_test.coffee +101 -0
  381. data/lib/vapid/vendor/trix/test/src/system/installation_process_test.coffee +59 -0
  382. data/lib/vapid/vendor/trix/test/src/system/list_formatting_test.coffee +70 -0
  383. data/lib/vapid/vendor/trix/test/src/system/mutation_input_test.coffee +73 -0
  384. data/lib/vapid/vendor/trix/test/src/system/pasting_test.coffee +215 -0
  385. data/lib/vapid/vendor/trix/test/src/system/text_formatting_test.coffee +151 -0
  386. data/lib/vapid/vendor/trix/test/src/system/undo_test.coffee +48 -0
  387. data/lib/vapid/vendor/trix/test/src/test.coffee +16 -0
  388. data/lib/vapid/vendor/trix/test/src/test_helpers/assertions.coffee +38 -0
  389. data/lib/vapid/vendor/trix/test/src/test_helpers/editor_helpers.coffee +37 -0
  390. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_empty.jst.eco +1 -0
  391. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_html.jst.eco +2 -0
  392. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_in_table.jst.eco +7 -0
  393. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_block_styles.jst.eco +6 -0
  394. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_bold_styles.jst.eco +7 -0
  395. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_image.jst.eco +2 -0
  396. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_styled_content.jst.eco +7 -0
  397. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/editor_with_toolbar_and_input.jst.eco +5 -0
  398. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/fixtures.coffee +536 -0
  399. data/lib/vapid/vendor/trix/test/src/test_helpers/fixtures/logo.png +0 -0
  400. data/lib/vapid/vendor/trix/test/src/test_helpers/index.coffee +18 -0
  401. data/lib/vapid/vendor/trix/test/src/test_helpers/input_helpers.coffee +183 -0
  402. data/lib/vapid/vendor/trix/test/src/test_helpers/selection_helpers.coffee +91 -0
  403. data/lib/vapid/vendor/trix/test/src/test_helpers/test_helpers.coffee +72 -0
  404. data/lib/vapid/vendor/trix/test/src/test_helpers/test_stubs.coffee +31 -0
  405. data/lib/vapid/vendor/trix/test/src/test_helpers/toolbar_helpers.coffee +45 -0
  406. data/lib/vapid/vendor/trix/test/src/unit/attachment_test.coffee +23 -0
  407. data/lib/vapid/vendor/trix/test/src/unit/block_test.coffee +22 -0
  408. data/lib/vapid/vendor/trix/test/src/unit/composition_test.coffee +13 -0
  409. data/lib/vapid/vendor/trix/test/src/unit/document_test.coffee +11 -0
  410. data/lib/vapid/vendor/trix/test/src/unit/document_view_test.coffee +6 -0
  411. data/lib/vapid/vendor/trix/test/src/unit/html_parser_test.coffee +162 -0
  412. data/lib/vapid/vendor/trix/test/src/unit/location_mapper_test.coffee +185 -0
  413. data/lib/vapid/vendor/trix/test/src/unit/mutation_observer_test.coffee +84 -0
  414. data/lib/vapid/vendor/trix/test/src/unit/serialization_test.coffee +7 -0
  415. data/lib/vapid/vendor/trix/test/src/unit/string_change_summary_test.coffee +57 -0
  416. data/lib/vapid/vendor/trix/test/src/unit/text_test.coffee +19 -0
  417. data/lib/vapid/vendor/trix/test/vendor/rangy-core.js +3845 -0
  418. data/lib/vapid/vendor/trix/test/vendor/rangy-textrange.js +1930 -0
  419. data/lib/vapid/version.rb +4 -0
  420. data/lib/vapid/views/admin/install.html.erb +19 -0
  421. data/lib/vapid/views/admin/login.html.erb +11 -0
  422. data/lib/vapid/views/admin/overview/index.html.erb +1 -0
  423. data/lib/vapid/views/admin/records/_form.erb +9 -0
  424. data/lib/vapid/views/admin/records/edit.html.erb +5 -0
  425. data/lib/vapid/views/admin/records/index.html.erb +38 -0
  426. data/lib/vapid/views/admin/records/new.html.erb +3 -0
  427. data/lib/vapid/views/errors/404.html.erb +1 -0
  428. data/lib/vapid/views/errors/500.html.erb +1 -0
  429. data/lib/vapid/views/layouts/admin.html.erb +41 -0
  430. data/lib/vapid/views/layouts/admin_form.html.erb +22 -0
  431. data/lib/vapid/views/layouts/error.html.erb +11 -0
  432. data/vapid.gemspec +48 -0
  433. metadata +811 -0
@@ -0,0 +1,1930 @@
1
+ /**
2
+ * Text range module for Rangy.
3
+ * Text-based manipulation and searching of ranges and selections.
4
+ *
5
+ * Features
6
+ *
7
+ * - Ability to move range boundaries by character or word offsets
8
+ * - Customizable word tokenizer
9
+ * - Ignores text nodes inside <script> or <style> elements or those hidden by CSS display and visibility properties
10
+ * - Range findText method to search for text or regex within the page or within a range. Flags for whole words and case
11
+ * sensitivity
12
+ * - Selection and range save/restore as text offsets within a node
13
+ * - Methods to return visible text within a range or selection
14
+ * - innerText method for elements
15
+ *
16
+ * References
17
+ *
18
+ * https://www.w3.org/Bugs/Public/show_bug.cgi?id=13145
19
+ * http://aryeh.name/spec/innertext/innertext.html
20
+ * http://dvcs.w3.org/hg/editing/raw-file/tip/editing.html
21
+ *
22
+ * Part of Rangy, a cross-browser JavaScript range and selection library
23
+ * https://github.com/timdown/rangy
24
+ *
25
+ * Depends on Rangy core.
26
+ *
27
+ * Copyright 2015, Tim Down
28
+ * Licensed under the MIT license.
29
+ * Version: 1.3.1-dev
30
+ * Build date: 20 May 2015
31
+ */
32
+
33
+ /**
34
+ * Problem: handling of trailing spaces before line breaks is handled inconsistently between browsers.
35
+ *
36
+ * First, a <br>: this is relatively simple. For the following HTML:
37
+ *
38
+ * 1 <br>2
39
+ *
40
+ * - IE and WebKit render the space, include it in the selection (i.e. when the content is selected and pasted into a
41
+ * textarea, the space is present) and allow the caret to be placed after it.
42
+ * - Firefox does not acknowledge the space in the selection but it is possible to place the caret after it.
43
+ * - Opera does not render the space but has two separate caret positions on either side of the space (left and right
44
+ * arrow keys show this) and includes the space in the selection.
45
+ *
46
+ * The other case is the line break or breaks implied by block elements. For the following HTML:
47
+ *
48
+ * <p>1 </p><p>2<p>
49
+ *
50
+ * - WebKit does not acknowledge the space in any way
51
+ * - Firefox, IE and Opera as per <br>
52
+ *
53
+ * One more case is trailing spaces before line breaks in elements with white-space: pre-line. For the following HTML:
54
+ *
55
+ * <p style="white-space: pre-line">1
56
+ * 2</p>
57
+ *
58
+ * - Firefox and WebKit include the space in caret positions
59
+ * - IE does not support pre-line up to and including version 9
60
+ * - Opera ignores the space
61
+ * - Trailing space only renders if there is a non-collapsed character in the line
62
+ *
63
+ * Problem is whether Rangy should ever acknowledge the space and if so, when. Another problem is whether this can be
64
+ * feature-tested
65
+ */
66
+ (function(factory, root) {
67
+ if (typeof define == "function" && define.amd) {
68
+ // AMD. Register as an anonymous module with a dependency on Rangy.
69
+ define(["./rangy-core"], factory);
70
+ } else if (typeof module != "undefined" && typeof exports == "object") {
71
+ // Node/CommonJS style
72
+ module.exports = factory( require("rangy") );
73
+ } else {
74
+ // No AMD or CommonJS support so we use the rangy property of root (probably the global variable)
75
+ factory(root.rangy);
76
+ }
77
+ })(function(rangy) {
78
+ rangy.createModule("TextRange", ["WrappedSelection"], function(api, module) {
79
+ var UNDEF = "undefined";
80
+ var CHARACTER = "character", WORD = "word";
81
+ var dom = api.dom, util = api.util;
82
+ var extend = util.extend;
83
+ var createOptions = util.createOptions;
84
+ var getBody = dom.getBody;
85
+
86
+
87
+ var spacesRegex = /^[ \t\f\r\n]+$/;
88
+ var spacesMinusLineBreaksRegex = /^[ \t\f\r]+$/;
89
+ var allWhiteSpaceRegex = /^[\t-\r \u0085\u00A0\u1680\u180E\u2000-\u200B\u2028\u2029\u202F\u205F\u3000]+$/;
90
+ var nonLineBreakWhiteSpaceRegex = /^[\t \u00A0\u1680\u180E\u2000-\u200B\u202F\u205F\u3000]+$/;
91
+ var lineBreakRegex = /^[\n-\r\u0085\u2028\u2029]$/;
92
+
93
+ var defaultLanguage = "en";
94
+
95
+ var isDirectionBackward = api.Selection.isDirectionBackward;
96
+
97
+ // Properties representing whether trailing spaces inside blocks are completely collapsed (as they are in WebKit,
98
+ // but not other browsers). Also test whether trailing spaces before <br> elements are collapsed.
99
+ var trailingSpaceInBlockCollapses = false;
100
+ var trailingSpaceBeforeBrCollapses = false;
101
+ var trailingSpaceBeforeBlockCollapses = false;
102
+ var trailingSpaceBeforeLineBreakInPreLineCollapses = true;
103
+
104
+ (function() {
105
+ var el = dom.createTestElement(document, "<p>1 </p><p></p>", true);
106
+ var p = el.firstChild;
107
+ var sel = api.getSelection();
108
+ sel.collapse(p.lastChild, 2);
109
+ sel.setStart(p.firstChild, 0);
110
+ trailingSpaceInBlockCollapses = ("" + sel).length == 1;
111
+
112
+ el.innerHTML = "1 <br />";
113
+ sel.collapse(el, 2);
114
+ sel.setStart(el.firstChild, 0);
115
+ trailingSpaceBeforeBrCollapses = ("" + sel).length == 1;
116
+
117
+ el.innerHTML = "1 <p>1</p>";
118
+ sel.collapse(el, 2);
119
+ sel.setStart(el.firstChild, 0);
120
+ trailingSpaceBeforeBlockCollapses = ("" + sel).length == 1;
121
+
122
+ dom.removeNode(el);
123
+ sel.removeAllRanges();
124
+ })();
125
+
126
+ /*----------------------------------------------------------------------------------------------------------------*/
127
+
128
+ // This function must create word and non-word tokens for the whole of the text supplied to it
129
+ function defaultTokenizer(chars, wordOptions) {
130
+ var word = chars.join(""), result, tokenRanges = [];
131
+
132
+ function createTokenRange(start, end, isWord) {
133
+ tokenRanges.push( { start: start, end: end, isWord: isWord } );
134
+ }
135
+
136
+ // Match words and mark characters
137
+ var lastWordEnd = 0, wordStart, wordEnd;
138
+ while ( (result = wordOptions.wordRegex.exec(word)) ) {
139
+ wordStart = result.index;
140
+ wordEnd = wordStart + result[0].length;
141
+
142
+ // Create token for non-word characters preceding this word
143
+ if (wordStart > lastWordEnd) {
144
+ createTokenRange(lastWordEnd, wordStart, false);
145
+ }
146
+
147
+ // Get trailing space characters for word
148
+ if (wordOptions.includeTrailingSpace) {
149
+ while ( nonLineBreakWhiteSpaceRegex.test(chars[wordEnd]) ) {
150
+ ++wordEnd;
151
+ }
152
+ }
153
+ createTokenRange(wordStart, wordEnd, true);
154
+ lastWordEnd = wordEnd;
155
+ }
156
+
157
+ // Create token for trailing non-word characters, if any exist
158
+ if (lastWordEnd < chars.length) {
159
+ createTokenRange(lastWordEnd, chars.length, false);
160
+ }
161
+
162
+ return tokenRanges;
163
+ }
164
+
165
+ function convertCharRangeToToken(chars, tokenRange) {
166
+ var tokenChars = chars.slice(tokenRange.start, tokenRange.end);
167
+ var token = {
168
+ isWord: tokenRange.isWord,
169
+ chars: tokenChars,
170
+ toString: function() {
171
+ return tokenChars.join("");
172
+ }
173
+ };
174
+ for (var i = 0, len = tokenChars.length; i < len; ++i) {
175
+ tokenChars[i].token = token;
176
+ }
177
+ return token;
178
+ }
179
+
180
+ function tokenize(chars, wordOptions, tokenizer) {
181
+ var tokenRanges = tokenizer(chars, wordOptions);
182
+ var tokens = [];
183
+ for (var i = 0, tokenRange; tokenRange = tokenRanges[i++]; ) {
184
+ tokens.push( convertCharRangeToToken(chars, tokenRange) );
185
+ }
186
+ return tokens;
187
+ }
188
+
189
+ var defaultCharacterOptions = {
190
+ includeBlockContentTrailingSpace: true,
191
+ includeSpaceBeforeBr: true,
192
+ includeSpaceBeforeBlock: true,
193
+ includePreLineTrailingSpace: true,
194
+ ignoreCharacters: ""
195
+ };
196
+
197
+ function normalizeIgnoredCharacters(ignoredCharacters) {
198
+ // Check if character is ignored
199
+ var ignoredChars = ignoredCharacters || "";
200
+
201
+ // Normalize ignored characters into a string consisting of characters in ascending order of character code
202
+ var ignoredCharsArray = (typeof ignoredChars == "string") ? ignoredChars.split("") : ignoredChars;
203
+ ignoredCharsArray.sort(function(char1, char2) {
204
+ return char1.charCodeAt(0) - char2.charCodeAt(0);
205
+ });
206
+
207
+ /// Convert back to a string and remove duplicates
208
+ return ignoredCharsArray.join("").replace(/(.)\1+/g, "$1");
209
+ }
210
+
211
+ var defaultCaretCharacterOptions = {
212
+ includeBlockContentTrailingSpace: !trailingSpaceBeforeLineBreakInPreLineCollapses,
213
+ includeSpaceBeforeBr: !trailingSpaceBeforeBrCollapses,
214
+ includeSpaceBeforeBlock: !trailingSpaceBeforeBlockCollapses,
215
+ includePreLineTrailingSpace: true
216
+ };
217
+
218
+ var defaultWordOptions = {
219
+ "en": {
220
+ wordRegex: /[a-z0-9]+('[a-z0-9]+)*/gi,
221
+ includeTrailingSpace: false,
222
+ tokenizer: defaultTokenizer
223
+ }
224
+ };
225
+
226
+ var defaultFindOptions = {
227
+ caseSensitive: false,
228
+ withinRange: null,
229
+ wholeWordsOnly: false,
230
+ wrap: false,
231
+ direction: "forward",
232
+ wordOptions: null,
233
+ characterOptions: null
234
+ };
235
+
236
+ var defaultMoveOptions = {
237
+ wordOptions: null,
238
+ characterOptions: null
239
+ };
240
+
241
+ var defaultExpandOptions = {
242
+ wordOptions: null,
243
+ characterOptions: null,
244
+ trim: false,
245
+ trimStart: true,
246
+ trimEnd: true
247
+ };
248
+
249
+ var defaultWordIteratorOptions = {
250
+ wordOptions: null,
251
+ characterOptions: null,
252
+ direction: "forward"
253
+ };
254
+
255
+ function createWordOptions(options) {
256
+ var lang, defaults;
257
+ if (!options) {
258
+ return defaultWordOptions[defaultLanguage];
259
+ } else {
260
+ lang = options.language || defaultLanguage;
261
+ defaults = {};
262
+ extend(defaults, defaultWordOptions[lang] || defaultWordOptions[defaultLanguage]);
263
+ extend(defaults, options);
264
+ return defaults;
265
+ }
266
+ }
267
+
268
+ function createNestedOptions(optionsParam, defaults) {
269
+ var options = createOptions(optionsParam, defaults);
270
+ if (defaults.hasOwnProperty("wordOptions")) {
271
+ options.wordOptions = createWordOptions(options.wordOptions);
272
+ }
273
+ if (defaults.hasOwnProperty("characterOptions")) {
274
+ options.characterOptions = createOptions(options.characterOptions, defaultCharacterOptions);
275
+ }
276
+ return options;
277
+ }
278
+
279
+ /*----------------------------------------------------------------------------------------------------------------*/
280
+
281
+ /* DOM utility functions */
282
+ var getComputedStyleProperty = dom.getComputedStyleProperty;
283
+
284
+ // Create cachable versions of DOM functions
285
+
286
+ // Test for old IE's incorrect display properties
287
+ var tableCssDisplayBlock;
288
+ (function() {
289
+ var table = document.createElement("table");
290
+ var body = getBody(document);
291
+ body.appendChild(table);
292
+ tableCssDisplayBlock = (getComputedStyleProperty(table, "display") == "block");
293
+ body.removeChild(table);
294
+ })();
295
+
296
+ var defaultDisplayValueForTag = {
297
+ table: "table",
298
+ caption: "table-caption",
299
+ colgroup: "table-column-group",
300
+ col: "table-column",
301
+ thead: "table-header-group",
302
+ tbody: "table-row-group",
303
+ tfoot: "table-footer-group",
304
+ tr: "table-row",
305
+ td: "table-cell",
306
+ th: "table-cell"
307
+ };
308
+
309
+ // Corrects IE's "block" value for table-related elements
310
+ function getComputedDisplay(el, win) {
311
+ var display = getComputedStyleProperty(el, "display", win);
312
+ var tagName = el.tagName.toLowerCase();
313
+ return (display == "block" &&
314
+ tableCssDisplayBlock &&
315
+ defaultDisplayValueForTag.hasOwnProperty(tagName)) ?
316
+ defaultDisplayValueForTag[tagName] : display;
317
+ }
318
+
319
+ function isHidden(node) {
320
+ var ancestors = getAncestorsAndSelf(node);
321
+ for (var i = 0, len = ancestors.length; i < len; ++i) {
322
+ if (ancestors[i].nodeType == 1 && getComputedDisplay(ancestors[i]) == "none") {
323
+ return true;
324
+ }
325
+ }
326
+
327
+ return false;
328
+ }
329
+
330
+ function isVisibilityHiddenTextNode(textNode) {
331
+ var el;
332
+ return textNode.nodeType == 3 &&
333
+ (el = textNode.parentNode) &&
334
+ getComputedStyleProperty(el, "visibility") == "hidden";
335
+ }
336
+
337
+ /*----------------------------------------------------------------------------------------------------------------*/
338
+
339
+
340
+ // "A block node is either an Element whose "display" property does not have
341
+ // resolved value "inline" or "inline-block" or "inline-table" or "none", or a
342
+ // Document, or a DocumentFragment."
343
+ function isBlockNode(node) {
344
+ return node &&
345
+ ((node.nodeType == 1 && !/^(inline(-block|-table)?|none)$/.test(getComputedDisplay(node))) ||
346
+ node.nodeType == 9 || node.nodeType == 11);
347
+ }
348
+
349
+ function getLastDescendantOrSelf(node) {
350
+ var lastChild = node.lastChild;
351
+ return lastChild ? getLastDescendantOrSelf(lastChild) : node;
352
+ }
353
+
354
+ function containsPositions(node) {
355
+ return dom.isCharacterDataNode(node) ||
356
+ !/^(area|base|basefont|br|col|frame|hr|img|input|isindex|link|meta|param)$/i.test(node.nodeName);
357
+ }
358
+
359
+ function getAncestors(node) {
360
+ var ancestors = [];
361
+ while (node.parentNode) {
362
+ ancestors.unshift(node.parentNode);
363
+ node = node.parentNode;
364
+ }
365
+ return ancestors;
366
+ }
367
+
368
+ function getAncestorsAndSelf(node) {
369
+ return getAncestors(node).concat([node]);
370
+ }
371
+
372
+ function nextNodeDescendants(node) {
373
+ while (node && !node.nextSibling) {
374
+ node = node.parentNode;
375
+ }
376
+ if (!node) {
377
+ return null;
378
+ }
379
+ return node.nextSibling;
380
+ }
381
+
382
+ function nextNode(node, excludeChildren) {
383
+ if (!excludeChildren && node.hasChildNodes()) {
384
+ return node.firstChild;
385
+ }
386
+ return nextNodeDescendants(node);
387
+ }
388
+
389
+ function previousNode(node) {
390
+ var previous = node.previousSibling;
391
+ if (previous) {
392
+ node = previous;
393
+ while (node.hasChildNodes()) {
394
+ node = node.lastChild;
395
+ }
396
+ return node;
397
+ }
398
+ var parent = node.parentNode;
399
+ if (parent && parent.nodeType == 1) {
400
+ return parent;
401
+ }
402
+ return null;
403
+ }
404
+
405
+ // Adpated from Aryeh's code.
406
+ // "A whitespace node is either a Text node whose data is the empty string; or
407
+ // a Text node whose data consists only of one or more tabs (0x0009), line
408
+ // feeds (0x000A), carriage returns (0x000D), and/or spaces (0x0020), and whose
409
+ // parent is an Element whose resolved value for "white-space" is "normal" or
410
+ // "nowrap"; or a Text node whose data consists only of one or more tabs
411
+ // (0x0009), carriage returns (0x000D), and/or spaces (0x0020), and whose
412
+ // parent is an Element whose resolved value for "white-space" is "pre-line"."
413
+ function isWhitespaceNode(node) {
414
+ if (!node || node.nodeType != 3) {
415
+ return false;
416
+ }
417
+ var text = node.data;
418
+ if (text === "") {
419
+ return true;
420
+ }
421
+ var parent = node.parentNode;
422
+ if (!parent || parent.nodeType != 1) {
423
+ return false;
424
+ }
425
+ var computedWhiteSpace = getComputedStyleProperty(node.parentNode, "whiteSpace");
426
+
427
+ return (/^[\t\n\r ]+$/.test(text) && /^(normal|nowrap)$/.test(computedWhiteSpace)) ||
428
+ (/^[\t\r ]+$/.test(text) && computedWhiteSpace == "pre-line");
429
+ }
430
+
431
+ // Adpated from Aryeh's code.
432
+ // "node is a collapsed whitespace node if the following algorithm returns
433
+ // true:"
434
+ function isCollapsedWhitespaceNode(node) {
435
+ // "If node's data is the empty string, return true."
436
+ if (node.data === "") {
437
+ return true;
438
+ }
439
+
440
+ // "If node is not a whitespace node, return false."
441
+ if (!isWhitespaceNode(node)) {
442
+ return false;
443
+ }
444
+
445
+ // "Let ancestor be node's parent."
446
+ var ancestor = node.parentNode;
447
+
448
+ // "If ancestor is null, return true."
449
+ if (!ancestor) {
450
+ return true;
451
+ }
452
+
453
+ // "If the "display" property of some ancestor of node has resolved value "none", return true."
454
+ if (isHidden(node)) {
455
+ return true;
456
+ }
457
+
458
+ return false;
459
+ }
460
+
461
+ function isCollapsedNode(node) {
462
+ var type = node.nodeType;
463
+ return type == 7 /* PROCESSING_INSTRUCTION */ ||
464
+ type == 8 /* COMMENT */ ||
465
+ isHidden(node) ||
466
+ /^(script|style)$/i.test(node.nodeName) ||
467
+ isVisibilityHiddenTextNode(node) ||
468
+ isCollapsedWhitespaceNode(node);
469
+ }
470
+
471
+ function isIgnoredNode(node, win) {
472
+ var type = node.nodeType;
473
+ return type == 7 /* PROCESSING_INSTRUCTION */ ||
474
+ type == 8 /* COMMENT */ ||
475
+ (type == 1 && getComputedDisplay(node, win) == "none");
476
+ }
477
+
478
+ /*----------------------------------------------------------------------------------------------------------------*/
479
+
480
+ // Possibly overengineered caching system to prevent repeated DOM calls slowing everything down
481
+
482
+ function Cache() {
483
+ this.store = {};
484
+ }
485
+
486
+ Cache.prototype = {
487
+ get: function(key) {
488
+ return this.store.hasOwnProperty(key) ? this.store[key] : null;
489
+ },
490
+
491
+ set: function(key, value) {
492
+ return this.store[key] = value;
493
+ }
494
+ };
495
+
496
+ var cachedCount = 0, uncachedCount = 0;
497
+
498
+ function createCachingGetter(methodName, func, objProperty) {
499
+ return function(args) {
500
+ var cache = this.cache;
501
+ if (cache.hasOwnProperty(methodName)) {
502
+ cachedCount++;
503
+ return cache[methodName];
504
+ } else {
505
+ uncachedCount++;
506
+ var value = func.call(this, objProperty ? this[objProperty] : this, args);
507
+ cache[methodName] = value;
508
+ return value;
509
+ }
510
+ };
511
+ }
512
+
513
+ /*----------------------------------------------------------------------------------------------------------------*/
514
+
515
+ function NodeWrapper(node, session) {
516
+ this.node = node;
517
+ this.session = session;
518
+ this.cache = new Cache();
519
+ this.positions = new Cache();
520
+ }
521
+
522
+ var nodeProto = {
523
+ getPosition: function(offset) {
524
+ var positions = this.positions;
525
+ return positions.get(offset) || positions.set(offset, new Position(this, offset));
526
+ },
527
+
528
+ toString: function() {
529
+ return "[NodeWrapper(" + dom.inspectNode(this.node) + ")]";
530
+ }
531
+ };
532
+
533
+ NodeWrapper.prototype = nodeProto;
534
+
535
+ var EMPTY = "EMPTY",
536
+ NON_SPACE = "NON_SPACE",
537
+ UNCOLLAPSIBLE_SPACE = "UNCOLLAPSIBLE_SPACE",
538
+ COLLAPSIBLE_SPACE = "COLLAPSIBLE_SPACE",
539
+ TRAILING_SPACE_BEFORE_BLOCK = "TRAILING_SPACE_BEFORE_BLOCK",
540
+ TRAILING_SPACE_IN_BLOCK = "TRAILING_SPACE_IN_BLOCK",
541
+ TRAILING_SPACE_BEFORE_BR = "TRAILING_SPACE_BEFORE_BR",
542
+ PRE_LINE_TRAILING_SPACE_BEFORE_LINE_BREAK = "PRE_LINE_TRAILING_SPACE_BEFORE_LINE_BREAK",
543
+ TRAILING_LINE_BREAK_AFTER_BR = "TRAILING_LINE_BREAK_AFTER_BR",
544
+ INCLUDED_TRAILING_LINE_BREAK_AFTER_BR = "INCLUDED_TRAILING_LINE_BREAK_AFTER_BR";
545
+
546
+ extend(nodeProto, {
547
+ isCharacterDataNode: createCachingGetter("isCharacterDataNode", dom.isCharacterDataNode, "node"),
548
+ getNodeIndex: createCachingGetter("nodeIndex", dom.getNodeIndex, "node"),
549
+ getLength: createCachingGetter("nodeLength", dom.getNodeLength, "node"),
550
+ containsPositions: createCachingGetter("containsPositions", containsPositions, "node"),
551
+ isWhitespace: createCachingGetter("isWhitespace", isWhitespaceNode, "node"),
552
+ isCollapsedWhitespace: createCachingGetter("isCollapsedWhitespace", isCollapsedWhitespaceNode, "node"),
553
+ getComputedDisplay: createCachingGetter("computedDisplay", getComputedDisplay, "node"),
554
+ isCollapsed: createCachingGetter("collapsed", isCollapsedNode, "node"),
555
+ isIgnored: createCachingGetter("ignored", isIgnoredNode, "node"),
556
+ next: createCachingGetter("nextPos", nextNode, "node"),
557
+ previous: createCachingGetter("previous", previousNode, "node"),
558
+
559
+ getTextNodeInfo: createCachingGetter("textNodeInfo", function(textNode) {
560
+ var spaceRegex = null, collapseSpaces = false;
561
+ var cssWhitespace = getComputedStyleProperty(textNode.parentNode, "whiteSpace");
562
+ var preLine = (cssWhitespace == "pre-line");
563
+ if (preLine) {
564
+ spaceRegex = spacesMinusLineBreaksRegex;
565
+ collapseSpaces = true;
566
+ } else if (cssWhitespace == "normal" || cssWhitespace == "nowrap") {
567
+ spaceRegex = spacesRegex;
568
+ collapseSpaces = true;
569
+ }
570
+
571
+ return {
572
+ node: textNode,
573
+ text: textNode.data,
574
+ spaceRegex: spaceRegex,
575
+ collapseSpaces: collapseSpaces,
576
+ preLine: preLine
577
+ };
578
+ }, "node"),
579
+
580
+ hasInnerText: createCachingGetter("hasInnerText", function(el, backward) {
581
+ var session = this.session;
582
+ var posAfterEl = session.getPosition(el.parentNode, this.getNodeIndex() + 1);
583
+ var firstPosInEl = session.getPosition(el, 0);
584
+
585
+ var pos = backward ? posAfterEl : firstPosInEl;
586
+ var endPos = backward ? firstPosInEl : posAfterEl;
587
+
588
+ /*
589
+ <body><p>X </p><p>Y</p></body>
590
+
591
+ Positions:
592
+
593
+ body:0:""
594
+ p:0:""
595
+ text:0:""
596
+ text:1:"X"
597
+ text:2:TRAILING_SPACE_IN_BLOCK
598
+ text:3:COLLAPSED_SPACE
599
+ p:1:""
600
+ body:1:"\n"
601
+ p:0:""
602
+ text:0:""
603
+ text:1:"Y"
604
+
605
+ A character is a TRAILING_SPACE_IN_BLOCK iff:
606
+
607
+ - There is no uncollapsed character after it within the visible containing block element
608
+
609
+ A character is a TRAILING_SPACE_BEFORE_BR iff:
610
+
611
+ - There is no uncollapsed character after it preceding a <br> element
612
+
613
+ An element has inner text iff
614
+
615
+ - It is not hidden
616
+ - It contains an uncollapsed character
617
+
618
+ All trailing spaces (pre-line, before <br>, end of block) require definite non-empty characters to render.
619
+ */
620
+
621
+ while (pos !== endPos) {
622
+ pos.prepopulateChar();
623
+ if (pos.isDefinitelyNonEmpty()) {
624
+ return true;
625
+ }
626
+ pos = backward ? pos.previousVisible() : pos.nextVisible();
627
+ }
628
+
629
+ return false;
630
+ }, "node"),
631
+
632
+ isRenderedBlock: createCachingGetter("isRenderedBlock", function(el) {
633
+ // Ensure that a block element containing a <br> is considered to have inner text
634
+ var brs = el.getElementsByTagName("br");
635
+ for (var i = 0, len = brs.length; i < len; ++i) {
636
+ if (!isCollapsedNode(brs[i])) {
637
+ return true;
638
+ }
639
+ }
640
+ return this.hasInnerText();
641
+ }, "node"),
642
+
643
+ getTrailingSpace: createCachingGetter("trailingSpace", function(el) {
644
+ if (el.tagName.toLowerCase() == "br") {
645
+ return "";
646
+ } else {
647
+ switch (this.getComputedDisplay()) {
648
+ case "inline":
649
+ var child = el.lastChild;
650
+ while (child) {
651
+ if (!isIgnoredNode(child)) {
652
+ return (child.nodeType == 1) ? this.session.getNodeWrapper(child).getTrailingSpace() : "";
653
+ }
654
+ child = child.previousSibling;
655
+ }
656
+ break;
657
+ case "inline-block":
658
+ case "inline-table":
659
+ case "none":
660
+ case "table-column":
661
+ case "table-column-group":
662
+ break;
663
+ case "table-cell":
664
+ return "\t";
665
+ default:
666
+ return this.isRenderedBlock(true) ? "\n" : "";
667
+ }
668
+ }
669
+ return "";
670
+ }, "node"),
671
+
672
+ getLeadingSpace: createCachingGetter("leadingSpace", function(el) {
673
+ switch (this.getComputedDisplay()) {
674
+ case "inline":
675
+ case "inline-block":
676
+ case "inline-table":
677
+ case "none":
678
+ case "table-column":
679
+ case "table-column-group":
680
+ case "table-cell":
681
+ break;
682
+ default:
683
+ return this.isRenderedBlock(false) ? "\n" : "";
684
+ }
685
+ return "";
686
+ }, "node")
687
+ });
688
+
689
+ /*----------------------------------------------------------------------------------------------------------------*/
690
+
691
+ function Position(nodeWrapper, offset) {
692
+ this.offset = offset;
693
+ this.nodeWrapper = nodeWrapper;
694
+ this.node = nodeWrapper.node;
695
+ this.session = nodeWrapper.session;
696
+ this.cache = new Cache();
697
+ }
698
+
699
+ function inspectPosition() {
700
+ return "[Position(" + dom.inspectNode(this.node) + ":" + this.offset + ")]";
701
+ }
702
+
703
+ var positionProto = {
704
+ character: "",
705
+ characterType: EMPTY,
706
+ isBr: false,
707
+
708
+ /*
709
+ This method:
710
+ - Fully populates positions that have characters that can be determined independently of any other characters.
711
+ - Populates most types of space positions with a provisional character. The character is finalized later.
712
+ */
713
+ prepopulateChar: function() {
714
+ var pos = this;
715
+ if (!pos.prepopulatedChar) {
716
+ var node = pos.node, offset = pos.offset;
717
+ var visibleChar = "", charType = EMPTY;
718
+ var finalizedChar = false;
719
+ if (offset > 0) {
720
+ if (node.nodeType == 3) {
721
+ var text = node.data;
722
+ var textChar = text.charAt(offset - 1);
723
+
724
+ var nodeInfo = pos.nodeWrapper.getTextNodeInfo();
725
+ var spaceRegex = nodeInfo.spaceRegex;
726
+ if (nodeInfo.collapseSpaces) {
727
+ if (spaceRegex.test(textChar)) {
728
+ // "If the character at position is from set, append a single space (U+0020) to newdata and advance
729
+ // position until the character at position is not from set."
730
+
731
+ // We also need to check for the case where we're in a pre-line and we have a space preceding a
732
+ // line break, because such spaces are collapsed in some browsers
733
+ if (offset > 1 && spaceRegex.test(text.charAt(offset - 2))) {
734
+ } else if (nodeInfo.preLine && text.charAt(offset) === "\n") {
735
+ visibleChar = " ";
736
+ charType = PRE_LINE_TRAILING_SPACE_BEFORE_LINE_BREAK;
737
+ } else {
738
+ visibleChar = " ";
739
+ //pos.checkForFollowingLineBreak = true;
740
+ charType = COLLAPSIBLE_SPACE;
741
+ }
742
+ } else {
743
+ visibleChar = textChar;
744
+ charType = NON_SPACE;
745
+ finalizedChar = true;
746
+ }
747
+ } else {
748
+ visibleChar = textChar;
749
+ charType = UNCOLLAPSIBLE_SPACE;
750
+ finalizedChar = true;
751
+ }
752
+ } else {
753
+ var nodePassed = node.childNodes[offset - 1];
754
+ if (nodePassed && nodePassed.nodeType == 1 && !isCollapsedNode(nodePassed)) {
755
+ if (nodePassed.tagName.toLowerCase() == "br") {
756
+ visibleChar = "\n";
757
+ pos.isBr = true;
758
+ charType = COLLAPSIBLE_SPACE;
759
+ finalizedChar = false;
760
+ } else {
761
+ pos.checkForTrailingSpace = true;
762
+ }
763
+ }
764
+
765
+ // Check the leading space of the next node for the case when a block element follows an inline
766
+ // element or text node. In that case, there is an implied line break between the two nodes.
767
+ if (!visibleChar) {
768
+ var nextNode = node.childNodes[offset];
769
+ if (nextNode && nextNode.nodeType == 1 && !isCollapsedNode(nextNode)) {
770
+ pos.checkForLeadingSpace = true;
771
+ }
772
+ }
773
+ }
774
+ }
775
+
776
+ pos.prepopulatedChar = true;
777
+ pos.character = visibleChar;
778
+ pos.characterType = charType;
779
+ pos.isCharInvariant = finalizedChar;
780
+ }
781
+ },
782
+
783
+ isDefinitelyNonEmpty: function() {
784
+ var charType = this.characterType;
785
+ return charType == NON_SPACE || charType == UNCOLLAPSIBLE_SPACE;
786
+ },
787
+
788
+ // Resolve leading and trailing spaces, which may involve prepopulating other positions
789
+ resolveLeadingAndTrailingSpaces: function() {
790
+ if (!this.prepopulatedChar) {
791
+ this.prepopulateChar();
792
+ }
793
+ if (this.checkForTrailingSpace) {
794
+ var trailingSpace = this.session.getNodeWrapper(this.node.childNodes[this.offset - 1]).getTrailingSpace();
795
+ if (trailingSpace) {
796
+ this.isTrailingSpace = true;
797
+ this.character = trailingSpace;
798
+ this.characterType = COLLAPSIBLE_SPACE;
799
+ }
800
+ this.checkForTrailingSpace = false;
801
+ }
802
+ if (this.checkForLeadingSpace) {
803
+ var leadingSpace = this.session.getNodeWrapper(this.node.childNodes[this.offset]).getLeadingSpace();
804
+ if (leadingSpace) {
805
+ this.isLeadingSpace = true;
806
+ this.character = leadingSpace;
807
+ this.characterType = COLLAPSIBLE_SPACE;
808
+ }
809
+ this.checkForLeadingSpace = false;
810
+ }
811
+ },
812
+
813
+ getPrecedingUncollapsedPosition: function(characterOptions) {
814
+ var pos = this, character;
815
+ while ( (pos = pos.previousVisible()) ) {
816
+ character = pos.getCharacter(characterOptions);
817
+ if (character !== "") {
818
+ return pos;
819
+ }
820
+ }
821
+
822
+ return null;
823
+ },
824
+
825
+ getCharacter: function(characterOptions) {
826
+ this.resolveLeadingAndTrailingSpaces();
827
+
828
+ var thisChar = this.character, returnChar;
829
+
830
+ // Check if character is ignored
831
+ var ignoredChars = normalizeIgnoredCharacters(characterOptions.ignoreCharacters);
832
+ var isIgnoredCharacter = (thisChar !== "" && ignoredChars.indexOf(thisChar) > -1);
833
+
834
+ // Check if this position's character is invariant (i.e. not dependent on character options) and return it
835
+ // if so
836
+ if (this.isCharInvariant) {
837
+ returnChar = isIgnoredCharacter ? "" : thisChar;
838
+ return returnChar;
839
+ }
840
+
841
+ var cacheKey = ["character", characterOptions.includeSpaceBeforeBr, characterOptions.includeBlockContentTrailingSpace, characterOptions.includePreLineTrailingSpace, ignoredChars].join("_");
842
+ var cachedChar = this.cache.get(cacheKey);
843
+ if (cachedChar !== null) {
844
+ return cachedChar;
845
+ }
846
+
847
+ // We need to actually get the character now
848
+ var character = "";
849
+ var collapsible = (this.characterType == COLLAPSIBLE_SPACE);
850
+
851
+ var nextPos, previousPos;
852
+ var gotPreviousPos = false;
853
+ var pos = this;
854
+
855
+ function getPreviousPos() {
856
+ if (!gotPreviousPos) {
857
+ previousPos = pos.getPrecedingUncollapsedPosition(characterOptions);
858
+ gotPreviousPos = true;
859
+ }
860
+ return previousPos;
861
+ }
862
+
863
+ // Disallow a collapsible space that is followed by a line break or is the last character
864
+ if (collapsible) {
865
+ // Allow a trailing space that we've previously determined should be included
866
+ if (this.type == INCLUDED_TRAILING_LINE_BREAK_AFTER_BR) {
867
+ character = "\n";
868
+ }
869
+ // Disallow a collapsible space that follows a trailing space or line break, or is the first character,
870
+ // or follows a collapsible included space
871
+ else if (thisChar == " " &&
872
+ (!getPreviousPos() || previousPos.isTrailingSpace || previousPos.character == "\n" || (previousPos.character == " " && previousPos.characterType == COLLAPSIBLE_SPACE))) {
873
+ }
874
+ // Allow a leading line break unless it follows a line break
875
+ else if (thisChar == "\n" && this.isLeadingSpace) {
876
+ if (getPreviousPos() && previousPos.character != "\n") {
877
+ character = "\n";
878
+ } else {
879
+ }
880
+ } else {
881
+ nextPos = this.nextUncollapsed();
882
+ if (nextPos) {
883
+ if (nextPos.isBr) {
884
+ this.type = TRAILING_SPACE_BEFORE_BR;
885
+ } else if (nextPos.isTrailingSpace && nextPos.character == "\n") {
886
+ this.type = TRAILING_SPACE_IN_BLOCK;
887
+ } else if (nextPos.isLeadingSpace && nextPos.character == "\n") {
888
+ this.type = TRAILING_SPACE_BEFORE_BLOCK;
889
+ }
890
+
891
+ if (nextPos.character == "\n") {
892
+ if (this.type == TRAILING_SPACE_BEFORE_BR && !characterOptions.includeSpaceBeforeBr) {
893
+ } else if (this.type == TRAILING_SPACE_BEFORE_BLOCK && !characterOptions.includeSpaceBeforeBlock) {
894
+ } else if (this.type == TRAILING_SPACE_IN_BLOCK && nextPos.isTrailingSpace && !characterOptions.includeBlockContentTrailingSpace) {
895
+ } else if (this.type == PRE_LINE_TRAILING_SPACE_BEFORE_LINE_BREAK && nextPos.type == NON_SPACE && !characterOptions.includePreLineTrailingSpace) {
896
+ } else if (thisChar == "\n") {
897
+ if (nextPos.isTrailingSpace) {
898
+ if (this.isTrailingSpace) {
899
+ } else if (this.isBr) {
900
+ nextPos.type = TRAILING_LINE_BREAK_AFTER_BR;
901
+
902
+ if (getPreviousPos() && previousPos.isLeadingSpace && !previousPos.isTrailingSpace && previousPos.character == "\n") {
903
+ nextPos.character = "";
904
+ } else {
905
+ nextPos.type = INCLUDED_TRAILING_LINE_BREAK_AFTER_BR;
906
+ }
907
+ }
908
+ } else {
909
+ character = "\n";
910
+ }
911
+ } else if (thisChar == " ") {
912
+ character = " ";
913
+ } else {
914
+ }
915
+ } else {
916
+ character = thisChar;
917
+ }
918
+ } else {
919
+ }
920
+ }
921
+ }
922
+
923
+ if (ignoredChars.indexOf(character) > -1) {
924
+ character = "";
925
+ }
926
+
927
+
928
+ this.cache.set(cacheKey, character);
929
+
930
+ return character;
931
+ },
932
+
933
+ equals: function(pos) {
934
+ return !!pos && this.node === pos.node && this.offset === pos.offset;
935
+ },
936
+
937
+ inspect: inspectPosition,
938
+
939
+ toString: function() {
940
+ return this.character;
941
+ }
942
+ };
943
+
944
+ Position.prototype = positionProto;
945
+
946
+ extend(positionProto, {
947
+ next: createCachingGetter("nextPos", function(pos) {
948
+ var nodeWrapper = pos.nodeWrapper, node = pos.node, offset = pos.offset, session = nodeWrapper.session;
949
+ if (!node) {
950
+ return null;
951
+ }
952
+ var nextNode, nextOffset, child;
953
+ if (offset == nodeWrapper.getLength()) {
954
+ // Move onto the next node
955
+ nextNode = node.parentNode;
956
+ nextOffset = nextNode ? nodeWrapper.getNodeIndex() + 1 : 0;
957
+ } else {
958
+ if (nodeWrapper.isCharacterDataNode()) {
959
+ nextNode = node;
960
+ nextOffset = offset + 1;
961
+ } else {
962
+ child = node.childNodes[offset];
963
+ // Go into the children next, if children there are
964
+ if (session.getNodeWrapper(child).containsPositions()) {
965
+ nextNode = child;
966
+ nextOffset = 0;
967
+ } else {
968
+ nextNode = node;
969
+ nextOffset = offset + 1;
970
+ }
971
+ }
972
+ }
973
+
974
+ return nextNode ? session.getPosition(nextNode, nextOffset) : null;
975
+ }),
976
+
977
+ previous: createCachingGetter("previous", function(pos) {
978
+ var nodeWrapper = pos.nodeWrapper, node = pos.node, offset = pos.offset, session = nodeWrapper.session;
979
+ var previousNode, previousOffset, child;
980
+ if (offset == 0) {
981
+ previousNode = node.parentNode;
982
+ previousOffset = previousNode ? nodeWrapper.getNodeIndex() : 0;
983
+ } else {
984
+ if (nodeWrapper.isCharacterDataNode()) {
985
+ previousNode = node;
986
+ previousOffset = offset - 1;
987
+ } else {
988
+ child = node.childNodes[offset - 1];
989
+ // Go into the children next, if children there are
990
+ if (session.getNodeWrapper(child).containsPositions()) {
991
+ previousNode = child;
992
+ previousOffset = dom.getNodeLength(child);
993
+ } else {
994
+ previousNode = node;
995
+ previousOffset = offset - 1;
996
+ }
997
+ }
998
+ }
999
+ return previousNode ? session.getPosition(previousNode, previousOffset) : null;
1000
+ }),
1001
+
1002
+ /*
1003
+ Next and previous position moving functions that filter out
1004
+
1005
+ - Hidden (CSS visibility/display) elements
1006
+ - Script and style elements
1007
+ */
1008
+ nextVisible: createCachingGetter("nextVisible", function(pos) {
1009
+ var next = pos.next();
1010
+ if (!next) {
1011
+ return null;
1012
+ }
1013
+ var nodeWrapper = next.nodeWrapper, node = next.node;
1014
+ var newPos = next;
1015
+ if (nodeWrapper.isCollapsed()) {
1016
+ // We're skipping this node and all its descendants
1017
+ newPos = nodeWrapper.session.getPosition(node.parentNode, nodeWrapper.getNodeIndex() + 1);
1018
+ }
1019
+ return newPos;
1020
+ }),
1021
+
1022
+ nextUncollapsed: createCachingGetter("nextUncollapsed", function(pos) {
1023
+ var nextPos = pos;
1024
+ while ( (nextPos = nextPos.nextVisible()) ) {
1025
+ nextPos.resolveLeadingAndTrailingSpaces();
1026
+ if (nextPos.character !== "") {
1027
+ return nextPos;
1028
+ }
1029
+ }
1030
+ return null;
1031
+ }),
1032
+
1033
+ previousVisible: createCachingGetter("previousVisible", function(pos) {
1034
+ var previous = pos.previous();
1035
+ if (!previous) {
1036
+ return null;
1037
+ }
1038
+ var nodeWrapper = previous.nodeWrapper, node = previous.node;
1039
+ var newPos = previous;
1040
+ if (nodeWrapper.isCollapsed()) {
1041
+ // We're skipping this node and all its descendants
1042
+ newPos = nodeWrapper.session.getPosition(node.parentNode, nodeWrapper.getNodeIndex());
1043
+ }
1044
+ return newPos;
1045
+ })
1046
+ });
1047
+
1048
+ /*----------------------------------------------------------------------------------------------------------------*/
1049
+
1050
+ var currentSession = null;
1051
+
1052
+ var Session = (function() {
1053
+ function createWrapperCache(nodeProperty) {
1054
+ var cache = new Cache();
1055
+
1056
+ return {
1057
+ get: function(node) {
1058
+ var wrappersByProperty = cache.get(node[nodeProperty]);
1059
+ if (wrappersByProperty) {
1060
+ for (var i = 0, wrapper; wrapper = wrappersByProperty[i++]; ) {
1061
+ if (wrapper.node === node) {
1062
+ return wrapper;
1063
+ }
1064
+ }
1065
+ }
1066
+ return null;
1067
+ },
1068
+
1069
+ set: function(nodeWrapper) {
1070
+ var property = nodeWrapper.node[nodeProperty];
1071
+ var wrappersByProperty = cache.get(property) || cache.set(property, []);
1072
+ wrappersByProperty.push(nodeWrapper);
1073
+ }
1074
+ };
1075
+ }
1076
+
1077
+ var uniqueIDSupported = util.isHostProperty(document.documentElement, "uniqueID");
1078
+
1079
+ function Session() {
1080
+ this.initCaches();
1081
+ }
1082
+
1083
+ Session.prototype = {
1084
+ initCaches: function() {
1085
+ this.elementCache = uniqueIDSupported ? (function() {
1086
+ var elementsCache = new Cache();
1087
+
1088
+ return {
1089
+ get: function(el) {
1090
+ return elementsCache.get(el.uniqueID);
1091
+ },
1092
+
1093
+ set: function(elWrapper) {
1094
+ elementsCache.set(elWrapper.node.uniqueID, elWrapper);
1095
+ }
1096
+ };
1097
+ })() : createWrapperCache("tagName");
1098
+
1099
+ // Store text nodes keyed by data, although we may need to truncate this
1100
+ this.textNodeCache = createWrapperCache("data");
1101
+ this.otherNodeCache = createWrapperCache("nodeName");
1102
+ },
1103
+
1104
+ getNodeWrapper: function(node) {
1105
+ var wrapperCache;
1106
+ switch (node.nodeType) {
1107
+ case 1:
1108
+ wrapperCache = this.elementCache;
1109
+ break;
1110
+ case 3:
1111
+ wrapperCache = this.textNodeCache;
1112
+ break;
1113
+ default:
1114
+ wrapperCache = this.otherNodeCache;
1115
+ break;
1116
+ }
1117
+
1118
+ var wrapper = wrapperCache.get(node);
1119
+ if (!wrapper) {
1120
+ wrapper = new NodeWrapper(node, this);
1121
+ wrapperCache.set(wrapper);
1122
+ }
1123
+ return wrapper;
1124
+ },
1125
+
1126
+ getPosition: function(node, offset) {
1127
+ return this.getNodeWrapper(node).getPosition(offset);
1128
+ },
1129
+
1130
+ getRangeBoundaryPosition: function(range, isStart) {
1131
+ var prefix = isStart ? "start" : "end";
1132
+ return this.getPosition(range[prefix + "Container"], range[prefix + "Offset"]);
1133
+ },
1134
+
1135
+ detach: function() {
1136
+ this.elementCache = this.textNodeCache = this.otherNodeCache = null;
1137
+ }
1138
+ };
1139
+
1140
+ return Session;
1141
+ })();
1142
+
1143
+ /*----------------------------------------------------------------------------------------------------------------*/
1144
+
1145
+ function startSession() {
1146
+ endSession();
1147
+ return (currentSession = new Session());
1148
+ }
1149
+
1150
+ function getSession() {
1151
+ return currentSession || startSession();
1152
+ }
1153
+
1154
+ function endSession() {
1155
+ if (currentSession) {
1156
+ currentSession.detach();
1157
+ }
1158
+ currentSession = null;
1159
+ }
1160
+
1161
+ /*----------------------------------------------------------------------------------------------------------------*/
1162
+
1163
+ // Extensions to the rangy.dom utility object
1164
+
1165
+ extend(dom, {
1166
+ nextNode: nextNode,
1167
+ previousNode: previousNode
1168
+ });
1169
+
1170
+ /*----------------------------------------------------------------------------------------------------------------*/
1171
+
1172
+ function createCharacterIterator(startPos, backward, endPos, characterOptions) {
1173
+
1174
+ // Adjust the end position to ensure that it is actually reached
1175
+ if (endPos) {
1176
+ if (backward) {
1177
+ if (isCollapsedNode(endPos.node)) {
1178
+ endPos = startPos.previousVisible();
1179
+ }
1180
+ } else {
1181
+ if (isCollapsedNode(endPos.node)) {
1182
+ endPos = endPos.nextVisible();
1183
+ }
1184
+ }
1185
+ }
1186
+
1187
+ var pos = startPos, finished = false;
1188
+
1189
+ function next() {
1190
+ var charPos = null;
1191
+ if (backward) {
1192
+ charPos = pos;
1193
+ if (!finished) {
1194
+ pos = pos.previousVisible();
1195
+ finished = !pos || (endPos && pos.equals(endPos));
1196
+ }
1197
+ } else {
1198
+ if (!finished) {
1199
+ charPos = pos = pos.nextVisible();
1200
+ finished = !pos || (endPos && pos.equals(endPos));
1201
+ }
1202
+ }
1203
+ if (finished) {
1204
+ pos = null;
1205
+ }
1206
+ return charPos;
1207
+ }
1208
+
1209
+ var previousTextPos, returnPreviousTextPos = false;
1210
+
1211
+ return {
1212
+ next: function() {
1213
+ if (returnPreviousTextPos) {
1214
+ returnPreviousTextPos = false;
1215
+ return previousTextPos;
1216
+ } else {
1217
+ var pos, character;
1218
+ while ( (pos = next()) ) {
1219
+ character = pos.getCharacter(characterOptions);
1220
+ if (character) {
1221
+ previousTextPos = pos;
1222
+ return pos;
1223
+ }
1224
+ }
1225
+ return null;
1226
+ }
1227
+ },
1228
+
1229
+ rewind: function() {
1230
+ if (previousTextPos) {
1231
+ returnPreviousTextPos = true;
1232
+ } else {
1233
+ throw module.createError("createCharacterIterator: cannot rewind. Only one position can be rewound.");
1234
+ }
1235
+ },
1236
+
1237
+ dispose: function() {
1238
+ startPos = endPos = null;
1239
+ }
1240
+ };
1241
+ }
1242
+
1243
+ var arrayIndexOf = Array.prototype.indexOf ?
1244
+ function(arr, val) {
1245
+ return arr.indexOf(val);
1246
+ } :
1247
+ function(arr, val) {
1248
+ for (var i = 0, len = arr.length; i < len; ++i) {
1249
+ if (arr[i] === val) {
1250
+ return i;
1251
+ }
1252
+ }
1253
+ return -1;
1254
+ };
1255
+
1256
+ // Provides a pair of iterators over text positions, tokenized. Transparently requests more text when next()
1257
+ // is called and there is no more tokenized text
1258
+ function createTokenizedTextProvider(pos, characterOptions, wordOptions) {
1259
+ var forwardIterator = createCharacterIterator(pos, false, null, characterOptions);
1260
+ var backwardIterator = createCharacterIterator(pos, true, null, characterOptions);
1261
+ var tokenizer = wordOptions.tokenizer;
1262
+
1263
+ // Consumes a word and the whitespace beyond it
1264
+ function consumeWord(forward) {
1265
+ var pos, textChar;
1266
+ var newChars = [], it = forward ? forwardIterator : backwardIterator;
1267
+
1268
+ var passedWordBoundary = false, insideWord = false;
1269
+
1270
+ while ( (pos = it.next()) ) {
1271
+ textChar = pos.character;
1272
+
1273
+
1274
+ if (allWhiteSpaceRegex.test(textChar)) {
1275
+ if (insideWord) {
1276
+ insideWord = false;
1277
+ passedWordBoundary = true;
1278
+ }
1279
+ } else {
1280
+ if (passedWordBoundary) {
1281
+ it.rewind();
1282
+ break;
1283
+ } else {
1284
+ insideWord = true;
1285
+ }
1286
+ }
1287
+ newChars.push(pos);
1288
+ }
1289
+
1290
+
1291
+ return newChars;
1292
+ }
1293
+
1294
+ // Get initial word surrounding initial position and tokenize it
1295
+ var forwardChars = consumeWord(true);
1296
+ var backwardChars = consumeWord(false).reverse();
1297
+ var tokens = tokenize(backwardChars.concat(forwardChars), wordOptions, tokenizer);
1298
+
1299
+ // Create initial token buffers
1300
+ var forwardTokensBuffer = forwardChars.length ?
1301
+ tokens.slice(arrayIndexOf(tokens, forwardChars[0].token)) : [];
1302
+
1303
+ var backwardTokensBuffer = backwardChars.length ?
1304
+ tokens.slice(0, arrayIndexOf(tokens, backwardChars.pop().token) + 1) : [];
1305
+
1306
+ function inspectBuffer(buffer) {
1307
+ var textPositions = ["[" + buffer.length + "]"];
1308
+ for (var i = 0; i < buffer.length; ++i) {
1309
+ textPositions.push("(word: " + buffer[i] + ", is word: " + buffer[i].isWord + ")");
1310
+ }
1311
+ return textPositions;
1312
+ }
1313
+
1314
+
1315
+ return {
1316
+ nextEndToken: function() {
1317
+ var lastToken, forwardChars;
1318
+
1319
+ // If we're down to the last token, consume character chunks until we have a word or run out of
1320
+ // characters to consume
1321
+ while ( forwardTokensBuffer.length == 1 &&
1322
+ !(lastToken = forwardTokensBuffer[0]).isWord &&
1323
+ (forwardChars = consumeWord(true)).length > 0) {
1324
+
1325
+ // Merge trailing non-word into next word and tokenize
1326
+ forwardTokensBuffer = tokenize(lastToken.chars.concat(forwardChars), wordOptions, tokenizer);
1327
+ }
1328
+
1329
+ return forwardTokensBuffer.shift();
1330
+ },
1331
+
1332
+ previousStartToken: function() {
1333
+ var lastToken, backwardChars;
1334
+
1335
+ // If we're down to the last token, consume character chunks until we have a word or run out of
1336
+ // characters to consume
1337
+ while ( backwardTokensBuffer.length == 1 &&
1338
+ !(lastToken = backwardTokensBuffer[0]).isWord &&
1339
+ (backwardChars = consumeWord(false)).length > 0) {
1340
+
1341
+ // Merge leading non-word into next word and tokenize
1342
+ backwardTokensBuffer = tokenize(backwardChars.reverse().concat(lastToken.chars), wordOptions, tokenizer);
1343
+ }
1344
+
1345
+ return backwardTokensBuffer.pop();
1346
+ },
1347
+
1348
+ dispose: function() {
1349
+ forwardIterator.dispose();
1350
+ backwardIterator.dispose();
1351
+ forwardTokensBuffer = backwardTokensBuffer = null;
1352
+ }
1353
+ };
1354
+ }
1355
+
1356
+ function movePositionBy(pos, unit, count, characterOptions, wordOptions) {
1357
+ var unitsMoved = 0, currentPos, newPos = pos, charIterator, nextPos, absCount = Math.abs(count), token;
1358
+ if (count !== 0) {
1359
+ var backward = (count < 0);
1360
+
1361
+ switch (unit) {
1362
+ case CHARACTER:
1363
+ charIterator = createCharacterIterator(pos, backward, null, characterOptions);
1364
+ while ( (currentPos = charIterator.next()) && unitsMoved < absCount ) {
1365
+ ++unitsMoved;
1366
+ newPos = currentPos;
1367
+ }
1368
+ nextPos = currentPos;
1369
+ charIterator.dispose();
1370
+ break;
1371
+ case WORD:
1372
+ var tokenizedTextProvider = createTokenizedTextProvider(pos, characterOptions, wordOptions);
1373
+ var next = backward ? tokenizedTextProvider.previousStartToken : tokenizedTextProvider.nextEndToken;
1374
+
1375
+ while ( (token = next()) && unitsMoved < absCount ) {
1376
+ if (token.isWord) {
1377
+ ++unitsMoved;
1378
+ newPos = backward ? token.chars[0] : token.chars[token.chars.length - 1];
1379
+ }
1380
+ }
1381
+ break;
1382
+ default:
1383
+ throw new Error("movePositionBy: unit '" + unit + "' not implemented");
1384
+ }
1385
+
1386
+ // Perform any necessary position tweaks
1387
+ if (backward) {
1388
+ newPos = newPos.previousVisible();
1389
+ unitsMoved = -unitsMoved;
1390
+ } else if (newPos && newPos.isLeadingSpace && !newPos.isTrailingSpace) {
1391
+ // Tweak the position for the case of a leading space. The problem is that an uncollapsed leading space
1392
+ // before a block element (for example, the line break between "1" and "2" in the following HTML:
1393
+ // "1<p>2</p>") is considered to be attached to the position immediately before the block element, which
1394
+ // corresponds with a different selection position in most browsers from the one we want (i.e. at the
1395
+ // start of the contents of the block element). We get round this by advancing the position returned to
1396
+ // the last possible equivalent visible position.
1397
+ if (unit == WORD) {
1398
+ charIterator = createCharacterIterator(pos, false, null, characterOptions);
1399
+ nextPos = charIterator.next();
1400
+ charIterator.dispose();
1401
+ }
1402
+ if (nextPos) {
1403
+ newPos = nextPos.previousVisible();
1404
+ }
1405
+ }
1406
+ }
1407
+
1408
+
1409
+ return {
1410
+ position: newPos,
1411
+ unitsMoved: unitsMoved
1412
+ };
1413
+ }
1414
+
1415
+ function createRangeCharacterIterator(session, range, characterOptions, backward) {
1416
+ var rangeStart = session.getRangeBoundaryPosition(range, true);
1417
+ var rangeEnd = session.getRangeBoundaryPosition(range, false);
1418
+ var itStart = backward ? rangeEnd : rangeStart;
1419
+ var itEnd = backward ? rangeStart : rangeEnd;
1420
+
1421
+ return createCharacterIterator(itStart, !!backward, itEnd, characterOptions);
1422
+ }
1423
+
1424
+ function getRangeCharacters(session, range, characterOptions) {
1425
+
1426
+ var chars = [], it = createRangeCharacterIterator(session, range, characterOptions), pos;
1427
+ while ( (pos = it.next()) ) {
1428
+ chars.push(pos);
1429
+ }
1430
+
1431
+ it.dispose();
1432
+ return chars;
1433
+ }
1434
+
1435
+ function isWholeWord(startPos, endPos, wordOptions) {
1436
+ var range = api.createRange(startPos.node);
1437
+ range.setStartAndEnd(startPos.node, startPos.offset, endPos.node, endPos.offset);
1438
+ return !range.expand("word", { wordOptions: wordOptions });
1439
+ }
1440
+
1441
+ function findTextFromPosition(initialPos, searchTerm, isRegex, searchScopeRange, findOptions) {
1442
+ var backward = isDirectionBackward(findOptions.direction);
1443
+ var it = createCharacterIterator(
1444
+ initialPos,
1445
+ backward,
1446
+ initialPos.session.getRangeBoundaryPosition(searchScopeRange, backward),
1447
+ findOptions.characterOptions
1448
+ );
1449
+ var text = "", chars = [], pos, currentChar, matchStartIndex, matchEndIndex;
1450
+ var result, insideRegexMatch;
1451
+ var returnValue = null;
1452
+
1453
+ function handleMatch(startIndex, endIndex) {
1454
+ var startPos = chars[startIndex].previousVisible();
1455
+ var endPos = chars[endIndex - 1];
1456
+ var valid = (!findOptions.wholeWordsOnly || isWholeWord(startPos, endPos, findOptions.wordOptions));
1457
+
1458
+ return {
1459
+ startPos: startPos,
1460
+ endPos: endPos,
1461
+ valid: valid
1462
+ };
1463
+ }
1464
+
1465
+ while ( (pos = it.next()) ) {
1466
+ currentChar = pos.character;
1467
+ if (!isRegex && !findOptions.caseSensitive) {
1468
+ currentChar = currentChar.toLowerCase();
1469
+ }
1470
+
1471
+ if (backward) {
1472
+ chars.unshift(pos);
1473
+ text = currentChar + text;
1474
+ } else {
1475
+ chars.push(pos);
1476
+ text += currentChar;
1477
+ }
1478
+
1479
+ if (isRegex) {
1480
+ result = searchTerm.exec(text);
1481
+ if (result) {
1482
+ matchStartIndex = result.index;
1483
+ matchEndIndex = matchStartIndex + result[0].length;
1484
+ if (insideRegexMatch) {
1485
+ // Check whether the match is now over
1486
+ if ((!backward && matchEndIndex < text.length) || (backward && matchStartIndex > 0)) {
1487
+ returnValue = handleMatch(matchStartIndex, matchEndIndex);
1488
+ break;
1489
+ }
1490
+ } else {
1491
+ insideRegexMatch = true;
1492
+ }
1493
+ }
1494
+ } else if ( (matchStartIndex = text.indexOf(searchTerm)) != -1 ) {
1495
+ returnValue = handleMatch(matchStartIndex, matchStartIndex + searchTerm.length);
1496
+ break;
1497
+ }
1498
+ }
1499
+
1500
+ // Check whether regex match extends to the end of the range
1501
+ if (insideRegexMatch) {
1502
+ returnValue = handleMatch(matchStartIndex, matchEndIndex);
1503
+ }
1504
+ it.dispose();
1505
+
1506
+ return returnValue;
1507
+ }
1508
+
1509
+ function createEntryPointFunction(func) {
1510
+ return function() {
1511
+ var sessionRunning = !!currentSession;
1512
+ var session = getSession();
1513
+ var args = [session].concat( util.toArray(arguments) );
1514
+ var returnValue = func.apply(this, args);
1515
+ if (!sessionRunning) {
1516
+ endSession();
1517
+ }
1518
+ return returnValue;
1519
+ };
1520
+ }
1521
+
1522
+ /*----------------------------------------------------------------------------------------------------------------*/
1523
+
1524
+ // Extensions to the Rangy Range object
1525
+
1526
+ function createRangeBoundaryMover(isStart, collapse) {
1527
+ /*
1528
+ Unit can be "character" or "word"
1529
+ Options:
1530
+
1531
+ - includeTrailingSpace
1532
+ - wordRegex
1533
+ - tokenizer
1534
+ - collapseSpaceBeforeLineBreak
1535
+ */
1536
+ return createEntryPointFunction(
1537
+ function(session, unit, count, moveOptions) {
1538
+ if (typeof count == UNDEF) {
1539
+ count = unit;
1540
+ unit = CHARACTER;
1541
+ }
1542
+ moveOptions = createNestedOptions(moveOptions, defaultMoveOptions);
1543
+
1544
+ var boundaryIsStart = isStart;
1545
+ if (collapse) {
1546
+ boundaryIsStart = (count >= 0);
1547
+ this.collapse(!boundaryIsStart);
1548
+ }
1549
+ var moveResult = movePositionBy(session.getRangeBoundaryPosition(this, boundaryIsStart), unit, count, moveOptions.characterOptions, moveOptions.wordOptions);
1550
+ var newPos = moveResult.position;
1551
+ this[boundaryIsStart ? "setStart" : "setEnd"](newPos.node, newPos.offset);
1552
+ return moveResult.unitsMoved;
1553
+ }
1554
+ );
1555
+ }
1556
+
1557
+ function createRangeTrimmer(isStart) {
1558
+ return createEntryPointFunction(
1559
+ function(session, characterOptions) {
1560
+ characterOptions = createOptions(characterOptions, defaultCharacterOptions);
1561
+ var pos;
1562
+ var it = createRangeCharacterIterator(session, this, characterOptions, !isStart);
1563
+ var trimCharCount = 0;
1564
+ while ( (pos = it.next()) && allWhiteSpaceRegex.test(pos.character) ) {
1565
+ ++trimCharCount;
1566
+ }
1567
+ it.dispose();
1568
+ var trimmed = (trimCharCount > 0);
1569
+ if (trimmed) {
1570
+ this[isStart ? "moveStart" : "moveEnd"](
1571
+ "character",
1572
+ isStart ? trimCharCount : -trimCharCount,
1573
+ { characterOptions: characterOptions }
1574
+ );
1575
+ }
1576
+ return trimmed;
1577
+ }
1578
+ );
1579
+ }
1580
+
1581
+ extend(api.rangePrototype, {
1582
+ moveStart: createRangeBoundaryMover(true, false),
1583
+
1584
+ moveEnd: createRangeBoundaryMover(false, false),
1585
+
1586
+ move: createRangeBoundaryMover(true, true),
1587
+
1588
+ trimStart: createRangeTrimmer(true),
1589
+
1590
+ trimEnd: createRangeTrimmer(false),
1591
+
1592
+ trim: createEntryPointFunction(
1593
+ function(session, characterOptions) {
1594
+ var startTrimmed = this.trimStart(characterOptions), endTrimmed = this.trimEnd(characterOptions);
1595
+ return startTrimmed || endTrimmed;
1596
+ }
1597
+ ),
1598
+
1599
+ expand: createEntryPointFunction(
1600
+ function(session, unit, expandOptions) {
1601
+ var moved = false;
1602
+ expandOptions = createNestedOptions(expandOptions, defaultExpandOptions);
1603
+ var characterOptions = expandOptions.characterOptions;
1604
+ if (!unit) {
1605
+ unit = CHARACTER;
1606
+ }
1607
+ if (unit == WORD) {
1608
+ var wordOptions = expandOptions.wordOptions;
1609
+ var startPos = session.getRangeBoundaryPosition(this, true);
1610
+ var endPos = session.getRangeBoundaryPosition(this, false);
1611
+
1612
+ var startTokenizedTextProvider = createTokenizedTextProvider(startPos, characterOptions, wordOptions);
1613
+ var startToken = startTokenizedTextProvider.nextEndToken();
1614
+ var newStartPos = startToken.chars[0].previousVisible();
1615
+ var endToken, newEndPos;
1616
+
1617
+ if (this.collapsed) {
1618
+ endToken = startToken;
1619
+ } else {
1620
+ var endTokenizedTextProvider = createTokenizedTextProvider(endPos, characterOptions, wordOptions);
1621
+ endToken = endTokenizedTextProvider.previousStartToken();
1622
+ }
1623
+ newEndPos = endToken.chars[endToken.chars.length - 1];
1624
+
1625
+ if (!newStartPos.equals(startPos)) {
1626
+ this.setStart(newStartPos.node, newStartPos.offset);
1627
+ moved = true;
1628
+ }
1629
+ if (newEndPos && !newEndPos.equals(endPos)) {
1630
+ this.setEnd(newEndPos.node, newEndPos.offset);
1631
+ moved = true;
1632
+ }
1633
+
1634
+ if (expandOptions.trim) {
1635
+ if (expandOptions.trimStart) {
1636
+ moved = this.trimStart(characterOptions) || moved;
1637
+ }
1638
+ if (expandOptions.trimEnd) {
1639
+ moved = this.trimEnd(characterOptions) || moved;
1640
+ }
1641
+ }
1642
+
1643
+ return moved;
1644
+ } else {
1645
+ return this.moveEnd(CHARACTER, 1, expandOptions);
1646
+ }
1647
+ }
1648
+ ),
1649
+
1650
+ text: createEntryPointFunction(
1651
+ function(session, characterOptions) {
1652
+ return this.collapsed ?
1653
+ "" : getRangeCharacters(session, this, createOptions(characterOptions, defaultCharacterOptions)).join("");
1654
+ }
1655
+ ),
1656
+
1657
+ selectCharacters: createEntryPointFunction(
1658
+ function(session, containerNode, startIndex, endIndex, characterOptions) {
1659
+ var moveOptions = { characterOptions: characterOptions };
1660
+ if (!containerNode) {
1661
+ containerNode = getBody( this.getDocument() );
1662
+ }
1663
+ this.selectNodeContents(containerNode);
1664
+ this.collapse(true);
1665
+ this.moveStart("character", startIndex, moveOptions);
1666
+ this.collapse(true);
1667
+ this.moveEnd("character", endIndex - startIndex, moveOptions);
1668
+ }
1669
+ ),
1670
+
1671
+ // Character indexes are relative to the start of node
1672
+ toCharacterRange: createEntryPointFunction(
1673
+ function(session, containerNode, characterOptions) {
1674
+ if (!containerNode) {
1675
+ containerNode = getBody( this.getDocument() );
1676
+ }
1677
+ var parent = containerNode.parentNode, nodeIndex = dom.getNodeIndex(containerNode);
1678
+ var rangeStartsBeforeNode = (dom.comparePoints(this.startContainer, this.endContainer, parent, nodeIndex) == -1);
1679
+ var rangeBetween = this.cloneRange();
1680
+ var startIndex, endIndex;
1681
+ if (rangeStartsBeforeNode) {
1682
+ rangeBetween.setStartAndEnd(this.startContainer, this.startOffset, parent, nodeIndex);
1683
+ startIndex = -rangeBetween.text(characterOptions).length;
1684
+ } else {
1685
+ rangeBetween.setStartAndEnd(parent, nodeIndex, this.startContainer, this.startOffset);
1686
+ startIndex = rangeBetween.text(characterOptions).length;
1687
+ }
1688
+ endIndex = startIndex + this.text(characterOptions).length;
1689
+
1690
+ return {
1691
+ start: startIndex,
1692
+ end: endIndex
1693
+ };
1694
+ }
1695
+ ),
1696
+
1697
+ findText: createEntryPointFunction(
1698
+ function(session, searchTermParam, findOptions) {
1699
+ // Set up options
1700
+ findOptions = createNestedOptions(findOptions, defaultFindOptions);
1701
+
1702
+ // Create word options if we're matching whole words only
1703
+ if (findOptions.wholeWordsOnly) {
1704
+ // We don't ever want trailing spaces for search results
1705
+ findOptions.wordOptions.includeTrailingSpace = false;
1706
+ }
1707
+
1708
+ var backward = isDirectionBackward(findOptions.direction);
1709
+
1710
+ // Create a range representing the search scope if none was provided
1711
+ var searchScopeRange = findOptions.withinRange;
1712
+ if (!searchScopeRange) {
1713
+ searchScopeRange = api.createRange();
1714
+ searchScopeRange.selectNodeContents(this.getDocument());
1715
+ }
1716
+
1717
+ // Examine and prepare the search term
1718
+ var searchTerm = searchTermParam, isRegex = false;
1719
+ if (typeof searchTerm == "string") {
1720
+ if (!findOptions.caseSensitive) {
1721
+ searchTerm = searchTerm.toLowerCase();
1722
+ }
1723
+ } else {
1724
+ isRegex = true;
1725
+ }
1726
+
1727
+ var initialPos = session.getRangeBoundaryPosition(this, !backward);
1728
+
1729
+ // Adjust initial position if it lies outside the search scope
1730
+ var comparison = searchScopeRange.comparePoint(initialPos.node, initialPos.offset);
1731
+
1732
+ if (comparison === -1) {
1733
+ initialPos = session.getRangeBoundaryPosition(searchScopeRange, true);
1734
+ } else if (comparison === 1) {
1735
+ initialPos = session.getRangeBoundaryPosition(searchScopeRange, false);
1736
+ }
1737
+
1738
+ var pos = initialPos;
1739
+ var wrappedAround = false;
1740
+
1741
+ // Try to find a match and ignore invalid ones
1742
+ var findResult;
1743
+ while (true) {
1744
+ findResult = findTextFromPosition(pos, searchTerm, isRegex, searchScopeRange, findOptions);
1745
+
1746
+ if (findResult) {
1747
+ if (findResult.valid) {
1748
+ this.setStartAndEnd(findResult.startPos.node, findResult.startPos.offset, findResult.endPos.node, findResult.endPos.offset);
1749
+ return true;
1750
+ } else {
1751
+ // We've found a match that is not a whole word, so we carry on searching from the point immediately
1752
+ // after the match
1753
+ pos = backward ? findResult.startPos : findResult.endPos;
1754
+ }
1755
+ } else if (findOptions.wrap && !wrappedAround) {
1756
+ // No result found but we're wrapping around and limiting the scope to the unsearched part of the range
1757
+ searchScopeRange = searchScopeRange.cloneRange();
1758
+ pos = session.getRangeBoundaryPosition(searchScopeRange, !backward);
1759
+ searchScopeRange.setBoundary(initialPos.node, initialPos.offset, backward);
1760
+ wrappedAround = true;
1761
+ } else {
1762
+ // Nothing found and we can't wrap around, so we're done
1763
+ return false;
1764
+ }
1765
+ }
1766
+ }
1767
+ ),
1768
+
1769
+ pasteHtml: function(html) {
1770
+ this.deleteContents();
1771
+ if (html) {
1772
+ var frag = this.createContextualFragment(html);
1773
+ var lastChild = frag.lastChild;
1774
+ this.insertNode(frag);
1775
+ this.collapseAfter(lastChild);
1776
+ }
1777
+ }
1778
+ });
1779
+
1780
+ /*----------------------------------------------------------------------------------------------------------------*/
1781
+
1782
+ // Extensions to the Rangy Selection object
1783
+
1784
+ function createSelectionTrimmer(methodName) {
1785
+ return createEntryPointFunction(
1786
+ function(session, characterOptions) {
1787
+ var trimmed = false;
1788
+ this.changeEachRange(function(range) {
1789
+ trimmed = range[methodName](characterOptions) || trimmed;
1790
+ });
1791
+ return trimmed;
1792
+ }
1793
+ );
1794
+ }
1795
+
1796
+ extend(api.selectionPrototype, {
1797
+ expand: createEntryPointFunction(
1798
+ function(session, unit, expandOptions) {
1799
+ this.changeEachRange(function(range) {
1800
+ range.expand(unit, expandOptions);
1801
+ });
1802
+ }
1803
+ ),
1804
+
1805
+ move: createEntryPointFunction(
1806
+ function(session, unit, count, options) {
1807
+ var unitsMoved = 0;
1808
+ if (this.focusNode) {
1809
+ this.collapse(this.focusNode, this.focusOffset);
1810
+ var range = this.getRangeAt(0);
1811
+ if (!options) {
1812
+ options = {};
1813
+ }
1814
+ options.characterOptions = createOptions(options.characterOptions, defaultCaretCharacterOptions);
1815
+ unitsMoved = range.move(unit, count, options);
1816
+ this.setSingleRange(range);
1817
+ }
1818
+ return unitsMoved;
1819
+ }
1820
+ ),
1821
+
1822
+ trimStart: createSelectionTrimmer("trimStart"),
1823
+ trimEnd: createSelectionTrimmer("trimEnd"),
1824
+ trim: createSelectionTrimmer("trim"),
1825
+
1826
+ selectCharacters: createEntryPointFunction(
1827
+ function(session, containerNode, startIndex, endIndex, direction, characterOptions) {
1828
+ var range = api.createRange(containerNode);
1829
+ range.selectCharacters(containerNode, startIndex, endIndex, characterOptions);
1830
+ this.setSingleRange(range, direction);
1831
+ }
1832
+ ),
1833
+
1834
+ saveCharacterRanges: createEntryPointFunction(
1835
+ function(session, containerNode, characterOptions) {
1836
+ var ranges = this.getAllRanges(), rangeCount = ranges.length;
1837
+ var rangeInfos = [];
1838
+
1839
+ var backward = rangeCount == 1 && this.isBackward();
1840
+
1841
+ for (var i = 0, len = ranges.length; i < len; ++i) {
1842
+ rangeInfos[i] = {
1843
+ characterRange: ranges[i].toCharacterRange(containerNode, characterOptions),
1844
+ backward: backward,
1845
+ characterOptions: characterOptions
1846
+ };
1847
+ }
1848
+
1849
+ return rangeInfos;
1850
+ }
1851
+ ),
1852
+
1853
+ restoreCharacterRanges: createEntryPointFunction(
1854
+ function(session, containerNode, saved) {
1855
+ this.removeAllRanges();
1856
+ for (var i = 0, len = saved.length, range, rangeInfo, characterRange; i < len; ++i) {
1857
+ rangeInfo = saved[i];
1858
+ characterRange = rangeInfo.characterRange;
1859
+ range = api.createRange(containerNode);
1860
+ range.selectCharacters(containerNode, characterRange.start, characterRange.end, rangeInfo.characterOptions);
1861
+ this.addRange(range, rangeInfo.backward);
1862
+ }
1863
+ }
1864
+ ),
1865
+
1866
+ text: createEntryPointFunction(
1867
+ function(session, characterOptions) {
1868
+ var rangeTexts = [];
1869
+ for (var i = 0, len = this.rangeCount; i < len; ++i) {
1870
+ rangeTexts[i] = this.getRangeAt(i).text(characterOptions);
1871
+ }
1872
+ return rangeTexts.join("");
1873
+ }
1874
+ )
1875
+ });
1876
+
1877
+ /*----------------------------------------------------------------------------------------------------------------*/
1878
+
1879
+ // Extensions to the core rangy object
1880
+
1881
+ api.innerText = function(el, characterOptions) {
1882
+ var range = api.createRange(el);
1883
+ range.selectNodeContents(el);
1884
+ var text = range.text(characterOptions);
1885
+ return text;
1886
+ };
1887
+
1888
+ api.createWordIterator = function(startNode, startOffset, iteratorOptions) {
1889
+ var session = getSession();
1890
+ iteratorOptions = createNestedOptions(iteratorOptions, defaultWordIteratorOptions);
1891
+ var startPos = session.getPosition(startNode, startOffset);
1892
+ var tokenizedTextProvider = createTokenizedTextProvider(startPos, iteratorOptions.characterOptions, iteratorOptions.wordOptions);
1893
+ var backward = isDirectionBackward(iteratorOptions.direction);
1894
+
1895
+ return {
1896
+ next: function() {
1897
+ return backward ? tokenizedTextProvider.previousStartToken() : tokenizedTextProvider.nextEndToken();
1898
+ },
1899
+
1900
+ dispose: function() {
1901
+ tokenizedTextProvider.dispose();
1902
+ this.next = function() {};
1903
+ }
1904
+ };
1905
+ };
1906
+
1907
+ /*----------------------------------------------------------------------------------------------------------------*/
1908
+
1909
+ api.noMutation = function(func) {
1910
+ var session = getSession();
1911
+ func(session);
1912
+ endSession();
1913
+ };
1914
+
1915
+ api.noMutation.createEntryPointFunction = createEntryPointFunction;
1916
+
1917
+ api.textRange = {
1918
+ isBlockNode: isBlockNode,
1919
+ isCollapsedWhitespaceNode: isCollapsedWhitespaceNode,
1920
+
1921
+ createPosition: createEntryPointFunction(
1922
+ function(session, node, offset) {
1923
+ return session.getPosition(node, offset);
1924
+ }
1925
+ )
1926
+ };
1927
+ });
1928
+
1929
+ return rangy;
1930
+ }, this);