Просмотр исходного кода

Merge branch 'master-redesign' into master-redesign-templates-and-blog

Conflicts:
	public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
	public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionManager.coffee
	public/coffee/ide/online-users/OnlineUsersManager.coffee
	public/coffee/ide/track-changes/directives/infiniteScroll.coffee
Henry Oswald 12 лет назад
Родитель
Сommit
c0a6f0b7ad
100 измененных файлов с 10096 добавлено и 15435 удалено
  1. 1 1
      services/web/.gitignore
  2. 0 4
      services/web/BackgroundJobsWorker.coffee
  3. 1 1
      services/web/Gruntfile.coffee
  4. 3 3
      services/web/app/coffee/Features/Editor/EditorController.coffee
  5. 11 11
      services/web/app/coffee/Features/Project/ProjectController.coffee
  6. 0 19
      services/web/app/coffee/Features/Subscription/SubscriptionBackgroundTasks.coffee
  7. 0 6
      services/web/app/coffee/Features/Subscription/SubscriptionLocator.coffee
  8. 0 6
      services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee
  9. 0 43
      services/web/app/views/external/about.jade
  10. 0 113
      services/web/app/views/external/home.jade
  11. 0 714
      services/web/app/views/external/learn.jade
  12. 0 16
      services/web/app/views/external/planned_maintenance.jade
  13. 0 26
      services/web/app/views/external/privacy.jade
  14. 0 98
      services/web/app/views/external/security.jade
  15. 0 62
      services/web/app/views/external/style_guide.jade
  16. 0 34
      services/web/app/views/external/tos.jade
  17. 1 1
      services/web/app/views/layout.jade
  18. 2 2
      services/web/app/views/layout/footer.jade
  19. 2 2
      services/web/app/views/layout/navbar.jade
  20. 5 3
      services/web/app/views/project/editor.jade
  21. 1 1
      services/web/app/views/project/editor/editor.jade
  22. 64 70
      services/web/app/views/project/editor/left-menu.jade
  23. 2 2
      services/web/app/views/project/editor/track-changes.jade
  24. 11 9
      services/web/app/views/project/list.jade
  25. 3 3
      services/web/app/views/subscriptions/group_admin.jade
  26. 2 1
      services/web/app/views/user/login.jade
  27. 2 1
      services/web/app/views/user/register.jade
  28. 2 1
      services/web/app/views/user/settings.jade
  29. 11 0
      services/web/public/coffee/directives/maxHeight.coffee
  30. 5 6
      services/web/public/coffee/ide/connection/ConnectionManager.coffee
  31. 1 0
      services/web/public/coffee/ide/directives/layout.coffee
  32. 1 1
      services/web/public/coffee/ide/editor/Document.coffee
  33. 1 6
      services/web/public/coffee/ide/editor/EditorManager.coffee
  34. 16 16
      services/web/public/coffee/ide/editor/directives/aceEditor.coffee
  35. 59 42
      services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
  36. 33 26
      services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionManager.coffee
  37. 1 3
      services/web/public/coffee/ide/editor/directives/aceEditor/highlights/HighlightsManager.coffee
  38. 6 11
      services/web/public/coffee/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.coffee
  39. 1 3
      services/web/public/coffee/ide/editor/directives/aceEditor/undo/UndoManager.coffee
  40. 1 2
      services/web/public/coffee/ide/file-tree/FileTreeManager.coffee
  41. 3 5
      services/web/public/coffee/ide/online-users/OnlineUsersManager.coffee
  42. 7 2
      services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
  43. 1 1
      services/web/public/coffee/ide/pdf/directives/pdfJs.coffee
  44. 0 2
      services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee
  45. 1 8
      services/web/public/coffee/ide/track-changes/TrackChangesManager.coffee
  46. 4 7
      services/web/public/coffee/ide/track-changes/directives/infiniteScroll.coffee
  47. 1 0
      services/web/public/coffee/main.coffee
  48. 6 1
      services/web/public/coffee/main/project-list.coffee
  49. 5366 4
      services/web/public/js/ace/ace.js
  50. 0 242
      services/web/public/js/ace/anchor.js
  51. 0 188
      services/web/public/js/ace/anchor_test.js
  52. 0 351
      services/web/public/js/ace/autocomplete.js
  53. 0 332
      services/web/public/js/ace/autocomplete/popup.js
  54. 0 78
      services/web/public/js/ace/autocomplete/text_completer.js
  55. 0 74
      services/web/public/js/ace/autocomplete/util.js
  56. 0 255
      services/web/public/js/ace/background_tokenizer.js
  57. 0 85
      services/web/public/js/ace/background_tokenizer_test.js
  58. 0 112
      services/web/public/js/ace/commands/command_manager.js
  59. 0 199
      services/web/public/js/ace/commands/command_manager_test.js
  60. 0 542
      services/web/public/js/ace/commands/default_commands.js
  61. 0 180
      services/web/public/js/ace/commands/incremental_search_commands.js
  62. 0 97
      services/web/public/js/ace/commands/multi_select_commands.js
  63. 0 110
      services/web/public/js/ace/commands/occur_commands.js
  64. 0 295
      services/web/public/js/ace/config.js
  65. 0 128
      services/web/public/js/ace/config_test.js
  66. BIN
      services/web/public/js/ace/css/codefolding-fold-button-states.png
  67. 0 85
      services/web/public/js/ace/css/editor.css
  68. BIN
      services/web/public/js/ace/css/expand-marker.png
  69. 0 642
      services/web/public/js/ace/document.js
  70. 0 306
      services/web/public/js/ace/document_test.js
  71. 0 2541
      services/web/public/js/ace/edit_session.js
  72. 0 219
      services/web/public/js/ace/edit_session/bracket_match.js
  73. 0 140
      services/web/public/js/ace/edit_session/fold.js
  74. 0 268
      services/web/public/js/ace/edit_session/fold_line.js
  75. 0 846
      services/web/public/js/ace/edit_session/folding.js
  76. 0 1075
      services/web/public/js/ace/edit_session_test.js
  77. 0 2444
      services/web/public/js/ace/editor.js
  78. 0 188
      services/web/public/js/ace/editor_change_document_test.js
  79. 0 223
      services/web/public/js/ace/editor_highlight_selected_word_test.js
  80. 0 164
      services/web/public/js/ace/editor_navigation_test.js
  81. 0 557
      services/web/public/js/ace/editor_text_edit_test.js
  82. 335 0
      services/web/public/js/ace/ext-beautify.js
  83. 6 444
      services/web/public/js/ace/ext-chromevox.js
  84. 7 50
      services/web/public/js/ace/ext-elastic_tabstops_lite.js
  85. 417 93
      services/web/public/js/ace/ext-emmet.js
  86. 6 0
      services/web/public/js/ace/ext-error_marker.js
  87. 180 0
      services/web/public/js/ace/ext-keybinding_menu.js
  88. 1813 0
      services/web/public/js/ace/ext-language_tools.js
  89. 53 0
      services/web/public/js/ace/ext-linking.js
  90. 14 11
      services/web/public/js/ace/ext-modelist.js
  91. 478 0
      services/web/public/js/ace/ext-old_ie.js
  92. 154 47
      services/web/public/js/ace/ext-searchbox.js
  93. 622 0
      services/web/public/js/ace/ext-settings_menu.js
  94. 6 3
      services/web/public/js/ace/ext-spellcheck.js
  95. 12 138
      services/web/public/js/ace/ext-split.js
  96. 36 69
      services/web/public/js/ace/ext-static_highlight.js
  97. 7 3
      services/web/public/js/ace/ext-statusbar.js
  98. 242 168
      services/web/public/js/ace/ext-textarea.js
  99. 59 0
      services/web/public/js/ace/ext-themelist.js
  100. 11 44
      services/web/public/js/ace/ext-whitespace.js

+ 1 - 1
services/web/.gitignore

@@ -66,4 +66,4 @@ Gemfile.lock
 *.swp
 .DS_Store
 
-
+app/views/external

+ 0 - 4
services/web/BackgroundJobsWorker.coffee

@@ -1,5 +1,4 @@
 settings = require('settings-sharelatex')
-SubscriptionBackgroundTasks = require("./app/js/Features/Subscription/SubscriptionBackgroundTasks")
 TpdsPollingBackgroundTasks = require("./app/js/Features/ThirdPartyDataStore/TpdsPollingBackgroundTasks")
 
 time =
@@ -19,7 +18,4 @@ runPeriodically = (funcToRun, periodLength)->
 			setTimeout recursiveReference, periodLength
 	setTimeout recursiveReference, 0
 
-# TODO: Remove this one month after the ability to start free trials was removed
-runPeriodically ((cb) -> SubscriptionBackgroundTasks.downgradeExpiredFreeTrials(cb)), time.oneHour
-
 runPeriodically ((cb) -> TpdsPollingBackgroundTasks.pollUsersWithDropbox(cb)), time.fifteenMinutes

+ 1 - 1
services/web/Gruntfile.coffee

@@ -165,7 +165,7 @@ module.exports = (grunt) ->
 	grunt.registerTask 'wrap_sharejs', 'Wrap the compiled ShareJS code for AMD module loading', () ->
 		content = fs.readFileSync "public/js/libs/sharejs.js"
 		fs.writeFileSync "public/js/libs/sharejs.js", """
-			define(["ace/range"], function() {
+			define(["ace/ace"], function() {
 				#{content}
 				return window.sharejs;
 			});

+ 3 - 3
services/web/app/coffee/Features/Editor/EditorController.coffee

@@ -155,7 +155,7 @@ module.exports = EditorController =
 
 	addDoc: (project_id, folder_id, docName, docLines, sl_req_id, callback = (error, doc)->)->
 		{callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
-		docName = sanitize.escape(docName)
+		docName = docName.trim()
 		logger.log sl_req_id:sl_req_id, "sending new doc to project #{project_id}"
 		Metrics.inc "editor.add-doc"
 		ProjectEntityHandler.addDoc project_id, folder_id, docName, docLines, sl_req_id, (err, doc, folder_id)=>
@@ -164,7 +164,7 @@ module.exports = EditorController =
 
 	addFile: (project_id, folder_id, fileName, path, sl_req_id, callback = (error, file)->)->
 		{callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
-		fileName = sanitize.escape(fileName)
+		fileName = fileName.trim()
 		logger.log  sl_req_id:sl_req_id, "sending new file to project #{project_id} with folderid: #{folder_id}"
 		Metrics.inc "editor.add-file"
 		ProjectEntityHandler.addFile project_id, folder_id, fileName, path, (err, fileRef, folder_id)=>
@@ -177,7 +177,7 @@ module.exports = EditorController =
 
 	addFolder: (project_id, folder_id, folderName, sl_req_id, callback = (error, folder)->)->
 		{callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
-		folderName = sanitize.escape(folderName)
+		folderName = folderName.trim()
 		logger.log "sending new folder to project #{project_id}"
 		Metrics.inc "editor.add-folder"
 		ProjectEntityHandler.addFolder project_id, folder_id, folderName, (err, folder, folder_id)=>

+ 11 - 11
services/web/app/coffee/Features/Project/ProjectController.coffee

@@ -87,8 +87,8 @@ module.exports = ProjectController =
 
 	newProject: (req, res)->
 		user = req.session.user
-		projectName = sanitize.escape(req.body.projectName)
-		template = sanitize.escape(req.body.template)
+		projectName = req.body.projectName?.trim()
+		template = req.body.template
 		logger.log user: user, type: template, name: projectName, "creating project"
 		async.waterfall [
 			(cb)->
@@ -201,7 +201,7 @@ module.exports = ProjectController =
 					bodyClasses: ["editor"]
 					project : project
 					project_id : project._id
-					userObject : JSON.stringify({
+					user : {
 						id    : user.id
 						email : user.email
 						first_name : user.first_name
@@ -209,18 +209,18 @@ module.exports = ProjectController =
 						referal_id : user.referal_id
 						subscription :
 							freeTrial: {allowed: allowedFreeTrial}
-					})
-					userSettingsObject: JSON.stringify({
+					}
+					userSettings: {
 						mode  : user.ace.mode
 						theme : user.ace.theme
 						fontSize : user.ace.fontSize
 						autoComplete: user.ace.autoComplete
 						pdfViewer : user.ace.pdfViewer
-					})
-					sharelatexObject : JSON.stringify({
+					}
+					sharelatex : {
 						siteUrl: Settings.siteUrl,
 						jsPath: res.locals.jsPath
-					})
+					}
 					privilegeLevel: privilegeLevel
 					loadPdfjs: (user.ace.pdfViewer == "pdfjs")
 					chatUrl: Settings.apis.chat.url
@@ -290,9 +290,9 @@ defaultSettingsForAnonymousUser = (user_id)->
 
 THEME_LIST = []
 do generateThemeList = () ->
-	files = fs.readdirSync __dirname + '/../../../../public/js/ace/theme'
+	files = fs.readdirSync __dirname + '/../../../../public/js/ace'
 	for file in files
-		if file.slice(-2) == "js"
-			cleanName = file.slice(0,-3)
+		if file.slice(-2) == "js" and file.match(/^theme-/)
+			cleanName = file.slice(0,-3).slice(6)
 			THEME_LIST.push cleanName
 

+ 0 - 19
services/web/app/coffee/Features/Subscription/SubscriptionBackgroundTasks.coffee

@@ -1,19 +0,0 @@
-async = require 'async'
-logger = require 'logger-sharelatex'
-SubscriptionUpdater = require("./SubscriptionUpdater")
-SubscriptionLocator = require("./SubscriptionLocator")
-
-module.exports = SubscriptionBackgroundJobs =
-	# TODO: Remove this one month after the ability to start free trials was removed
-	downgradeExpiredFreeTrials: (callback = (error, subscriptions)->) ->
-		SubscriptionLocator.expiredFreeTrials (error, subscriptions) =>
-			return callback(error) if error?
-			logger.log total_subscriptions:subscriptions.length, "downgraging subscriptions"
-			downgrades = []
-			for subscription in subscriptions
-				do (subscription) =>
-					downgrades.push	(cb) =>
-						logger.log subscription: subscription, "downgrading free trial"
-						SubscriptionUpdater.downgradeFreeTrial(subscription, cb)
-			async.series downgrades, (error) -> callback(error, subscriptions)
-

+ 0 - 6
services/web/app/coffee/Features/Subscription/SubscriptionLocator.coffee

@@ -12,9 +12,3 @@ module.exports =
 		logger.log user_id:user_id, "getting users subscription"
 		Subscription.findOne admin_id:user_id, callback
 
-	# TODO: Remove this one month after the ability to start free trials was removed
-	expiredFreeTrials: (callback = (error, subscriptions)->) ->
-		query =
-			"freeTrial.expiresAt": "$lt": new Date()
-			"freeTrial.downgraded": "$ne": true
-		Subscription.find query, callback

+ 0 - 6
services/web/app/coffee/Features/Subscription/SubscriptionUpdater.coffee

@@ -24,12 +24,6 @@ module.exports =
 				self._createNewSubscription adminUser_id, (err, subscription)->
 					self._updateSubscription recurlySubscription, subscription, callback
 
-	# TODO: Remove this one month after the ability to start free trials was removed
-	downgradeFreeTrial: (subscription, callback = (error)->) ->
-		UserFeaturesUpdater.updateFeatures subscription.admin_id, Settings.defaultPlanCode, ->
-			subscription.freeTrial.downgraded = true
-			subscription.save callback
-
 	addUserToGroup: (adminUser_id, user_id, callback)->
 		logger.log adminUser_id:adminUser_id, user_id:user_id, "adding user into mongo subscription"
 		searchOps = 

+ 0 - 43
services/web/app/views/external/about.jade

@@ -1,43 +0,0 @@
-extends ../layout
-
-block content
-	.content
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					.page-header
-						h1 About us
-					h2 Meet the team behind your favourite online LaTeX editor.
-					p.team-profile
-						span.img-container.img-circle
-							img(src='/img/about/henry_oswald.jpg')
-						strong Henry Oswald
-						|  built an experimental LaTeX editor in 2011 which later became ShareLaTeX. He is a trained software engineer who lives in London.
-						| Henry has been responsible for building up a reliable platform for ShareLaTeX that allows instant real-time collaboration.
-						| Henry is a strong advocate of Test Driven Development and makes sure we keep the ShareLaTeX code clean and easy to maintain.
-						a(href='https://twitter.com/henryoswald')  Follow me on Twitter
-						| .
-					p.team-profile
-						span.img-container.img-circle
-							img(src='/img/about/james_allen.jpg')
-						strong James Allen
-						|  started working with Henry early in 2012 and finished his PhD in theoretical physics early in 2013. James began working on
-						| one of the first online LaTeX editors, ScribTeX, in 2008 and he has played a large role in developing the technologies and
-						| concepts that made ScribTeX and now ShareLaTeX possible. James is also slightly too obsessed with typography and learning the internals of how LaTeX works.
-					p(style="clear: both")
-
-					h3 Motivation
-					p Our first priority with ShareLaTeX is to build a tool which makes life easier for all the LaTeX users out there.
-						| The "thank you"s and success stories are a strong motivator for us, and we hope to always be able to offer a fully-functional
-						| LaTeX editor which anyone can use for free.
-					p We also believe that charging money for tools like ShareLaTeX is important since it helps to guarantee the future of the site and
-						| the safety of your work, as well as allowing us to focus on it full time to develop new features. We prefer to provide a valuable
-						| service at a fair price rather than having to try run the site from adverts, or worse. As our customers, we are answerable to you and only you.
-
-					h3 Technologies
-					p
-						| We use a lot of exciting technologies to run ShareLaTeX. We have mutliple APIs behind the scenes that we've written in Node.js, but the one exception is our
-						a(href='https://github.com/scribtex/clsi')  open sourced compiler API.
-						|  This is written in Ruby on Rails. We generally write in
-						a(href='http://coffeescript.org/')  coffee-script
-						|  as we find it helps to speed up development and make our code more readable. Your data is stored in MongoDB, Redis and Amazon S3. We practice Test Driven Development (TDD) to help produce robust and clean code which is easy to refactor.

+ 0 - 113
services/web/app/views/external/home.jade

@@ -1,113 +0,0 @@
-link(rel='canonical', href='https://www.sharelatex.com')
-
-extends ../layout
-
-block content
-	.masthead
-		.container
-			.row
-				.col-md-12
-					h1 LaTeX,
-						col-md-  Evolved
-					p Introducing the first real-time collaborative LaTeX editor.
-			.row
-				.col-md-12
-					img(src="img/screen.png")
-		.container-fluid
-			.row
-				a(href="/register").btn.btn-lg.btn-hero Start Free Trial
-				a(href="/user/subscription/plans").btn.btn-lg.btn-primary.btn-hero Buy Now
-
-	.universities
-		.container
-			.row.row-spaced-small
-				div.small Used by over <strong>200,000</strong> students and academics at:
-				#slides.container-fluid
-					.row.row-spaced
-						.col-md-3
-							img(src="/img/crests/harvard.gif", alt="harvard university logo")
-						.col-md-3
-							img(src="/img/crests/mit.gif", alt="mit university logo")
-						.col-md-3
-							img(src="/img/crests/oxford.gif", alt="oxford university logo")
-						.col-md-3
-							img(src="/img/crests/tokyo.png", alt="tokyo university logo")
-					.row.row-spaced
-						.col-md-3
-							img(src="/img/crests/cambridge.png", alt="cambridge university logo")
-						.col-md-3
-							img(src="/img/crests/liverpool.jpg", alt="liverpool university logo")
-						.col-md-3
-							img(src="/img/crests/icl.png", alt="icl university logo")
-						.col-md-3
-							img(src="/img/crests/yale.png", alt="yale university logo")
-					.row.row-spaced
-						.col-md-3
-							img(src="/img/crests/durham.png", alt="durham university logo")
-						.col-md-3
-							img(src="/img/crests/nasa.png", alt="nasa university logo")
-						.col-md-3
-							img(src="/img/crests/toronto.gif", alt="toronto university logo")
-						.col-md-3
-							img(src="/img/crests/stanford.png", alt="stanford university logo")
-
-	.pattern-container
-		.container
-			.long-form-features
-				.row
-					.col-md-4.col-md-offset-2
-						h2.h1 Seamless Collaboration
-						p Keep your LaTeX collaborators up to date by letting everyone access and edit the same LaTeX document.
-					.col-md-3.col-md-offset-1
-						h2.h1
-							i.fa.fa-group.fa-5x
-
-				.row
-					.col-md-2.col-md-offset-3
-						h2.h1
-							i.fa.fa-thumbs-up.fa-5x
-					.col-md-4.col-md-offset-1
-						h2.h1 Easy to Use
-						p ShareLaTeX is the easiest LaTeX editor to get started if you’ve never used LaTeX before. No installation required!
-
-				.row
-					.col-md-4.col-md-offset-2
-						h2.h1 Work Anywhere
-						p You can access our LaTeX editor and compile your LaTeX documents from any computer with an internet connection.
-					.col-md-3.col-md-offset-1
-						h2.h1
-							i.fa.fa-globe.fa-5x
-
-				.row
-					.col-md-2.col-md-offset-3
-						h2.h1
-							i.fa.fa-dropbox.fa-5x
-					.col-md-4.col-md-offset-1
-						h2.h1 Sync with Dropbox
-						p Keep all of your LaTeX projects synced to your Dropbox folder.
-			.row
-				.col-md-12
-					p.text-center
-						a(href="/user/subscription/plans").btn.btn-primary.btn-lg Sign up now!
-
-			.row-spaced
-
-	script(src=jsPath + "libs/jquery.slides.min.js")
-	script(type="text/javascript").
-		$(function() {
-			$('#slides').slidesjs({
-				width: 940,
-				height: 100,
-				play: {
-					active: false,
-					auto: true,
-					interval: 5000
-				},
-				navigation: {
-					active: false
-				},
-				pagination: {
-					active: false
-				}
-			});
-		});

+ 0 - 714
services/web/app/views/external/learn.jade

@@ -1,714 +0,0 @@
-extends ../layout
-
-block content
-  .container
-    .row
-      .offset2.span8
-        #bodyContent
-        h1 Positioning images and tables
-          #mw-content-text.mw-content-ltr(lang='en', dir='ltr')
-            p
-              span.texhtml(style='font-family: \'CMU Serif\', cmr10, LMRoman10-Regular, \'Times New Roman\', \'Nimbus Roman No9 L\', Times, serif;')
-                | L
-                span(style='text-transform: uppercase; font-size: 70%; margin-left: -0.36em; vertical-align: 0.3em; line-height: 0; margin-right: -0.15em;') a
-                | T
-                span(style='text-transform: uppercase; margin-left: -0.1667em; vertical-align: -0.5ex; line-height: 0; margin-right: -0.125em;') e
-                | X
-              | is an editing tool that takes care of the format so you only have to worry about the contents of your document. Nevertheless, sometimes a better control of floating elements is necessary. This article explains how to position images and tables in a
-              span.texhtml(style='font-family: \'CMU Serif\', cmr10, LMRoman10-Regular, \'Times New Roman\', \'Nimbus Roman No9 L\', Times, serif;')
-                | L
-                span(style='text-transform: uppercase; font-size: 70%; margin-left: -0.36em; vertical-align: 0.3em; line-height: 0; margin-right: -0.15em;') a
-                | T
-                span(style='text-transform: uppercase; margin-left: -0.1667em; vertical-align: -0.5ex; line-height: 0; margin-right: -0.125em;') e
-                | X
-              | document.
-            table#toc.toc
-              tbody
-                tr
-                  td
-                    #toctitle
-                      h2 Contents
-                      span.toctoggle
-                        | [
-                        a#togglelink.internal(href='#') hide
-                        | ]
-                    ul
-                      li.toclevel-1.tocsection-1
-                        a(href='#Introduction')
-                          span.tocnumber 1
-                          span.toctext Introduction
-                      li.toclevel-1.tocsection-2
-                        a(href='#Positioning_images')
-                          span.tocnumber 2
-                          span.toctext Positioning images
-                        ul
-                          li.toclevel-2.tocsection-3
-                            a(href='#Basic_positioning')
-                              span.tocnumber 2.1
-                              span.toctext Basic positioning
-                          li.toclevel-2.tocsection-4
-                            a(href='#The_figure_environment')
-                              span.tocnumber 2.2
-                              span.toctext The figure environment
-                          li.toclevel-2.tocsection-5
-                            a(href='#Multiple_images_in_one_figure')
-                              span.tocnumber 2.3
-                              span.toctext Multiple images in one figure
-                          li.toclevel-2.tocsection-6
-                            a(href='#Wrapping_text_around_a_figure')
-                              span.tocnumber 2.4
-                              span.toctext Wrapping text around a figure
-                      li.toclevel-1.tocsection-7
-                        a(href='#Positioning_tables')
-                          span.tocnumber 3
-                          span.toctext Positioning tables
-                        ul
-                          li.toclevel-2.tocsection-8
-                            a(href='#Basic_positioning_2')
-                              span.tocnumber 3.1
-                              span.toctext Basic positioning
-                          li.toclevel-2.tocsection-9
-                            a(href='#The_table_environment')
-                              span.tocnumber 3.2
-                              span.toctext The table environment
-                          li.toclevel-2.tocsection-10
-                            a(href='#Wrapping_text_around_a_table')
-                              span.tocnumber 3.3
-                              span.toctext Wrapping text around a table
-                      li.toclevel-1.tocsection-11
-                        a(href='#Reference_guide')
-                          span.tocnumber 4
-                          span.toctext Reference guide
-                      li.toclevel-1.tocsection-12
-                        a(href='#Further_reading')
-                          span.tocnumber 5
-                          span.toctext Further reading
-            h1
-              span#Introduction.mw-headline Introduction
-            p
-              | The default alignment for images and tables is set to
-              i left
-
-            figure.wikifigure
-              .row-fluid
-                .span6
-                  pre(data-number)
-                    | \nLorem ipsum dolor sit amet, consectetuer adipiscing elit.
-                    | Etiam lobortis facilisis sem. Nullam nec mi et neque pharetra
-                    | sollicitudin.
-                    br
-                    br
-                    | Praesent imperdiet mi nec
-                    | ante. Donec ullamcorper, felis non sodales commodo, lectus velit
-                    | ultrices augue, a dignissim nibh lectus placerat pede.
-                    | Vivamus nunc nunc, molestie ut, ultricies
-                    | vel, semper in, velit. Ut porttitor.
-                .span6
-                  a(href='/learn/File:PositioningTnIEX1.png')
-                    img(alt='PositioningTnIEX1.png', src='/learn-scripts/images/thumb/2/23/PositioningTnIEX1.png/300px-PositioningTnIEX1.png', width='300', height='233')
-              figcaption
-                strong Figure 1.
-                |  Positioning floats within a document.
-            p
-            p
-              | This is a simple example, for a description of this and other ways to include images in your
-              span.texhtml(style='font-family: \'CMU Serif\', cmr10, LMRoman10-Regular, \'Times New Roman\', \'Nimbus Roman No9 L\', Times, serif;')
-                | L
-                span(style='text-transform: uppercase; font-size: 70%; margin-left: -0.36em; vertical-align: 0.3em; line-height: 0; margin-right: -0.15em;') a
-                | T
-                span(style='text-transform: uppercase; margin-left: -0.1667em; vertical-align: -0.5ex; line-height: 0; margin-right: -0.125em;') e
-                | X
-              | file see the article
-              a(href='/learn/Inserting_Images', title='Inserting Images') Inserting Images
-              | .
-            p
-              a.external.text(rel='nofollow', href='https://www.sharelatex.com/project/new/template?zipUrl=/project/532cda2653f42c100f002b56/download/zip&templateName=PositioningTablesandFigures&compiler=pdflatex')
-                img(alt='Open.png', src='/learn-scripts/images/7/7f/Open.png', width='24', height='24', style='vertical-align: middle;')
-                | Open an example in ShareLaTeX
-            h1
-              span#Positioning_images.mw-headline Positioning images
-            h2
-              span#Basic_positioning.mw-headline Basic positioning
-            p
-              | To change the default alignment of a image from
-              i left
-              | or
-              i right
-              | an easy manner is to add
-            .mw-geshi.mw-code.mw-content-ltr(dir='ltr')
-              .latex.source-latex
-                pre.de1.
-
-            p
-              | to the preamble of your file and then use an additional option in your image importing statement
-            p
-            figure.wikifigure
-              .row-fluid
-                .span6
-                  pre(data-number)
-                    | \nLorem ipsum dolor sit amet, consectetuer adipiscing elit.
-                    | Etiam lobortis facilisis sem. Nullam nec mi et neque pharetra
-                    | sollicitudin.
-                    br
-                    br
-                    | Praesent imperdiet mi nec
-                    | ante. Donec ullamcorper, felis non sodales commodo, lectus velit
-                    | ultrices augue, a dignissim nibh lectus placerat pede.
-                    | Vivamus nunc nunc, molestie ut, ultricies
-                    | vel, semper in, velit. Ut porttitor.
-                .span6
-                  a(href='/learn/File:PositioningTnIEX1.png')
-                    img(alt='PositioningTnIEX1.png', src='/learn-scripts/images/thumb/2/23/PositioningTnIEX1.png/300px-PositioningTnIEX1.png', width='300', height='233')
-              figcaption
-                strong Figure 1.
-                |  Positioning floats within a document.
-
-            p
-              | The package
-              b adjustbox
-              | enables an additional option in the
-              code \includegraphics
-              | command, in the example the picture is aligned to
-              code right
-              | . The available values are:
-              i left
-              | ,
-              i right
-              | ,
-              i center
-              | ,
-              i outer
-              | and
-              i inner
-              | , the last two are intended for two-sided documents.
-            p
-              a.external.text(rel='nofollow', href='https://www.sharelatex.com/project/new/template?zipUrl=/project/532cda2653f42c100f002b56/download/zip&templateName=PositioningTablesandFigures&compiler=pdflatex')
-                img(alt='Open.png', src='/learn-scripts/images/7/7f/Open.png', width='24', height='24', style='vertical-align: middle;')
-                | Open an example in ShareLaTeX
-            h2
-              span#The_figure_environment.mw-headline The figure environment
-            p
-              | The figure environment (see
-              a(href='/learn/Inserting_Images', title='Inserting Images') Inserting Images
-              | ) is intended to provide automatic positioning.
-            p
-            figure.wikifigure
-              .row-fluid
-                .span6
-                  pre(data-number)
-                    | \nLorem ipsum dolor sit amet, consectetuer adipiscing elit.
-                    | Etiam lobortis facilisis sem. Nullam nec mi et neque pharetra
-                    | sollicitudin.
-                    br
-                    br
-                    | Praesent imperdiet mi nec
-                    | ante. Donec ullamcorper, felis non sodales commodo, lectus velit
-                    | ultrices augue, a dignissim nibh lectus placerat pede.
-                    | Vivamus nunc nunc, molestie ut, ultricies
-                    | vel, semper in, velit. Ut porttitor.
-                .span6
-                  a(href='/learn/File:PositioningTnIEX1.png')
-                    img(alt='PositioningTnIEX1.png', src='/learn-scripts/images/thumb/2/23/PositioningTnIEX1.png/300px-PositioningTnIEX1.png', width='300', height='233')
-              figcaption
-                strong Figure 1.
-                |  Positioning floats within a document.
-            p
-              | This environment uses a positioning parameter passed inside brackets, it can take the next values:
-            table.wikitable
-              tbody
-                tr
-                  th
-                    | Parameter
-                  th
-                    | Position
-                tr
-                  td
-                    tt h
-                  td
-                    | Place the float
-                    i here
-                    | , i.e.,
-                    i approximately
-                    | at the same point it occurs in the source text (however, not
-                    i exactly
-                    | at the spot)
-                tr
-                  td
-                    tt t
-                  td
-                    | Position at the
-                    i top
-                    | of the page.
-                tr
-                  td
-                    tt b
-                  td
-                    | Position at the
-                    i bottom
-                    | of the page.
-                tr
-                  td
-                    tt p
-                  td
-                    | Put on a special
-                    i page
-                    | for floats only.
-                tr
-                  td
-                    tt !
-                  td
-                    | Override internal parameters LaTeX uses for determining "good" float positions.
-                tr
-                  td
-                    tt H
-                  td
-                    | Places the float at precisely the location in the
-                    span.texhtml(style='font-family: \'CMU Serif\', cmr10, LMRoman10-Regular, \'Times New Roman\', \'Nimbus Roman No9 L\', Times, serif;')
-                      | L
-                      span(style='text-transform: uppercase; font-size: 70%; margin-left: -0.36em; vertical-align: 0.3em; line-height: 0; margin-right: -0.15em;') a
-                      | T
-                      span(style='text-transform: uppercase; margin-left: -0.1667em; vertical-align: -0.5ex; line-height: 0; margin-right: -0.125em;') e
-                      | X
-                    | code. Requires the
-                    code float
-                    | package. This is somewhat equivalent to
-                    tt h!
-                    | .
-            p
-              | You can put more than one value in the parameter, for instance, if you write
-              code [ht]
-              span.texhtml(style='font-family: \'CMU Serif\', cmr10, LMRoman10-Regular, \'Times New Roman\', \'Nimbus Roman No9 L\', Times, serif;')
-                | L
-                span(style='text-transform: uppercase; font-size: 70%; margin-left: -0.36em; vertical-align: 0.3em; line-height: 0; margin-right: -0.15em;') a
-                | T
-                span(style='text-transform: uppercase; margin-left: -0.1667em; vertical-align: -0.5ex; line-height: 0; margin-right: -0.125em;') e
-                | X
-              | will try to position the figure
-              b here
-              | , but if it's not possible (the space may be insufficient) then the figure will appear at the
-              b top
-              | of the page. It is recommended to use more than one positioning parameter to prevent unexpected results.
-            p
-              a.external.text(rel='nofollow', href='https://www.sharelatex.com/project/new/template?zipUrl=/project/532cda2653f42c100f002b56/download/zip&templateName=PositioningTablesandFigures&compiler=pdflatex')
-                img(alt='Open.png', src='/learn-scripts/images/7/7f/Open.png', width='24', height='24', style='vertical-align: middle;')
-                | Open an example in ShareLaTeX
-            h2
-              span#Multiple_images_in_one_figure.mw-headline Multiple images in one figure
-            p
-              | It is possible to insert several images in one figure, each one with its own reference and label
-            p
-            figure.wikifigure
-              .row-fluid
-                .span6
-                  pre(data-number)
-                    | \nLorem ipsum dolor sit amet, consectetuer adipiscing elit.
-                    | Etiam lobortis facilisis sem. Nullam nec mi et neque pharetra
-                    | sollicitudin.
-                    br
-                    br
-                    | Praesent imperdiet mi nec
-                    | ante. Donec ullamcorper, felis non sodales commodo, lectus velit
-                    | ultrices augue, a dignissim nibh lectus placerat pede.
-                    | Vivamus nunc nunc, molestie ut, ultricies
-                    | vel, semper in, velit. Ut porttitor.
-                .span6
-                  a(href='/learn/File:PositioningTnIEX1.png')
-                    img(alt='PositioningTnIEX1.png', src='/learn-scripts/images/thumb/2/23/PositioningTnIEX1.png/300px-PositioningTnIEX1.png', width='300', height='233')
-              figcaption
-                strong Figure 1.
-                |  Positioning floats within a document.
-            p
-              | First, you must import the package
-              b subcaption
-              | by adding to the preamble
-            .mw-geshi.mw-code.mw-content-ltr(dir='ltr')
-              .latex.source-latex
-                pre.de1.
-
-            p
-              | then you can use the environment
-              code \subfigure
-              | that takes one parameter, the width of the figure. This environment must be used inside a
-              code figure
-              | environment, captions and labels can be set to each
-              i subfigure
-              | .
-            p
-              a.external.text(rel='nofollow', href='https://www.sharelatex.com/project/new/template?zipUrl=/project/532cda2653f42c100f002b56/download/zip&templateName=PositioningTablesandFigures&compiler=pdflatex')
-                img(alt='Open.png', src='/learn-scripts/images/7/7f/Open.png', width='24', height='24', style='vertical-align: middle;')
-                | Open an example in ShareLaTeX
-            h2
-              span#Wrapping_text_around_a_figure.mw-headline Wrapping text around a figure
-            p
-              | The package
-              b wrapfig
-              | provides a useful feature, text can be floated around the images.
-            p
-            figure.wikifigure
-              .row-fluid
-                .span6
-                  pre(data-number)
-                    | \nLorem ipsum dolor sit amet, consectetuer adipiscing elit.
-                    | Etiam lobortis facilisis sem. Nullam nec mi et neque pharetra
-                    | sollicitudin.
-                    br
-                    br
-                    | Praesent imperdiet mi nec
-                    | ante. Donec ullamcorper, felis non sodales commodo, lectus velit
-                    | ultrices augue, a dignissim nibh lectus placerat pede.
-                    | Vivamus nunc nunc, molestie ut, ultricies
-                    | vel, semper in, velit. Ut porttitor.
-                .span6
-                  a(href='/learn/File:PositioningTnIEX1.png')
-                    img(alt='PositioningTnIEX1.png', src='/learn-scripts/images/thumb/2/23/PositioningTnIEX1.png/300px-PositioningTnIEX1.png', width='300', height='233')
-              figcaption
-                strong Figure 1.
-                |  Positioning floats within a document.
-            p
-              | First import the package
-              i wrapfig
-              | by adding
-            .mw-geshi.mw-code.mw-content-ltr(dir='ltr')
-              .latex.source-latex
-                pre.de1.
-
-            p
-              | to the preamble.
-            p
-              | After that you can use the environment
-              code wrapfig
-              | , it takes two parameters that are passed inside braces: the alignement that can be
-              i l
-              | ,
-              i r
-              | ,
-              i c
-              | ,
-              i i
-              | or
-              i o
-              | ; this letters stand for left, right, centre, inner and outer (the last two intended for two-sided documents). The second parameter is the width of the figure, in the example is 0.25 the width of the text. See the
-              a(href='#Reference_guide') reference guide
-              | for a list of possible length units.
-            p
-              a.external.text(rel='nofollow', href='https://www.sharelatex.com/project/new/template?zipUrl=/project/532cda2653f42c100f002b56/download/zip&templateName=PositioningTablesandFigures&compiler=pdflatex')
-                img(alt='Open.png', src='/learn-scripts/images/7/7f/Open.png', width='24', height='24', style='vertical-align: middle;')
-                | Open an example in ShareLaTeX
-            h1
-              span#Positioning_tables.mw-headline Positioning tables
-            p
-              | Options for table positioning are similar to those available for figures
-            h2
-              span#Basic_positioning_2.mw-headline Basic positioning
-            p
-              | Default position of the tabular environment is
-              i centre
-              | .
-            p
-            figure.wikifigure
-              .row-fluid
-                .span6
-                  pre(data-number)
-                    | \nLorem ipsum dolor sit amet, consectetuer adipiscing elit.
-                    | Etiam lobortis facilisis sem. Nullam nec mi et neque pharetra
-                    | sollicitudin.
-                    br
-                    br
-                    | Praesent imperdiet mi nec
-                    | ante. Donec ullamcorper, felis non sodales commodo, lectus velit
-                    | ultrices augue, a dignissim nibh lectus placerat pede.
-                    | Vivamus nunc nunc, molestie ut, ultricies
-                    | vel, semper in, velit. Ut porttitor.
-                .span6
-                  a(href='/learn/File:PositioningTnIEX1.png')
-                    img(alt='PositioningTnIEX1.png', src='/learn-scripts/images/thumb/2/23/PositioningTnIEX1.png/300px-PositioningTnIEX1.png', width='300', height='233')
-              figcaption
-                strong Figure 1.
-                |  Positioning floats within a document.
-            p
-              | To learn about how to create tables see the
-              a(href='/learn/Tables', title='Tables') Tables
-              | article.
-            p
-              a.external.text(rel='nofollow', href='https://www.sharelatex.com/project/new/template?zipUrl=/project/532cda2653f42c100f002b56/download/zip&templateName=PositioningTablesandFigures&compiler=pdflatex')
-                img(alt='Open.png', src='/learn-scripts/images/7/7f/Open.png', width='24', height='24', style='vertical-align: middle;')
-                | Open an example in ShareLaTeX
-            h2
-              span#The_table_environment.mw-headline The table environment
-            p
-              | The table environment is intended to automatically position tables so they fit nicely in the flow of your document.
-            p
-            figure.wikifigure
-              .row-fluid
-                .span6
-                  pre(data-number)
-                    | \nLorem ipsum dolor sit amet, consectetuer adipiscing elit.
-                    | Etiam lobortis facilisis sem. Nullam nec mi et neque pharetra
-                    | sollicitudin.
-                    br
-                    br
-                    | Praesent imperdiet mi nec
-                    | ante. Donec ullamcorper, felis non sodales commodo, lectus velit
-                    | ultrices augue, a dignissim nibh lectus placerat pede.
-                    | Vivamus nunc nunc, molestie ut, ultricies
-                    | vel, semper in, velit. Ut porttitor.
-                .span6
-                  a(href='/learn/File:PositioningTnIEX1.png')
-                    img(alt='PositioningTnIEX1.png', src='/learn-scripts/images/thumb/2/23/PositioningTnIEX1.png/300px-PositioningTnIEX1.png', width='300', height='233')
-              figcaption
-                strong Figure 1.
-                |  Positioning floats within a document.
-            p
-              | A position parameter, inside brackets, can be passed to the
-              b table
-              | environment. This parameter can take the next values:
-            table.wikitable
-              tbody
-                tr
-                  th
-                    | Parameter
-                  th
-                    | Position
-                tr
-                  td
-                    tt h
-                  td
-                    | Place the float
-                    i here
-                    | , i.e.,
-                    i approximately
-                    | at the same point it occurs in the source text (however, not
-                    i exactly
-                    | at the spot)
-                tr
-                  td
-                    tt t
-                  td
-                    | Position at the
-                    i top
-                    | of the page.
-                tr
-                  td
-                    tt b
-                  td
-                    | Position at the
-                    i bottom
-                    | of the page.
-                tr
-                  td
-                    tt p
-                  td
-                    | Put on a special
-                    i page
-                    | for floats only.
-                tr
-                  td
-                    tt !
-                  td
-                    | Override internal parameters LaTeX uses for determining "good" float positions.
-                tr
-                  td
-                    tt H
-                  td
-                    | Places the float at precisely the location in the
-                    span.texhtml(style='font-family: \'CMU Serif\', cmr10, LMRoman10-Regular, \'Times New Roman\', \'Nimbus Roman No9 L\', Times, serif;')
-                      | L
-                      span(style='text-transform: uppercase; font-size: 70%; margin-left: -0.36em; vertical-align: 0.3em; line-height: 0; margin-right: -0.15em;') a
-                      | T
-                      span(style='text-transform: uppercase; margin-left: -0.1667em; vertical-align: -0.5ex; line-height: 0; margin-right: -0.125em;') e
-                      | X
-                    | code. Requires the
-                    code float
-                    | package. This is somewhat equivalent to
-                    tt h!
-                    | .
-            p
-              | You can set more than one value in the parameter, for instance, if you write
-              code [ht]
-              span.texhtml(style='font-family: \'CMU Serif\', cmr10, LMRoman10-Regular, \'Times New Roman\', \'Nimbus Roman No9 L\', Times, serif;')
-                | L
-                span(style='text-transform: uppercase; font-size: 70%; margin-left: -0.36em; vertical-align: 0.3em; line-height: 0; margin-right: -0.15em;') a
-                | T
-                span(style='text-transform: uppercase; margin-left: -0.1667em; vertical-align: -0.5ex; line-height: 0; margin-right: -0.125em;') e
-                | X
-              | will try to position the table
-              b here
-              | , but if it's not possible (the space may be insufficient) then the table will appear at the
-              b top
-              | of the page. It is recommended to use more than one positioning parameter to prevent unexpected results.
-            p
-              | Notice also the command
-              code \centering
-              | . This changes the alignment of the table within its container to
-              i centre
-              | instead of the default
-              i left
-              | .
-            p
-              a.external.text(rel='nofollow', href='https://www.sharelatex.com/project/new/template?zipUrl=/project/532cda2653f42c100f002b56/download/zip&templateName=PositioningTablesandFigures&compiler=pdflatex')
-                img(alt='Open.png', src='/learn-scripts/images/7/7f/Open.png', width='24', height='24', style='vertical-align: middle;')
-                | Open an example in ShareLaTeX
-            h2
-              span#Wrapping_text_around_a_table.mw-headline Wrapping text around a table
-            p
-              | If your table don't take all available space and you want to put text next or before it, is possible with the package
-              b wraptable
-              | .
-            p
-            figure.wikifigure
-              .row-fluid
-                .span6
-                  pre(data-number)
-                    | \nLorem ipsum dolor sit amet, consectetuer adipiscing elit.
-                    | Etiam lobortis facilisis sem. Nullam nec mi et neque pharetra
-                    | sollicitudin.
-                    br
-                    br
-                    | Praesent imperdiet mi nec
-                    | ante. Donec ullamcorper, felis non sodales commodo, lectus velit
-                    | ultrices augue, a dignissim nibh lectus placerat pede.
-                    | Vivamus nunc nunc, molestie ut, ultricies
-                    | vel, semper in, velit. Ut porttitor.
-                .span6
-                  a(href='/learn/File:PositioningTnIEX1.png')
-                    img(alt='PositioningTnIEX1.png', src='/learn-scripts/images/thumb/2/23/PositioningTnIEX1.png/300px-PositioningTnIEX1.png', width='300', height='233')
-              figcaption
-                strong Figure 1.
-                |  Positioning floats within a document.
-            p
-              | First, import the package
-            .mw-geshi.mw-code.mw-content-ltr(dir='ltr')
-              .latex.source-latex
-                pre.de1.
-
-            p
-              | then you can use the environment
-              i wraptable
-              | which takes two parameters: The first one is the alignment that can be
-              i l
-              | ,
-              i r
-              | ,
-              i c
-              | ,
-              i i
-              | or
-              i o
-              | for left, right, centre, inner and outer respectively. The second one is the width of the table container, keep in mind that this latter parameter must be the same as the width of the table, otherwise things may not be properly aligned.
-            p
-              a.external.text(rel='nofollow', href='https://www.sharelatex.com/project/new/template?zipUrl=/project/532cda2653f42c100f002b56/download/zip&templateName=PositioningTablesandFigures&compiler=pdflatex')
-                img(alt='Open.png', src='/learn-scripts/images/7/7f/Open.png', width='24', height='24', style='vertical-align: middle;')
-                | Open an example in ShareLaTeX
-            h1
-              span#Reference_guide.mw-headline Reference guide
-            p
-              b
-                span.texhtml(style='font-family: \'CMU Serif\', cmr10, LMRoman10-Regular, \'Times New Roman\', \'Nimbus Roman No9 L\', Times, serif;')
-                  | L
-                  span(style='text-transform: uppercase; font-size: 70%; margin-left: -0.36em; vertical-align: 0.3em; line-height: 0; margin-right: -0.15em;') a
-                  | T
-                  span(style='text-transform: uppercase; margin-left: -0.1667em; vertical-align: -0.5ex; line-height: 0; margin-right: -0.125em;') e
-                  | X
-                | units and lengths
-            table.wikitable
-              tbody
-                tr
-                  th
-                    | Abbreviation
-                  th
-                    | Definition
-                tr
-                  td
-                    tt pt
-                  td
-                    | A point, is the default length unit. About 0.3515mm
-                tr
-                  td
-                    tt mm
-                  td
-                    | a millimetre
-                tr
-                  td
-                    tt cm
-                  td
-                    | a centimetre
-                tr
-                  td
-                    tt in
-                  td
-                    | an inch
-                tr
-                  td
-                    tt ex
-                  td
-                    | the height of an
-                    b x
-                    | in the current font
-                tr
-                  td
-                    tt em
-                  td
-                    | the width of an
-                    b m
-                    | in the current font
-                tr
-                  td
-                    tt \columnsep
-                  td
-                    | distance between columns
-                tr
-                  td
-                    tt \columnwidth
-                  td
-                    | width of the column
-                tr
-                  td
-                    tt \linewidth
-                  td
-                    | width of the line in the current environment
-                tr
-                  td
-                    tt \paperwidth
-                  td
-                    | width of the page
-                tr
-                  td
-                    tt \paperheight
-                  td
-                    | height of the page
-                tr
-                  td
-                    tt \textwidth
-                  td
-                    | width of the text
-                tr
-                  td
-                    tt \textheight
-                  td
-                    | height of the text
-                tr
-                  td
-                    tt \unitleght
-                  td
-                    | units of length in the
-                    i picture
-                    | environment.
-            h1
-              span#Further_reading.mw-headline Further reading
-            p
-              | For more information see the full package documentation
-            p
-              a.external.text(rel='nofollow', href='http://tug.ctan.org/tex-archive/macros/latex/contrib/floatrow/floatrow.pdf')
-                b floatrow
-                | package for advanced caption managing of floating elements (tables and figures)
-            p
-              a.external.text(rel='nofollow', href='ftp://ctan.tug.org/tex-archive/macros/latex/contrib/sidecap/sidecap.pdf')
-                b sidecap
-                | package documentation, for side-figure captions
-          .printfooter
-            | Retrieved from "
-            a(href='http://www.sharelatex.com/learn-scripts/index.php?title=Positioning_images_and_tables&oldid=2521')
-              | http://www.sharelatex.com/learn-scripts/index.php?title=Positioning_images_and_tables&oldid=2521
-            | "
-
-  include ../general/small-footer

+ 0 - 16
services/web/app/views/external/planned_maintenance.jade

@@ -1,16 +0,0 @@
-extends ../layout
-
-block content
-	.content
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					.page-header
-						h1 Planned Maintenance
-					p There is currently no planned maintenance
-
-
-
-
-
-

+ 0 - 26
services/web/app/views/external/privacy.jade

@@ -1,26 +0,0 @@
-extends ../layout
-
-block content
-	.content
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					.page-header
-						h1 Privacy Policy
-					h3 Information gathering
-					p When you register for ShareLaTeX we collect information such as your name and email address. ShareLaTeX uses this information to be able to provide our service, for identification and authorization of users, and to be able to contact you.
-					p The information we collect is not shared with other organisations except as detailed below for the provisioning and improvement of our service. The data we collect will never be sold to third parties for commercial purposes.
-
-					h3 Cookies
-					p ShareLaTeX uses a cookie, which is a small amount of data stored by your web browser on your computer. The cookie stores your current session and allows you to stay logged in. Cookies are required to use the ShareLaTeX service.
-
-					h3 Data storage
-					p ShareLaTeX uses third parties to host our services and store your data. You retain all rights to the data you upload to ShareLaTeX.
-
-					h3 Credit cards and billing details
-					p We use a third party vendor,
-						a(href="recurly.com")  Recurly
-						| , to store and process credit card transactions. Your email address, credit card details and billing address are passed on to Recurly and are not stored with ShareLaTeX.
-
-					h3 Third party tracking
-					p We use Google Analytics to track users' interactions with ShareLaTeX. This data is used for the improvement of our service.

+ 0 - 98
services/web/app/views/external/security.jade

@@ -1,98 +0,0 @@
-extends ../layout
-
-block content
-	.content
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					.page-header
-						h1 Security
-					p
-						| Keeping your data safe is one of our top priorities.
-						| We work hard to make sure that ShareLaTeX is as secure as we can make it,
-						| and your input and feedback on our security is always appreciated.
-					h3 Responsible disclosure
-					p
-						| Please send reports of any urgent or sensitive security issues to
-						a(href="mailto:team@sharelatex.com")  team@sharelatex.com.
-						|  Use our
-						a(href="/sharelatex-security.pub")  public key
-						|  to encrypt your message and please provide us with a secure way to
-						| contact you.
-
-					p
-						| Note that the URLs at <strong>/learn</strong>, <strong>/help</strong>
-						| and <strong>ctan.sharelatex.com</strong> are not under our direct control,
-						| and vulnerabilities should be reported to either MediaWiki or TenderApp
-						| respectively.
-
-					p
-						| We are very grateful for all the responsibly reported security vulnerabilities,
-						| however listing on the hall of fame is reserved for people who report
-						| vulnerabilities that were previously unknown and we regard as serious.
-
-					h3 Acknowledgements
-					p
-						| We'd like to thank the following people who have responsibly disclosed
-						| vulnerabilities to us and helped improved the security of ShareLaTeX:
-
-					ul.unstyled
-						li
-							a(href="https://twitter.com/Abdulahhusam", rel="nofollow") Abdullah Hussam Gazi
-						li
-							a(href="http://adamziaja.com", rel="nofollow") Adam Ziaja
-						li
-							a(href="http://alihassanpenetrationtester.blogspot.com/", rel="nofollow") Ali Hasan Ghauri
-						li
-							a(href="https://twitter.com/EhArvindSingh") Arvind Singh Shekhawat
-						li
-							a(href="https://www.facebook.com/Dakshxss", rel="nofollow") Daksh Patel
-						li
-							a(href="https://twitter.com/dibsyhex", rel="nofollow") Dibyendu Sikdar
-						li
-							a(href="https://www.facebook.com/c.hari1997", rel="nofollow") Hari Krishnan
-						li
-							a(href="https://www.facebook.com/jaymark.pestano", rel="nofollow") Jaymark Pestaño
-						li
-							a(href="https://twitter.com/korapsyon", rel="nofollow") Jerold Camacho
-						li
-							a(href="https://twitter.com/kamilsevi", rel="nofollow") Kamil Sevi
-						li
-							a(href="https://twitter.com/kingkaustubhp", rel="nofollow") Kaustubh Padwad
-						li 'KoF2002' &amp; 'Sr33h4r!(XSS no0B)'
-						li
-							a(href="http://twitter.com/umenmactech", rel="nofollow") Manish Bhattacharya
-						li
-							a(href="http://twitter.com/Manjesh24", rel="nofollow") Manjesh S
-						li
-							a(href="https://www.facebook.com/Shahmeer.1994", rel="nofollow") Muhammad Shahmeer
-						li
-							a(href="http://nbsriharsha.blogspot.in", rel="nofollow") N B Sri Harsha
-						li
-							a(href="http://www.linkedin.com/in/osandamalith", rel="nofollow") Osanda Malith Jayathissa
-						li
-							a(href="https://twitter.com/prasadk14", rel="nofollow") Prasad Kancharla
-						li
-							a(href="https://www.facebook.com/c0m4dr3404", rel="nofollow") Praveen Nair (Kerala Cyber Squad - India)
-						li
-							a(href="https://twitter.com/iAmPr3m", rel="nofollow") Prem Kumar
-						li
-							a(href="https://www.facebook.com/HardNocksHittnHard", rel="nofollow") Sherin Panikar (Kerala Cyber Squad - India)
-						li
-							a(href="https://twitter.com/Simon90_Italy", rel="nofollow") Simone Memoli
-						li
-							a(href="https://twitter.com/pranavvenkats", rel="nofollow") S.Venkatesh
-						li
-							a(href="https://twitter.com/tareksiddiki", rel="nofollow") Tarek Siddiki
-						li
-							a(href="https://twitter.com/venugopalt", rel="nofollow") Venugopal Thotakura
-						li
-							a(href="http://softproweb.blogspot.com/", rel="nofollow") Waqeeh Ul Hasan
-						li
-							a(href="https://twitter.com/zerodayguys", rel="nofollow") Zeroday Guys (Rakesh Singh &amp; V.Harish Kumar)
-						li
-							a(href="https://twitter.com/_prashantnegi", rel="nofollow") Prashant Negi
-							span  and
-							a(href="https://twitter.com/AjaySinghNegi", rel="nofollow")  Singh Negi
-						li
-							a(href="https://twitter.com/mohitnitrr") Monendra Sahu

+ 0 - 62
services/web/app/views/external/style_guide.jade

@@ -1,62 +0,0 @@
-extends ../layout
-
-block content
-	.content
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					h1 ShareLaTeX CSS Style Guide
-
-					h2 Headings
-
-					p Here are our heading styles:
-
-					h1 Heading level 1
-					p Lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum
-
-					h2 Heading level 2
-					p Lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum
-
-					h3 Heading level 3
-					p Lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum
-
-					h4 Heading level 4
-					p Lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum
-
-					h2 Buttons
-
-					p Our primary button is red:
-					p
-						a.btn.btn-primary(href="#") Primary Button
-					p Our secondary button is blue:
-					p
-						a.btn.btn-info(href="#") Secondary Button
-					p All button styles
-					p
-						.btn-toolbar
-							a.btn.btn-primary(href="#") Primary
-							a.btn.btn-info(href="#") Info (secondary)
-							a.btn.btn-default(href="#") Default
-							a.btn.btn-success(href="#") Success
-							a.btn.btn-warning(href="#") Warning
-							a.btn.btn-danger(href="#") Danger
-
-					h2 Hyperlinks
-
-					p Hyperlinks are highlighted 
-						a(href="#") as shown
-						|.
-
-					h2 Alerts
-
-					.alert.alert-danger An <code>.alert-danger</code> alert
-					.alert.alert-success An <code>.alert-success</code> alert
-					.alert.alert-info An <code>.alert-info</code> alert
-					.alert.alert-warning An <code>.alert-warning</code> alert
-
-	.content.content-alt
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					.card
-						p Cards look best on a <code>.content.content-alt</code> background

+ 0 - 34
services/web/app/views/external/tos.jade

@@ -1,34 +0,0 @@
-extends ../layout
-
-block content
-	.content
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					.page-header
-						h1 Terms of Service
-					p
-						| Thank you for taking the time to read our terms of service.
-						| We've tried to keep it simple, but you must agree to these terms in order to use ShareLaTeX.
-					h3 You must provide a valid email address
-					p
-						| We expect you to register with a valid email address.
-						| This will be our only way to communicate with you.
-						| Changes to these terms of service and any other notifications about updates
-						| to the service will be sent to the email address you provide.
-						| We will not be held responsible for any information not being received.
-					h3 You own your content, not us
-					p You retain all ownership, copyright and intellectual property rights to any content uploaded to ShareLaTeX. Your content will only be shared with other users of your choosing and we will never share your content with third parties without your consent. The staff of ShareLaTeX have access to your content, but we make an effort to only access it when absolutely necessary.
-					h3 You're not allowed to abuse the service
-					p ShareLaTeX is provided assuming users will act in good faith. However, we retain the right to remove any account or content that we feel is abusing the service. In the rare event of this happening, it will most likely be for one of the following reasons: using the service for illegal reasons; uploading illegal, unauthorised or objectionable content; consuming an excessive amount of computing resources.
-					h3 We're not infallible
-					p ShareLaTeX is provided on an 'as is' basis, without future promise of availability. ShareLaTeX is not liable for any damages caused by the loss or inability to access your data.
-					p [Founder's note: We try hard to provide a service which is reliable, secure and regularly backed up. While the above paragraph sounds grim, we need it there to cover ourselves incase something does go wrong. We certainly aren't planning to need to fall back on it. This assurance aside, it's always good practice to back up your own data.]
-					h3 Cancellation and refunds policy
-					p ShareLaTeX's services are paid for monthly or yearly in advance and are non-refundable. There will be no refunds or credits for partial months/years of service or downgrades. Subscription downgrades and cancellations will come into effect when your next payment is due. Upgrades to your plan will be billed immediately at the new rate for the remainder of the billing period. You may cancel your account at any time from your account subscription settings.
-					h3 Paypal Reference Transactions
-					p PayPal Reference Transactions are used to allow ShareLaTeX to make subsequent transactions on a monthly or annual basis until you cancel your account.
-					h3 Pricing changes
-					p We will notify you of any changes to our pricing plans that affect you at least 30 days before your next payment is due. We will notify you via the email address you provided when subscribing for a paid plan.
-					h3 You must be human
-					p To protect ourselves from accidental or malicious abuse, automated scripts and programs are not permitted to register an account or access the service.

+ 1 - 1
services/web/app/views/layout.jade

@@ -32,7 +32,7 @@ html(itemscope, itemtype='http://schema.org/Product')
 		
 		block scripts
 		script(src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js")
-		script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js")
+		script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.14/angular.min.js")
 		
 	body
 		- if(typeof(suppressNavbar) == "undefined")

+ 2 - 2
services/web/app/views/layout/footer.jade

@@ -5,7 +5,7 @@ footer
 				each item in nav.left_footer
 					li
 						if item.url
-							a(href=item.url) !{item.text}
+							a(href=item.url, class=item.class) !{item.text}
 						else
 							| !{item.text}
 							
@@ -13,7 +13,7 @@ footer
 				each item in nav.right_footer
 					li
 						if item.url
-							a(href=item.url) !{item.text}
+							a(href=item.url, class=item.class) !{item.text}
 						else
 							| !{item.text}
 

+ 2 - 2
services/web/app/views/layout/navbar.jade

@@ -21,13 +21,13 @@ nav.navbar.navbar-default
 										else
 											li
 												if child.url
-													a(href=child.url) !{child.text}
+													a(href=child.url, class=child.class) !{child.text}
 												else
 													| !{child.text}
 						else
 							li(class=item.class)
 								if item.url
-									a(href=item.url) !{item.text}
+									a(href=item.url, class=item.class) !{item.text}
 								else
 									| !{item.text}
 

+ 5 - 3
services/web/app/views/project/editor.jade

@@ -77,11 +77,13 @@ block content
 
 	script(src='/socket.io/socket.io.js')
 
+	//- We need to do .replace(/\//g, '\\/') do that '</script>' -> '<\/script>'
+	//- and doesn't prematurely end the script tag.
 	script(type='text/javascript').
 		window.project_id = "!{project_id}"
-		window.sharelatex = !{sharelatexObject};
-		window.userSettings = !{userSettingsObject};
-		window.user = !{userObject};
+		window.sharelatex = !{JSON.stringify(sharelatex).replace(/\//g, '\\/')};
+		window.userSettings = !{JSON.stringify(userSettings).replace(/\//g, '\\/')};
+		window.user = !{JSON.stringify(user).replace(/\//g, '\\/')};
 		window.csrfToken = "!{csrfToken}";
 		window.requirejs = {
 			"paths" : {

+ 1 - 1
services/web/app/views/project/editor/editor.jade

@@ -11,7 +11,7 @@ div.full-size(
 			| &nbsp;&nbsp;Loading...
 
 		#editor(
-			ace-editor,
+			ace-editor="editor",
 			ng-show="!!editor.sharejs_doc && !editor.opening"
 			theme="settings.theme",
 			keybindings="settings.mode",

+ 64 - 70
services/web/app/views/project/editor/left-menu.jade

@@ -2,9 +2,17 @@ aside#left-menu.full-size(
 	ng-class="{ 'shown': ui.leftMenuShown }"
 	ng-cloak
 )
+	ul.list-unstyled.nav
+		li
+			a(
+				href="/project"
+			)
+				i.fa.fa-list-alt.fa-fw 
+				| &nbsp;&nbsp;View All Projects
+
 	h4 Download
 
-	ul.unformatted-list.nav.nav-downloads
+	ul.list-unstyled.nav.nav-downloads
 		li
 			a(
 				ng-href="/project/{{project_id}}/download/zip"
@@ -35,87 +43,73 @@ aside#left-menu.full-size(
 	form.settings(ng-controller="SettingsController")
 		.containter-fluid
 			.form-controls
-				.row
-					label.col-md-6(for="compiler") Compiler
-					.col-md-6
-						select.form-control(
-							name="compiler"
-							ng-model="project.compiler"
-						)
-							option(value='pdflatex') pdfLaTeX
-							option(value='latex') LaTeX
-							option(value='xelatex') XeLaTeX
-							option(value='lualatex') LuaLaTeX
+				label(for="compiler") Compiler
+				select.form-control(
+					name="compiler"
+					ng-model="project.compiler"
+				)
+					option(value='pdflatex') pdfLaTeX
+					option(value='latex') LaTeX
+					option(value='xelatex') XeLaTeX
+					option(value='lualatex') LuaLaTeX
 
 			.form-controls
-				.row
-					label.col-md-6(for="spellCheckLanguage") Spell Check
-					.col-md-6
-						select.form-control(
-							name="spellCheckLanguage"
-							ng-model="project.spellCheckLanguage"
-						)
-							option(value="") Off
-							optgroup(label="Language")
-								for language in languages
-									option(
-										value=language.code
-									)= language.name
+				label(for="spellCheckLanguage") Spell Check
+				select.form-control(
+					name="spellCheckLanguage"
+					ng-model="project.spellCheckLanguage"
+				)
+					option(value="") Off
+					optgroup(label="Language")
+						for language in languages
+							option(
+								value=language.code
+							)= language.name
 
 			.form-controls
-				.row
-					label.col-md-6(for="autoComplete") Auto-Complete
-					.col-md-6
-						select.form-control(
-							name="autoComplete"
-							ng-model="settings.autoComplete"
-							ng-options="o.v as o.n for o in [{ n: 'On', v: true }, { n: 'Off', v: false }]"
-						)
+				label(for="autoComplete") Auto-Complete
+				select.form-control(
+					name="autoComplete"
+					ng-model="settings.autoComplete"
+					ng-options="o.v as o.n for o in [{ n: 'On', v: true }, { n: 'Off', v: false }]"
+				)
 
 			.form-controls
-				.row
-					label.col-md-6(for="theme") Theme
-					.col-md-6
-						select.form-control(
-							name="theme"
-							ng-model="settings.theme"
-						)
-							each theme in themes
-								option(value=theme) #{theme}
+				label(for="theme") Theme
+				select.form-control(
+					name="theme"
+					ng-model="settings.theme"
+				)
+					each theme in themes
+						option(value=theme) #{theme}
 
 			.form-controls
-				.row
-					label.col-md-6(for="mode") Keybindings
-					.col-md-6
-						select.form-control(
-							name="mode"
-							ng-model="settings.mode"
-						)
-							option(value='default') None
-							option(value='vim') Vim
-							option(value='emacs') Emacs
+				label(for="mode") Keybindings
+				select.form-control(
+					name="mode"
+					ng-model="settings.mode"
+				)
+					option(value='default') None
+					option(value='vim') Vim
+					option(value='emacs') Emacs
 
 			.form-controls
-				.row
-					label.col-md-6(for="fontSize") Font Size
-					.col-md-6
-						select.form-control(
-							name="fontSize"
-							ng-model="settings.fontSize"
-						)
-							each size in ['10','11','12','13','14','16','20','24']
-								option(value=size) #{size}px
+				label(for="fontSize") Font Size
+				select.form-control(
+					name="fontSize"
+					ng-model="settings.fontSize"
+				)
+					each size in ['10','11','12','13','14','16','20','24']
+						option(value=size) #{size}px
 
 			.form-controls
-				.row
-					label.col-md-6(for="pdfViewer") PDF Viewer
-					.col-md-6
-						select.form-control(
-							name="pdfViewer"
-							ng-model="settings.pdfViewer"
-						)
-							option(value="pdfjs") Built-In
-							option(value="native") Native
+				label(for="pdfViewer") PDF Viewer
+				select.form-control(
+					name="pdfViewer"
+					ng-model="settings.pdfViewer"
+				)
+					option(value="pdfjs") Built-In
+					option(value="native") Native
 
 #left-menu-mask(
 	ng-show="ui.leftMenuShown",

+ 2 - 2
services/web/app/views/project/editor/track-changes.jade

@@ -82,8 +82,8 @@ div#trackChanges(ng-show="ui.view == 'track-changes'")
 
 	.diff.full-size(ng-controller="TrackChangesDiffController")
 		.diff-editor.hide-ace-cursor(
-			ace-editor,
-			ng-show="!!trackChanges.diff && !trackChanges.diff.loading && !trackChanges.diff.deleted",
+			ace-editor="track-changes",
+			ng-show="!!trackChanges.diff && !trackChanges.diff.loading && !trackChanges.diff.deleted && !trackChanges.diff.error",
 			theme="settings.theme",
 			font-size="settings.fontSize",
 			text="trackChanges.diff.text",

+ 11 - 9
services/web/app/views/project/list.jade

@@ -1,10 +1,12 @@
 extends ../layout
 
 block content
+	//- We need to do .replace(/\//g, '\\/') do that '</script>' -> '<\/script>'
+	//- and doesn't prematurely end the script tag.
 	script(type="text/javascript").
 		window.data = {
-			projects: !{JSON.stringify(projects)},
-			tags: !{JSON.stringify(tags)}
+			projects: !{JSON.stringify(projects).replace(/\//g, '\\/')},
+			tags: !{JSON.stringify(tags).replace(/\//g, '\\/')}
 		};
 		window.algolia = {
 			institutions: {
@@ -233,6 +235,7 @@ block content
 									ul.list-unstyled.project-list.structured-list(
 										select-all-list,
 										ng-if="projects.length > 0",
+										max-height="windowHeight - 290",
 										ng-cloak
 									)
 										li.container-fluid
@@ -242,11 +245,11 @@ block content
 														select-all,
 														type="checkbox"
 													)
-													| TITLE
+													span.header Title
 												.col-md-2
-													| OWNER
+													span.header Owner
 												.col-md-4
-													| LAST MODIFIED
+													span.header Last Modified
 										li.project_entry.container-fluid(
 											ng-repeat="project in visibleProjects | orderBy:'lastUpdated':true",
 											ng-controller="ProjectListItemController"
@@ -258,15 +261,14 @@ block content
 														type="checkbox",
 														ng-model="project.selected"
 													)
-													span.title
-														a.projectName(href="/project/{{project.id}}") {{project.name}}
 													span
-														span.label.label-default(ng-repeat='tag in project.tags')
+														a.projectName(href="/project/{{project.id}}") {{project.name}}
+														span.label.label-default.tag-label(ng-repeat='tag in project.tags')
 															| {{tag.name}}
 												.col-md-2
 													span.owner {{ownerName()}}
 												.col-md-4
-													span.last-modified.isoDate {{project.lastUpdated | formatDate}}
+													span.last-modified {{project.lastUpdated | formatDate}}
 										li(
 											ng-if="visibleProjects.length == 0",
 											ng-cloak

+ 3 - 3
services/web/app/views/subscriptions/group_admin.jade

@@ -28,11 +28,11 @@ block content
 												select-all,
 												type="checkbox"
 											)
-											span.email EMAIL
+											span.header Email
 										.col-md-5
-											span.name NAME
+											span.header Name
 										.col-md-2
-											span.registered REGISTERED
+											span.header Registered
 								li.container-fluid(
 									ng-repeat="user in users | orderBy:'email':true",
 									ng-controller="GroupMemberListItemController"

+ 2 - 1
services/web/app/views/user/login.jade

@@ -18,7 +18,8 @@ block content
 									name='email',
 									required,
 									placeholder='email@example.com',
-									ng-model="email"
+									ng-model="email",
+									ng-model-options="{ updateOn: 'blur' }"
 								)
 								span.small.text-primary(ng-show="loginForm.email.$invalid && loginForm.email.$dirty")
 									| Must be an email address

+ 2 - 1
services/web/app/views/user/register.jade

@@ -30,7 +30,8 @@ block content
 									placeholder="email@example.com"
 									required,
 									ng-model="email",
-									ng-init="email = #{JSON.stringify(new_email)}"
+									ng-init="email = #{JSON.stringify(new_email)}",
+									ng-model-options="{ updateOn: 'blur' }"
 								)
 								span.small.text-primary(ng-show="registerForm.email.$invalid && registerForm.email.$dirty")
 									| Must be an email address

+ 2 - 1
services/web/app/views/user/settings.jade

@@ -27,7 +27,8 @@ block content
 													placeholder="email@example.com"
 													required,
 													ng-model="email",
-													ng-init="email = #{JSON.stringify(user.email)}"
+													ng-init="email = #{JSON.stringify(user.email)}",
+													ng-model-options="{ updateOn: 'blur' }"
 												)
 												span.small.text-primary(ng-show="settingsForm.email.$invalid && settingsForm.email.$dirty")
 													| Must be an email address

+ 11 - 0
services/web/public/coffee/directives/maxHeight.coffee

@@ -0,0 +1,11 @@
+define [
+	"base"
+], (App) ->
+	App.directive "maxHeight", () ->
+		return {
+			restrict: "A"
+			link: (scope, element, attrs) ->
+				scope.$watch attrs.maxHeight, (value) ->
+					if value?
+						element.css("max-height": value)
+		}

+ 5 - 6
services/web/public/coffee/ide/connection/ConnectionManager.coffee

@@ -11,6 +11,9 @@ define [], () ->
 			@$scope.tryReconnectNow = () =>
 				@tryReconnect()
 
+			@$scope.$on "editor:change", () =>
+				@lastUpdated = new Date()
+
 			@ide.socket = io.connect null,
 				reconnect: false
 				"force new connection": true
@@ -22,7 +25,7 @@ define [], () ->
 				@$scope.$apply () =>
 					@$scope.connection.reconnecting = false
 					if @$scope.state.loading
-						@$scope.state.load_progress = 80
+						@$scope.state.load_progress = 70
 
 				setTimeout(() =>
 					@joinProject()
@@ -70,10 +73,8 @@ define [], () ->
 			@ide.socket.disconnect()
 
 		startAutoReconnectCountdown: () ->
-			lastUpdated = @ide.editorManager.lastUpdated()
-
 			twoMinutes = 2 * 60 * 1000
-			if lastUpdated? and new Date() - lastUpdated > twoMinutes
+			if @lastUpdated? and new Date() - @lastUpdated > twoMinutes
 				# between 1 minute and 3 minutes
 				countdown = 60 + Math.floor(Math.random() * 120)
 			else
@@ -92,7 +93,6 @@ define [], () ->
 			clearTimeout @timeoutId if @timeoutId?
 					
 		decreaseCountdown: () ->
-			console.log "Decreasing countdown"
 			return if !@$scope.connection.reconnection_countdown?
 			@$scope.$apply () =>
 				@$scope.connection.reconnection_countdown--
@@ -104,7 +104,6 @@ define [], () ->
 				@timeoutId = setTimeout (=> @decreaseCountdown()), 1000
 
 		tryReconnect: () ->
-			console.log "Trying reconnect"
 			@cancelReconnect()
 			@$scope.connection.reconnecting = true
 			delete @$scope.connection.reconnection_countdown

+ 1 - 0
services/web/public/coffee/ide/directives/layout.coffee

@@ -96,5 +96,6 @@ define [
 							, 0
 
 					resetOpenStates()
+					onInternalResize()
 		}
 	]

+ 1 - 1
services/web/public/coffee/ide/editor/Document.coffee

@@ -82,7 +82,7 @@ define [
 
 		_unBindFromSocketEvents: () ->
 			@ide.socket.removeListener "otUpdateApplied", @_onUpdateAppliedHandler
-			@ide.socket.removeListener "otUpdateError", @_onUpdateErrorHandler
+			@ide.socket.removeListener "otUpdateError", @_onErrorHandler
 			@ide.socket.removeListener "disconnect", @_onDisconnectHandler
 
 		leaveAndCleanUp: () ->

+ 1 - 6
services/web/public/coffee/ide/editor/EditorManager.coffee

@@ -7,10 +7,8 @@ define [
 		constructor: (@ide, @$scope) ->
 			@$scope.editor = {
 				sharejs_doc: null
-				last_updated: null
 				open_doc_id: null
 				opening: true
-				cursorPosition: {}
 				gotoLine: null
 			}
 
@@ -38,7 +36,7 @@ define [
 
 			done = () =>
 				if options.gotoLine?
-					@$scope.editor.gotoLine = options.gotoLine
+					@$scope.$broadcast "editor:gotoLine", options.gotoLine
 			
 			if doc.id == @$scope.editor.open_doc_id and !options.forceReopen
 				@$scope.$apply () =>
@@ -93,9 +91,6 @@ define [
 		_unbindFromDocumentEvents: (document) ->
 			document.off()
 
-		lastUpdated: () ->
-			@$scope.editor.last_updated
-
 		getCurrentDocValue: () ->
 			@$scope.editor.sharejs_doc?.getSnapshot()
 

+ 16 - 16
services/web/public/coffee/ide/editor/directives/aceEditor.coffee

@@ -6,12 +6,7 @@ define [
 	"ide/editor/directives/aceEditor/spell-check/SpellCheckManager"
 	"ide/editor/directives/aceEditor/highlights/HighlightsManager"
 	"ide/editor/directives/aceEditor/cursor-position/CursorPositionManager"
-	"ace/keyboard/vim"
-	"ace/keyboard/emacs"
-	"ace/mode/latex"
-	"ace/edit_session"
 ], (App, Ace, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager) ->
-	LatexMode = require("ace/mode/latex").Mode
 	EditSession = require('ace/edit_session').EditSession
 
 	App.directive "aceEditor", ["$timeout", ($timeout) ->
@@ -23,9 +18,7 @@ define [
 				fontSize: "="
 				autoComplete: "="
 				sharejsDoc: "="
-				lastUpdated: "="
 				spellCheckLanguage: "="
-				cursorPosition: "="
 				highlights: "="
 				text: "="
 				readOnly: "="
@@ -46,6 +39,8 @@ define [
 				window.editors ||= []
 				window.editors.push editor
 
+				scope.name = attrs.aceEditor
+
 				autoCompleteManager   = new AutoCompleteManager(scope, editor, element)
 				spellCheckManager     = new SpellCheckManager(scope, editor, element)
 				undoManager           = new UndoManager(scope, editor, element)
@@ -74,10 +69,10 @@ define [
 					editor.setShowPrintMargin(value)
 
 				scope.$watch "keybindings", (value) ->
-					Vim = require("ace/keyboard/vim").handler
-					Emacs = require("ace/keyboard/emacs").handler
-					keybindings = vim: Vim, emacs: Emacs
-					editor.setKeyboardHandler(keybindings[value])
+					if value in ["vim", "emacs"]
+						editor.setKeyboardHandler("ace/keyboard/#{value}")
+					else
+						editor.setKeyboardHandler(null)
 
 				scope.$watch "fontSize", (value) ->
 					element.find(".ace_editor, .ace_content").css({
@@ -96,7 +91,7 @@ define [
 						editor.setValue(text, -1)
 						session = editor.getSession()
 						session.setUseWrapMode(true)
-						session.setMode(new LatexMode())
+						session.setMode("ace/mode/latex")
 
 				scope.$watch "annotations", (annotations) ->
 					if annotations?
@@ -109,9 +104,12 @@ define [
 				resetSession = () ->
 					session = editor.getSession()
 					session.setUseWrapMode(true)
-					session.setMode(new LatexMode())
+					session.setMode("ace/mode/latex")
 					session.setAnnotations scope.annotations
 
+				emitChange = () -> 
+					scope.$emit "#{scope.name}:change"
+
 				attachToAce = (sharejs_doc) ->
 					lines = sharejs_doc.getSnapshot().split("\n") 
 					editor.setSession(new EditSession(lines))
@@ -119,9 +117,7 @@ define [
 					session = editor.getSession()
 
 					doc = session.getDocument()
-					doc.on "change", () ->
-						scope.$apply () ->
-							scope.lastUpdated = new Date()
+					doc.on "change", emitChange
 
 					sharejs_doc.on "remoteop.recordForUndo", () =>
 						undoManager.nextUpdateIsRemote = true
@@ -134,6 +130,10 @@ define [
 					sharejs_doc.detachFromAce()
 					sharejs_doc.off "remoteop.recordForUndo"
 
+					session = editor.getSession()
+					doc = session.getDocument()
+					doc.off "change", emitChange
+
 			template: """
 				<div class="ace-editor-wrapper">
 					<div

+ 59 - 42
services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee

@@ -1,23 +1,10 @@
 define [
 	"ide/editor/directives/aceEditor/auto-complete/SuggestionManager"
 	"ide/editor/directives/aceEditor/auto-complete/Snippets"
-	"ace/autocomplete/util"
-	"ace/autocomplete"
-	"ace/range"
-	"ace/ext/language_tools"
-], (SuggestionManager, Snippets, Util, AutoComplete) ->
-	Range = require("ace/range").Range
-	Autocomplete = AutoComplete.Autocomplete
-
-	Util.retrievePrecedingIdentifier = (text, pos, regex) ->
-		currentLineOffset = 0
-		for i in [(pos-1)..0]
-			if text[i] == "\n"
-				currentLineOffset = i + 1
-				break
-		currentLine = text.slice(currentLineOffset, pos)
-		fragment = getLastCommandFragment(currentLine) or ""
-		return fragment
+	"ace/ace"
+	"ace/ext-language_tools"
+], (SuggestionManager, Snippets, Ace) ->
+	Range = Ace.require("ace/range").Range
 
 	getLastCommandFragment = (lineUpToCursor) ->
 		if m = lineUpToCursor.match(/(\\[^\\ ]+)$/)
@@ -29,22 +16,7 @@ define [
 		constructor: (@$scope, @editor) ->
 			@suggestionManager = new SuggestionManager()
 
-			if !Autocomplete::_insertMatch?
-				# Only override this once since it's global but we may create multiple
-				# autocomplete handlers
-				Autocomplete::_insertMatch = Autocomplete::insertMatch
-				Autocomplete::insertMatch = (data) ->
-					pos = editor.getCursorPosition()
-					range = new Range(pos.row, pos.column, pos.row, pos.column + 1)
-					nextChar = editor.session.getTextRange(range)
-
-
-					# If we are in \begin{it|}, then we need to remove the trailing }
-					# since it will be adding in with the autocomplete of \begin{item}...
-					if this.completions.filterText.match(/^\\begin\{/) and nextChar == "}"
-						editor.session.remove(range)
-					
-					Autocomplete::_insertMatch.call this, data
+			@monkeyPatchAutocomplete()	
 
 			@$scope.$watch "autoComplete", (autocomplete) =>
 				if autocomplete
@@ -52,14 +24,18 @@ define [
 				else
 					@disable()
 
+			onChange = (change) =>
+				@onChange(change)
 
 			@editor.on "changeSession", (e) =>
-				@bindToSession(e.session)
+				e.oldSession.off "change", onChange
+				e.session.on "change", onChange
 
 		enable: () ->
 			@editor.setOptions({
 				enableBasicAutocompletion: true,
-				enableSnippets: true
+				enableSnippets: true,
+				enableLiveAutocompletion: false
 			})
 			
 			SnippetCompleter =
@@ -73,9 +49,6 @@ define [
 				enableSnippets: false
 			})
 
-		bindToSession: (@aceSession) ->
-			@aceSession.on "change", (change) => @onChange(change)
-
 		onChange: (change) ->
 			cursorPosition = @editor.getCursorPosition()
 			end = change.data.range.end
@@ -84,10 +57,54 @@ define [
 			if end.row == cursorPosition.row and end.column == cursorPosition.column + 1
 				if change.data.action == "insertText"
 					range = new Range(end.row, 0, end.row, end.column)
-					lineUpToCursor = @aceSession.getTextRange(range)
+					lineUpToCursor = @editor.getSession().getTextRange(range)
 					commandFragment = getLastCommandFragment(lineUpToCursor)
 
 					if commandFragment? and commandFragment.length > 2
-					 	setTimeout () =>
-					 		@editor.execCommand("startAutocomplete")
-					 	, 0
+						setTimeout () =>
+							@editor.execCommand("startAutocomplete")
+						, 0
+
+		monkeyPatchAutocomplete: () ->
+			Autocomplete = require("ace/autocomplete").Autocomplete
+			Util = require("ace/autocomplete/util")
+			editor = @editor
+
+			if !Autocomplete::_insertMatch?
+				# Only override this once since it's global but we may create multiple
+				# autocomplete handlers
+				Autocomplete::_insertMatch = Autocomplete::insertMatch
+				Autocomplete::insertMatch = (data) ->
+					pos = editor.getCursorPosition()
+					range = new Range(pos.row, pos.column, pos.row, pos.column + 1)
+					nextChar = editor.session.getTextRange(range)
+
+					# If we are in \begin{it|}, then we need to remove the trailing }
+					# since it will be adding in with the autocomplete of \begin{item}...
+					if this.completions.filterText.match(/^\\begin\{/) and nextChar == "}"
+						editor.session.remove(range)
+					
+					Autocomplete::_insertMatch.call this, data
+
+				# Overwrite this to set autoInsert = false
+				Autocomplete.startCommand = {
+					name: "startAutocomplete",
+					exec: (editor) ->
+						if (!editor.completer)
+							editor.completer = new Autocomplete()
+						editor.completer.autoInsert = false
+						editor.completer.autoSelect = true
+						editor.completer.showPopup(editor)
+						editor.completer.cancelContextMenu()
+					bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
+				}
+
+			Util.retrievePrecedingIdentifier = (text, pos, regex) ->
+				currentLineOffset = 0
+				for i in [(pos-1)..0]
+					if text[i] == "\n"
+						currentLineOffset = i + 1
+						break
+				currentLine = text.slice(currentLineOffset, pos)
+				fragment = getLastCommandFragment(currentLine) or ""
+				return fragment

+ 33 - 26
services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionManager.coffee

@@ -2,45 +2,52 @@ define [], () ->
 	class CursorPositionManager
 		constructor: (@$scope, @editor, @element) ->
 
+			onChangeCursor = (e) =>
+				@emitCursorUpdateEvent(e)
+
 			@editor.on "changeSession", (e) =>
-				e.session.on "changeScrollTop", (e) =>
-					@onScrollTopChange(e)
+				if e.oldSession?
+					@storeCursorPosition(e.oldSession)
+					@storeScrollTopPosition(e.oldSession)
+
+				@doc_id = @$scope.sharejsDoc?.doc_id
 
-				e.session.selection.on 'changeCursor', (e) =>
-					@onCursorChange(e)
+				e.oldSession?.selection.off 'changeCursor', onChangeCursor
+				e.session.selection.on 'changeCursor', onChangeCursor
 
-				@gotoStoredPosition()
+				setTimeout () =>
+					@gotoStoredPosition()
+				, 0
 
-			@editor.on "changeSelection", () =>
-				cursor = @editor.getCursorPosition()
-				@$scope.$apply () =>
-					if @$scope.cursorPosition?
-						@$scope.cursorPosition = cursor
+			$(window).on "unload", () =>
+				@storeCursorPosition(@editor.getSession())
+				@storeScrollTopPosition(@editor.getSession())
 
-			@$scope.$watch "gotoLine", (value) =>
+			@$scope.$on "#{@$scope.name}:gotoLine", (editor, value) =>
 				if value?
 					setTimeout () =>
 						@gotoLine(value)
-						@$scope.$apply () =>
-							@$scope.gotoLine = null
 					, 0
 
-		onScrollTopChange: (event) ->
-			if !@ignoreCursorPositionChanges and doc_id = @$scope.sharejsDoc?.doc_id
-				docPosition = $.localStorage("doc.position.#{doc_id}") || {}
-				docPosition.scrollTop = @editor.getSession().getScrollTop()
-				$.localStorage("doc.position.#{doc_id}", docPosition)
+		storeScrollTopPosition: (session) ->
+			if @doc_id?
+				docPosition = $.localStorage("doc.position.#{@doc_id}") || {}
+				docPosition.scrollTop = session.getScrollTop()
+				$.localStorage("doc.position.#{@doc_id}", docPosition)
+
+		storeCursorPosition: (session) ->
+			if @doc_id?
+				docPosition = $.localStorage("doc.position.#{@doc_id}") || {}
+				docPosition.cursorPosition = session.selection.getCursor()
+				$.localStorage("doc.position.#{@doc_id}", docPosition)
 			
-		onCursorChange: (event) ->
-			if !@ignoreCursorPositionChanges and doc_id = @$scope.sharejsDoc?.doc_id
-				docPosition = $.localStorage("doc.position.#{doc_id}") || {}
-				docPosition.cursorPosition = @editor.getCursorPosition()
-				$.localStorage("doc.position.#{doc_id}", docPosition)
+		emitCursorUpdateEvent: () ->
+			cursor = @editor.getCursorPosition()
+			@$scope.$emit "cursor:#{@$scope.name}:update", cursor
 
 		gotoStoredPosition: () ->
-			doc_id = @$scope.sharejsDoc?.doc_id
-			return if !doc_id?
-			pos = $.localStorage("doc.position.#{doc_id}") || {}
+			return if !@doc_id?
+			pos = $.localStorage("doc.position.#{@doc_id}") || {}
 			@ignoreCursorPositionChanges = true
 			@editor.moveCursorToPosition(pos.cursorPosition or {row: 0, column: 0})
 			@editor.getSession().setScrollTop(pos.scrollTop or 0)

+ 1 - 3
services/web/public/coffee/ide/editor/directives/aceEditor/highlights/HighlightsManager.coffee

@@ -1,5 +1,5 @@
 define [
-	"ace/range"
+	"ace/ace"
 ], () ->
 	Range = require("ace/range").Range
 
@@ -40,7 +40,6 @@ define [
 				do (annotation) =>
 					colorScheme = @_getColorScheme(annotation.hue)
 					if annotation.cursor?
-						console.log "DRAWING CURSOR", annotation
 						@labels.push {
 							text: annotation.label
 							range: new Range(
@@ -109,7 +108,6 @@ define [
 					@$scope.annotationLabel.show = true
 
 				$label = @element.find(".annotation-label")
-				console.log "pageX", coords.pageX, "label", $label.outerWidth(), "ace", $ace.width()
 
 				if coords.pageX + $label.outerWidth() < $ace.width()
 					left  = coords.pageX

+ 6 - 11
services/web/public/coffee/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.coffee

@@ -1,6 +1,6 @@
 define [
 	"ide/editor/directives/aceEditor/spell-check/HighlightedWordManager"
-	"ace/range"
+	"ace/ace"
 ], (HighlightedWordManager) ->
 	Range = require("ace/range").Range
 
@@ -13,12 +13,14 @@ define [
 				if language != oldLanguage and oldLanguage?
 					@runFullCheck()
 
+			onChange = (e) =>
+				@runCheckOnChange(e)
+
 			@editor.on "changeSession", (e) =>
 				@runFullCheck()
 
-				doc = e.session.getDocument()
-				doc.on "change", (e) =>
-					@runCheckOnChange(e)
+				e.oldSession?.getDocument().off "change", onChange
+				e.session.getDocument().on "change", onChange
 
 			@$scope.spellingMenu = {left: '0px', top: '0px'}
 
@@ -29,8 +31,6 @@ define [
 			$(document).on "click", (e) =>
 				@closeContextMenu(e)
 				return true
-			# $(document).on "contextmenu", (e) =>
-			# 	@closeContextMenu(e)
 
 			@$scope.replaceWord = (highlight, suggestion) =>
 				@replaceWord(highlight, suggestion)
@@ -44,7 +44,6 @@ define [
 				@runSpellCheck()
 
 		runCheckOnChange: (e) ->
-			console.log "Checking change", e.data
 			if @$scope.spellCheckLanguage and @$scope.spellCheckLanguage != ""
 				@highlightedWordManager.applyChange(e.data)
 				@markLinesAsUpdated(e.data)
@@ -59,8 +58,6 @@ define [
 			@$scope.$apply () =>
 				@$scope.spellingMenu.highlight = highlight
 
-			console.log "highlight", @$scope.highlight_under_mouse
-
 			if highlight
 				e.stopPropagation()
 				e.preventDefault()
@@ -72,8 +69,6 @@ define [
 					)
 				)
 
-				console.log "Height", @element.find(".context-menu").height()
-
 				@$scope.$apply () =>
 					@$scope.spellingMenu.open = true
 					@$scope.spellingMenu.left = e.clientX + 'px'

+ 1 - 3
services/web/public/coffee/ide/editor/directives/aceEditor/undo/UndoManager.coffee

@@ -1,7 +1,5 @@
 define [
-	"ace/range"
-	"ace/edit_session"
-	"ace/document"
+	"ace/ace"
 ], () ->
 	Range = require("ace/range").Range
 	EditSession = require("ace/edit_session").EditSession

+ 1 - 2
services/web/public/coffee/ide/file-tree/FileTreeManager.coffee

@@ -60,7 +60,6 @@ define [
 			@ide.socket.on "reciveEntityMove", (entity_id, folder_id) =>
 				entity = @findEntityById(entity_id)
 				folder = @findEntityById(folder_id)
-				console.log "Got recive ENTITY", entity_id, folder_id, entity, folder
 				@$scope.$apply () =>
 					@_moveEntityInScope(entity, folder)
 
@@ -189,7 +188,7 @@ define [
 
 		loadDeletedDocs: () ->
 			@$scope.deletedDocs = []
-			for doc in @$scope.project.deletedDocs
+			for doc in @$scope.project.deletedDocs or []
 				@$scope.deletedDocs.push {
 					name: doc.name
 					id:   doc._id

+ 3 - 5
services/web/public/coffee/ide/online-users/OnlineUsersManager.coffee

@@ -6,9 +6,8 @@ define [
 			@$scope.onlineUsers = {}
 			@$scope.onlineUserCursorHighlights = {}
 
-			@$scope.$watch "editor.cursorPosition", (position) =>
-				if position?
-					@sendCursorPositionUpdate()
+			@$scope.$on "cursor:editor:update", (event, position) =>
+				@sendCursorPositionUpdate(position)
 
 			@ide.socket.on "clientTracking.clientUpdated", (client) =>
 				if client.id != @ide.socket.socket.sessionid # Check it's not me!
@@ -36,10 +35,9 @@ define [
 				}
 
 		UPDATE_INTERVAL: 500
-		sendCursorPositionUpdate: () ->
+		sendCursorPositionUpdate: (position) ->
 			if !@cursorUpdateTimeout?
 				@cursorUpdateTimeout = setTimeout ()=>
-					position = @$scope.editor.cursorPosition
 					doc_id   = @$scope.editor.open_doc_id
 
 					@ide.socket.emit "clientTracking.updatePosition", {

+ 7 - 2
services/web/public/coffee/ide/pdf/controllers/PdfController.coffee

@@ -229,13 +229,18 @@ define [
 				$scope.showControls = false
 			else
 				$scope.showControls = true
+
 			setTimeout () ->
 				$scope.$digest()
 			, 0
 
-		$scope.syncToPdf = () ->
+		@cursorPosition = null
+		ide.$scope.$on "cursor:editor:update", (event, @cursorPosition) =>
+
+		$scope.syncToPdf = () =>
+			return if !@cursorPosition?
 			synctex
-				.syncToPdf($scope.editor.cursorPosition)
+				.syncToPdf(@cursorPosition)
 				.then (highlights) ->
 					$scope.pdf.highlights = highlights
 

+ 1 - 1
services/web/public/coffee/ide/pdf/directives/pdfJs.coffee

@@ -38,7 +38,7 @@ define [
 					annotationsLayerBuilder: AnnotationsLayerBuilder
 					highlightsLayerBuilder: HighlightsLayerBuilder
 					ondblclick: (e) -> onDoubleClick(e)
-					logLevel: PDFListView.Logger.DEBUG
+					# logLevel: PDFListView.Logger.DEBUG
 				pdfListView.listView.pageWidthOffset = 20
 				pdfListView.listView.pageHeightOffset = 20
 

+ 0 - 2
services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee

@@ -23,7 +23,6 @@ define [
 			$scope.canAddCollaborators = noOfMembers < allowedNoOfMembers or allowedNoOfMembers == INFINITE_COLLABORATORS
 
 		$scope.addMember = () ->
-			console.log "EMAIL", $scope.inputs.email
 			return if !$scope.inputs.email? or $scope.inputs.email == ""
 			$scope.state.error = null
 			$scope.state.inflight = true
@@ -32,7 +31,6 @@ define [
 				.then (user) ->
 					$scope.state.inflight = false
 					$scope.inputs.email = ""
-					console.log "GOT USER", user
 					$scope.project.members.push user
 				.catch () ->
 					$scope.state.inflight = false

+ 1 - 8
services/web/public/coffee/ide/track-changes/TrackChangesManager.coffee

@@ -51,7 +51,6 @@ define [
 			}
 
 		autoSelectRecentUpdates: () ->
-			console.log "AUTO SELECTING UPDATES", @$scope.trackChanges.updates.length
 			return if @$scope.trackChanges.updates.length == 0
 
 			@$scope.trackChanges.updates[0].selectedTo = true
@@ -84,8 +83,6 @@ define [
 			{updates, doc} = @$scope.trackChanges.selection
 			{fromV, toV}   = @_calculateRangeFromSelection()
 
-			console.log "Checking if diff has changed", doc?.id, fromV, toV, updates
-
 			return if !doc?
 
 			return if diff? and
@@ -93,8 +90,6 @@ define [
 				diff.fromV == fromV and
 				diff.toV   == toV
 
-			console.log "Loading diff", fromV, toV, doc?.id
-
 			@$scope.trackChanges.diff = diff = {
 				fromV:   fromV
 				toV:     toV
@@ -120,7 +115,6 @@ define [
 						diff.error = true
 			else
 				diff.deleted = true
-				console.log "DOC IS DELETED - NO DIFF FOR YOU!"
 
 		restoreDeletedDoc: (doc) ->
 			@ide.$http.post "/project/#{@$scope.project_id}/doc/#{doc.id}/restore", {
@@ -240,9 +234,8 @@ define [
 
 			selected_doc = @$scope.trackChanges.selection.doc
 			if selected_doc? and affected_docs[selected_doc.id]?
-				console.log "An affected doc is already open, bravo!"
+				# Selected doc is already open
 			else
-				console.log "selected doc is not open, selecting first one"
 				for doc_id, doc of affected_docs
 					selected_doc = doc
 					break

+ 4 - 7
services/web/public/coffee/ide/track-changes/directives/infiniteScroll.coffee

@@ -8,19 +8,16 @@ define [
 				element.css 'overflow-y': 'auto'
 
 				atEndOfListView = () ->
-					if element?
-						element.scrollTop() + element.height() >= innerElement.height() - 30
+					element.scrollTop() + element.height() >= innerElement.height() - 30
 
 				listShorterThanContainer = () ->
-					if element? 
-						element.innerHeight() > @$(".change-list").outerHeight()
+					element.innerHeight() > @$(".change-list").outerHeight()
 
 				loadUntilFull = () ->
 					if (listShorterThanContainer() or atEndOfListView()) and not scope.$eval(attrs.infiniteScrollDisabled)
 						promise = scope.$eval(attrs.infiniteScroll)
-						# promise.then () ->
-						# 	loadUntilFull()
-						
+						promise.then () ->
+							loadUntilFull()
 						# @collection.fetchNextBatch
 						# 	error: (error) =>
 						# 		@hideLoading()

+ 1 - 0
services/web/public/coffee/main.coffee

@@ -12,6 +12,7 @@ define [
 	"directives/fineUpload"
 	"directives/onEnter"
 	"directives/selectAll"
+	"directives/maxHeight"
 	"filters/formatDate"
 ], () ->
 	angular.bootstrap(document.body, ["SharelatexApp"])

+ 6 - 1
services/web/public/coffee/main/project-list.coffee

@@ -50,7 +50,7 @@ define [
 
 	]
 
-	App.controller "ProjectPageController", ($scope, $modal, $q, queuedHttp) ->
+	App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp) ->
 		$scope.projects = window.data.projects
 		$scope.visibleProjects = $scope.projects
 		$scope.tags = window.data.tags
@@ -58,6 +58,11 @@ define [
 		$scope.selectedProjects = []
 		$scope.filter = "all"
 
+		$scope.windowHeight = $window.innerHeight
+		angular.element($window).bind "resize", () ->
+			$scope.windowHeight = $window.innerHeight
+			$scope.$apply()
+
 		# Allow tags to be accessed on projects as well
 		projectsById = {}
 		for project in $scope.projects

Разница между файлами не показана из-за своего большого размера
+ 5366 - 4
services/web/public/js/ace/ace.js


+ 0 - 242
services/web/public/js/ace/anchor.js

@@ -1,242 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var oop = require("./lib/oop");
-var EventEmitter = require("./lib/event_emitter").EventEmitter;
-
-/**
- *
- * Defines the floating pointer in the document. Whenever text is inserted or deleted before the cursor, the position of the cursor is updated.
- *
- * @class Anchor
- **/
-
-/**
- * Creates a new `Anchor` and associates it with a document.
- *
- * @param {Document} doc The document to associate with the anchor
- * @param {Number} row The starting row position
- * @param {Number} column The starting column position
- *
- * @constructor
- **/
-
-var Anchor = exports.Anchor = function(doc, row, column) {
-    this.$onChange = this.onChange.bind(this);
-    this.attach(doc);
-    
-    if (typeof column == "undefined")
-        this.setPosition(row.row, row.column);
-    else
-        this.setPosition(row, column);
-};
-
-(function() {
-
-    oop.implement(this, EventEmitter);
-
-    /**
-     * Returns an object identifying the `row` and `column` position of the current anchor.
-     * @returns {Object}
-     **/
-    this.getPosition = function() {
-        return this.$clipPositionToDocument(this.row, this.column);
-    };
-
-    /**
-     *
-     * Returns the current document.
-     * @returns {Document}
-     **/
-    this.getDocument = function() {
-        return this.document;
-    };
-
-    /**
-     * experimental: allows anchor to stick to the next on the left
-     */
-    this.$insertRight = false;
-    /**
-     * Fires whenever the anchor position changes.
-     *
-     * Both of these objects have a `row` and `column` property corresponding to the position.
-     *
-     * Events that can trigger this function include [[Anchor.setPosition `setPosition()`]].
-     *
-     * @event change
-     * @param {Object} e  An object containing information about the anchor position. It has two properties:
-     *  - `old`: An object describing the old Anchor position
-     *  - `value`: An object describing the new Anchor position
-     *
-     **/
-    this.onChange = function(e) {
-        var delta = e.data;
-        var range = delta.range;
-
-        if (range.start.row == range.end.row && range.start.row != this.row)
-            return;
-
-        if (range.start.row > this.row)
-            return;
-
-        if (range.start.row == this.row && range.start.column > this.column)
-            return;
-
-        var row = this.row;
-        var column = this.column;
-        var start = range.start;
-        var end = range.end;
-
-        if (delta.action === "insertText") {
-            if (start.row === row && start.column <= column) {
-                if (start.column === column && this.$insertRight) {
-                    // do nothing
-                } else if (start.row === end.row) {
-                    column += end.column - start.column;
-                } else {
-                    column -= start.column;
-                    row += end.row - start.row;
-                }
-            } else if (start.row !== end.row && start.row < row) {
-                row += end.row - start.row;
-            }
-        } else if (delta.action === "insertLines") {
-            if (start.row <= row) {
-                row += end.row - start.row;
-            }
-        } else if (delta.action === "removeText") {
-            if (start.row === row && start.column < column) {
-                if (end.column >= column)
-                    column = start.column;
-                else
-                    column = Math.max(0, column - (end.column - start.column));
-
-            } else if (start.row !== end.row && start.row < row) {
-                if (end.row === row)
-                    column = Math.max(0, column - end.column) + start.column;
-                row -= (end.row - start.row);
-            } else if (end.row === row) {
-                row -= end.row - start.row;
-                column = Math.max(0, column - end.column) + start.column;
-            }
-        } else if (delta.action == "removeLines") {
-            if (start.row <= row) {
-                if (end.row <= row)
-                    row -= end.row - start.row;
-                else {
-                    row = start.row;
-                    column = 0;
-                }
-            }
-        }
-
-        this.setPosition(row, column, true);
-    };
-
-    /**
-     * Sets the anchor position to the specified row and column. If `noClip` is `true`, the position is not clipped.
-     * @param {Number} row The row index to move the anchor to
-     * @param {Number} column The column index to move the anchor to
-     * @param {Boolean} noClip Identifies if you want the position to be clipped
-     *
-     **/
-    this.setPosition = function(row, column, noClip) {
-        var pos;
-        if (noClip) {
-            pos = {
-                row: row,
-                column: column
-            };
-        } else {
-            pos = this.$clipPositionToDocument(row, column);
-        }
-
-        if (this.row == pos.row && this.column == pos.column)
-            return;
-
-        var old = {
-            row: this.row,
-            column: this.column
-        };
-
-        this.row = pos.row;
-        this.column = pos.column;
-        this._emit("change", {
-            old: old,
-            value: pos
-        });
-    };
-
-    /**
-     * When called, the `'change'` event listener is removed.
-     *
-     **/
-    this.detach = function() {
-        this.document.removeEventListener("change", this.$onChange);
-    };
-    this.attach = function(doc) {
-        this.document = doc || this.document;
-        this.document.on("change", this.$onChange);
-    };
-
-    /**
-     * Clips the anchor position to the specified row and column.
-     * @param {Number} row The row index to clip the anchor to
-     * @param {Number} column The column index to clip the anchor to
-     *
-     **/
-    this.$clipPositionToDocument = function(row, column) {
-        var pos = {};
-
-        if (row >= this.document.getLength()) {
-            pos.row = Math.max(0, this.document.getLength() - 1);
-            pos.column = this.document.getLine(pos.row).length;
-        }
-        else if (row < 0) {
-            pos.row = 0;
-            pos.column = 0;
-        }
-        else {
-            pos.row = row;
-            pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));
-        }
-
-        if (column < 0)
-            pos.column = 0;
-
-        return pos;
-    };
-
-}).call(Anchor.prototype);
-
-});

+ 0 - 188
services/web/public/js/ace/anchor_test.js

@@ -1,188 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var Document = require("./document").Document;
-var Anchor = require("./anchor").Anchor;
-var Range = require("./range").Range;
-var assert = require("./test/assertions");
-
-module.exports = {
-
-    "test create anchor" : function() {
-        var doc = new Document("juhu");
-        var anchor = new Anchor(doc, 0, 0);
-        
-        assert.position(anchor.getPosition(), 0, 0);
-        assert.equal(anchor.getDocument(), doc);
-    },
-    
-    "test insert text in same row before cursor should move anchor column": function() {
-        var doc = new Document("juhu\nkinners");
-        var anchor = new Anchor(doc, 1, 4);
-        
-        doc.insert({row: 1, column: 1}, "123");
-        assert.position(anchor.getPosition(), 1, 7);
-    },
-    
-    "test insert lines before cursor should move anchor row": function() {
-        var doc = new Document("juhu\nkinners");
-        var anchor = new Anchor(doc, 1, 4);
-        
-        doc.insertLines(1, ["123", "456"]);
-        assert.position(anchor.getPosition(), 3, 4);    
-    },
-    
-    "test insert new line before cursor should move anchor column": function() {
-        var doc = new Document("juhu\nkinners");
-        var anchor = new Anchor(doc, 1, 4);
-        
-        doc.insertNewLine({row: 0, column: 0});
-        assert.position(anchor.getPosition(), 2, 4);    
-    },
-    
-    "test insert new line in anchor line before anchor should move anchor column and row": function() {
-        var doc = new Document("juhu\nkinners");
-        var anchor = new Anchor(doc, 1, 4);
-        
-        doc.insertNewLine({row: 1, column: 2});
-        assert.position(anchor.getPosition(), 2, 2);
-    },
-    
-    "test delete text in anchor line before anchor should move anchor column": function() {
-        var doc = new Document("juhu\nkinners");
-        var anchor = new Anchor(doc, 1, 4);
-        
-        doc.remove(new Range(1, 1, 1, 3));
-        assert.position(anchor.getPosition(), 1, 2);
-    },
-    
-    "test remove range which contains the anchor should move the anchor to the start of the range": function() {
-        var doc = new Document("juhu\nkinners");
-        var anchor = new Anchor(doc, 0, 3);
-        
-        doc.remove(new Range(0, 1, 1, 3));
-        assert.position(anchor.getPosition(), 0, 1);
-    },
-    
-    "test delete character before the anchor should have no effect": function() {
-        var doc = new Document("juhu\nkinners");
-        var anchor = new Anchor(doc, 1, 4);
-        
-        doc.remove(new Range(1, 4, 1, 5));
-        assert.position(anchor.getPosition(), 1, 4);
-    },
-    
-    "test delete lines in anchor line before anchor should move anchor row": function() {
-        var doc = new Document("juhu\n1\n2\nkinners");
-        var anchor = new Anchor(doc, 3, 4);
-        
-        doc.removeLines(1, 2);
-        assert.position(anchor.getPosition(), 1, 4);
-    },
-    
-    "test remove new line before the cursor": function() {
-        var doc = new Document("juhu\nkinners");
-        var anchor = new Anchor(doc, 1, 4);
-        
-        doc.removeNewLine(0);
-        assert.position(anchor.getPosition(), 0, 8);
-    },
-    
-    "test delete range which contains the anchor should move anchor to the end of the range": function() {
-        var doc = new Document("juhu\nkinners");
-        var anchor = new Anchor(doc, 1, 4);
-        
-        doc.remove(new Range(0, 2, 1, 2));
-        assert.position(anchor.getPosition(), 0, 4);
-    },
-    
-    "test delete line which contains the anchor should move anchor to the end of the range": function() {
-        var doc = new Document("juhu\nkinners\n123");
-        var anchor = new Anchor(doc, 1, 5);
-        
-        doc.removeLines(1, 1);
-        assert.position(anchor.getPosition(), 1, 0);
-    },
-    
-    "test remove after the anchor should have no effect": function() {
-        var doc = new Document("juhu\nkinners\n123");
-        var anchor = new Anchor(doc, 1, 2);
-        
-        doc.remove(new Range(1, 4, 2, 2));
-        assert.position(anchor.getPosition(), 1, 2);
-    },
-    
-    "test anchor changes triggered by document changes should emit change event": function(next) {
-        var doc = new Document("juhu\nkinners\n123");
-        var anchor = new Anchor(doc, 1, 5);
-        
-        anchor.on("change", function(e) {
-            assert.position(anchor.getPosition(), 0, 0);
-            next();
-        });
-        
-        doc.remove(new Range(0, 0, 2, 1));
-    },
-    
-    "test only fire change event if position changes": function() {
-        var doc = new Document("juhu\nkinners\n123");
-        var anchor = new Anchor(doc, 1, 5);
-        
-        anchor.on("change", function(e) {
-            assert.fail();
-        });
-        
-        doc.remove(new Range(2, 0, 2, 1));
-    },
-    
-    "test insert/remove lines at the end of the document": function() {
-        var doc = new Document("juhu\nkinners\n123");
-        var anchor = new Anchor(doc, 2, 4);
-        
-        doc.removeLines(0, 3);
-        assert.position(anchor.getPosition(), 0, 0);
-        doc.insertLines(0, ["a", "b", "c"]);        
-        assert.position(anchor.getPosition(), 3, 0);
-        assert.equal(doc.getValue(), "a\nb\nc\n");
-    }
-};
-
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec()
-}

+ 0 - 351
services/web/public/js/ace/autocomplete.js

@@ -1,351 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2012, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var HashHandler = require("./keyboard/hash_handler").HashHandler;
-var AcePopup = require("./autocomplete/popup").AcePopup;
-var util = require("./autocomplete/util");
-var event = require("./lib/event");
-var lang = require("./lib/lang");
-var snippetManager = require("./snippets").snippetManager;
-
-var Autocomplete = function() {
-    this.keyboardHandler = new HashHandler();
-    this.keyboardHandler.bindKeys(this.commands);
-
-    this.blurListener = this.blurListener.bind(this);
-    this.changeListener = this.changeListener.bind(this);
-    this.mousedownListener = this.mousedownListener.bind(this);
-    this.mousewheelListener = this.mousewheelListener.bind(this);
-    
-    this.changeTimer = lang.delayedCall(function() {
-        this.updateCompletions(true);
-    }.bind(this))
-};
-
-(function() {
-    this.$init = function() {
-        this.popup = new AcePopup(document.body || document.documentElement);
-        this.popup.on("click", function(e) {
-            this.insertMatch();
-            e.stop();
-        }.bind(this));
-    };
-
-    this.openPopup = function(editor, prefix, keepPopupPosition) {
-        if (!this.popup)
-            this.$init();
-
-        this.popup.setData(this.completions.filtered);
-
-        var renderer = editor.renderer;
-        if (!keepPopupPosition) {
-            this.popup.setRow(0);
-            this.popup.setFontSize(editor.getFontSize());
-
-            var lineHeight = renderer.layerConfig.lineHeight;
-            
-            var pos = renderer.$cursorLayer.getPixelPosition(this.base, true);            
-            pos.left -= this.popup.getTextLeftOffset();
-            
-            var rect = editor.container.getBoundingClientRect();
-            pos.top += rect.top - renderer.layerConfig.offset;
-            pos.left += rect.left - editor.renderer.scrollLeft;
-            pos.left += renderer.$gutterLayer.gutterWidth;
-
-            this.popup.show(pos, lineHeight);
-        }
-    };
-
-    this.detach = function() {
-        this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
-        this.editor.off("changeSelection", this.changeListener);
-        this.editor.off("blur", this.changeListener);
-        this.editor.off("mousedown", this.mousedownListener);
-        this.editor.off("mousewheel", this.mousewheelListener);
-        this.changeTimer.cancel();
-        
-        if (this.popup)
-            this.popup.hide();
-
-        this.activated = false;
-        this.completions = this.base = null;
-    };
-
-    this.changeListener = function(e) {
-        var cursor = this.editor.selection.lead;
-        if (cursor.row != this.base.row || cursor.column < this.base.column) {
-            this.detach();
-        }
-        if (this.activated)
-            this.changeTimer.schedule();
-        else
-            this.detach();
-    };
-
-    this.blurListener = function() {
-        if (document.activeElement != this.editor.textInput.getElement())
-            this.detach();
-    };
-
-    this.mousedownListener = function(e) {
-        this.detach();
-    };
-
-    this.mousewheelListener = function(e) {
-        this.detach();
-    };
-
-    this.goTo = function(where) {
-        var row = this.popup.getRow();
-        var max = this.popup.session.getLength() - 1;
-
-        switch(where) {
-            case "up": row = row < 0 ? max : row - 1; break;
-            case "down": row = row >= max ? -1 : row + 1; break;
-            case "start": row = 0; break;
-            case "end": row = max; break;
-        }
-
-        this.popup.setRow(row);
-    };
-
-    this.insertMatch = function(data) {
-        if (!data)
-            data = this.popup.getData(this.popup.getRow());
-        if (!data)
-            return false;
-        if (data.completer && data.completer.insertMatch) {
-            data.completer.insertMatch(this.editor);
-        } else {
-            if (this.completions.filterText) {
-                var ranges = this.editor.selection.getAllRanges();
-                for (var i = 0, range; range = ranges[i]; i++) {
-                    range.start.column -= this.completions.filterText.length;
-                    this.editor.session.remove(range);
-                }
-            }
-            if (data.snippet)
-                snippetManager.insertSnippet(this.editor, data.snippet);
-            else
-                this.editor.execCommand("insertstring", data.value || data);
-        }
-        this.detach();
-    };
-
-    this.commands = {
-        "Up": function(editor) { editor.completer.goTo("up"); },
-        "Down": function(editor) { editor.completer.goTo("down"); },
-        "Ctrl-Up|Ctrl-Home": function(editor) { editor.completer.goTo("start"); },
-        "Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); },
-
-        "Esc": function(editor) { editor.completer.detach(); },
-        "Space": function(editor) { editor.completer.detach(); editor.insert(" ");},
-        "Return": function(editor) { editor.completer.insertMatch(); },
-        "Shift-Return": function(editor) { editor.completer.insertMatch(true); },
-        "Tab": function(editor) { editor.completer.insertMatch(); },
-
-        "PageUp": function(editor) { editor.completer.popup.gotoPageUp(); },
-        "PageDown": function(editor) { editor.completer.popup.gotoPageDown(); }
-    };
-
-    this.gatherCompletions = function(editor, callback) {
-        var session = editor.getSession();
-        var pos = editor.getCursorPosition();
-        
-        var line = session.getLine(pos.row);
-        var prefix = util.retrievePrecedingIdentifier(line, pos.column);
-        
-        this.base = editor.getCursorPosition();
-        this.base.column -= prefix.length;
-
-        var matches = [];
-        util.parForEach(editor.completers, function(completer, next) {
-            completer.getCompletions(editor, session, pos, prefix, function(err, results) {
-                if (!err)
-                    matches = matches.concat(results);
-                next();
-            });
-        }, function() {
-            callback(null, {
-                prefix: prefix,
-                matches: matches
-            });
-        });
-        return true;
-    };
-
-    this.showPopup = function(editor) {
-        if (this.editor)
-            this.detach();
-        
-        this.activated = true;
-
-        this.editor = editor;
-        if (editor.completer != this) {
-            if (editor.completer)
-                editor.completer.detach();
-            editor.completer = this;
-        }
-
-        editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
-        editor.on("changeSelection", this.changeListener);
-        editor.on("blur", this.blurListener);
-        editor.on("mousedown", this.mousedownListener);
-        editor.on("mousewheel", this.mousewheelListener);
-        
-        this.updateCompletions();
-    };
-    
-    this.updateCompletions = function(keepPopupPosition) {
-        if (keepPopupPosition && this.base && this.completions) {
-            var pos = this.editor.getCursorPosition();
-            var prefix = this.editor.session.getTextRange({start: this.base, end: pos});
-            if (prefix == this.completions.filterText)
-                return;
-            this.completions.setFilter(prefix);
-            if (!this.completions.filtered.length)
-                return this.detach();
-            this.openPopup(this.editor, prefix, keepPopupPosition);
-            return;
-        }
-        this.gatherCompletions(this.editor, function(err, results) {
-            var matches = results && results.matches;
-            if (!matches || !matches.length)
-                return this.detach();
-            // TODO reenable this when we have proper change tracking 
-            // if (matches.length == 1)
-            //     return this.insertMatch(matches[0]);
-
-            this.completions = new FilteredList(matches);
-            this.completions.setFilter(results.prefix);
-            if (!this.completions.filtered.length)
-                return this.detach();
-            this.openPopup(this.editor, results.prefix, keepPopupPosition);
-        }.bind(this));
-    };
-
-    this.cancelContextMenu = function() {
-        var stop = function(e) {
-            this.editor.off("nativecontextmenu", stop);
-            if (e && e.domEvent)
-                event.stopEvent(e.domEvent);
-        }.bind(this);
-        setTimeout(stop, 10);
-        this.editor.on("nativecontextmenu", stop);
-    };
-
-}).call(Autocomplete.prototype);
-
-Autocomplete.startCommand = {
-    name: "startAutocomplete",
-    exec: function(editor) {
-        if (!editor.completer)
-            editor.completer = new Autocomplete();
-        editor.completer.showPopup(editor);
-        // needed for firefox on mac
-        editor.completer.cancelContextMenu();
-    },
-    bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
-};
-
-var FilteredList = function(array, filterText, mutateData) {
-    this.all = array;
-    this.filtered = array;
-    this.filterText = filterText || "";
-};
-(function(){
-    this.setFilter = function(str) {
-        if (str.length > this.filterText && str.lastIndexOf(this.filterText, 0) === 0)
-            var matches = this.filtered;
-        else
-            var matches = this.all;
-
-        this.filterText = str;
-        matches = this.filterCompletions(matches, this.filterText);
-        matches = matches.sort(function(a, b) {
-            return b.exactMatch - a.exactMatch || b.score - a.score;
-        });
-        
-        // make unique
-        var prev = null;
-        matches = matches.filter(function(item){
-            var caption = item.value || item.caption || item.snippet; 
-            if (caption === prev) return false;
-            prev = caption;
-            return true;
-        });
-        
-        this.filtered = matches;
-    };
-    this.filterCompletions = function(items, needle) {
-        var results = [];
-        var upper = needle.toUpperCase();
-        var lower = needle.toLowerCase();
-        loop: for (var i = 0, item; item = items[i]; i++) {
-            var caption = item.value || item.caption || item.snippet;
-            if (!caption) continue;
-            var lastIndex = -1;
-            var matchMask = 0;
-            var penalty = 0;
-            var index, distance;
-            // caption char iteration is faster in Chrome but slower in Firefox, so lets use indexOf
-            for (var j = 0; j < needle.length; j++) {
-                // TODO add penalty on case mismatch
-                var i1 = caption.indexOf(lower[j], lastIndex + 1);
-                var i2 = caption.indexOf(upper[j], lastIndex + 1);
-                index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2;
-                if (index < 0)
-                    continue loop;
-                distance = index - lastIndex - 1;
-                if (distance > 0) {
-                    // first char mismatch should be more sensitive
-                    if (lastIndex === -1)
-                        penalty += 10;
-                    penalty += distance;
-                }
-                matchMask = matchMask | (1 << index);
-                lastIndex = index;
-            }
-            item.matchMask = matchMask;
-            item.exactMatch = penalty ? 0 : 1;
-            item.score = (item.score || 0) - penalty;
-            results.push(item);
-        }
-        return results;
-    };
-}).call(FilteredList.prototype);
-
-exports.Autocomplete = Autocomplete;
-exports.FilteredList = FilteredList;
-
-});

+ 0 - 332
services/web/public/js/ace/autocomplete/popup.js

@@ -1,332 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2012, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var EditSession = require("../edit_session").EditSession;
-var Renderer = require("../virtual_renderer").VirtualRenderer;
-var Editor = require("../editor").Editor;
-var Range = require("../range").Range;
-var event = require("../lib/event");
-var lang = require("../lib/lang");
-var dom = require("../lib/dom");
-
-var $singleLineEditor = function(el) {
-    var renderer = new Renderer(el);
-
-    renderer.$maxLines = 4;
-    
-    var editor = new Editor(renderer);
-
-    editor.setHighlightActiveLine(false);
-    editor.setShowPrintMargin(false);
-    editor.renderer.setShowGutter(false);
-    editor.renderer.setHighlightGutterLine(false);
-
-    editor.$mouseHandler.$focusWaitTimout = 0;
-
-    return editor;
-};
-
-var AcePopup = function(parentNode) {
-    var el = dom.createElement("div");
-    var popup = new $singleLineEditor(el);
-    
-    if (parentNode)
-        parentNode.appendChild(el);
-    el.style.display = "none";
-    popup.renderer.content.style.cursor = "default";
-    popup.renderer.setStyle("ace_autocomplete");
-    
-    popup.setOption("displayIndentGuides", false);
-
-    var noop = function(){};
-
-    popup.focus = noop;
-    popup.$isFocused = true;
-
-    popup.renderer.$cursorLayer.restartTimer = noop;
-    popup.renderer.$cursorLayer.element.style.opacity = 0;
-
-    popup.renderer.$maxLines = 8;
-    popup.renderer.$keepTextAreaAtCursor = false;
-
-    popup.setHighlightActiveLine(false);
-    // set default highlight color
-    popup.session.highlight("");
-    popup.session.$searchHighlight.clazz = "ace_highlight-marker";
-
-    popup.on("mousedown", function(e) {
-        var pos = e.getDocumentPosition();
-        popup.moveCursorToPosition(pos);
-        popup.selection.clearSelection();
-        selectionMarker.start.row = selectionMarker.end.row = pos.row;
-        e.stop();
-    });
-
-    var lastMouseEvent;
-    var hoverMarker = new Range(-1,0,-1,Infinity);
-    var selectionMarker = new Range(-1,0,-1,Infinity);
-    selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine");
-    popup.setSelectOnHover = function(val) {
-        if (!val) {
-            hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine");
-        } else if (hoverMarker.id) {
-            popup.session.removeMarker(hoverMarker.id);
-            hoverMarker.id = null;
-        }
-    }
-    popup.setSelectOnHover(false);
-    popup.on("mousemove", function(e) {
-        if (!lastMouseEvent) {
-            lastMouseEvent = e;
-            return;
-        }
-        if (lastMouseEvent.x == e.x && lastMouseEvent.y == e.y) {
-            return;
-        }
-        lastMouseEvent = e;
-        lastMouseEvent.scrollTop = popup.renderer.scrollTop;
-        var row = lastMouseEvent.getDocumentPosition().row;
-        if (hoverMarker.start.row != row) {
-            if (!hoverMarker.id)
-                popup.setRow(row);
-            setHoverMarker(row);
-        }
-    });
-    popup.renderer.on("beforeRender", function() {
-        if (lastMouseEvent && hoverMarker.start.row != -1) {
-            lastMouseEvent.$pos = null;
-            var row = lastMouseEvent.getDocumentPosition().row;
-            if (!hoverMarker.id)
-                popup.setRow(row);
-            setHoverMarker(row, true);
-        }
-    });
-    popup.renderer.on("afterRender", function() {
-        var row = popup.getRow();
-        var t = popup.renderer.$textLayer;
-        var selected = t.element.childNodes[row - t.config.firstRow];
-        if (selected == t.selectedNode)
-            return;
-        if (t.selectedNode)
-            dom.removeCssClass(t.selectedNode, "ace_selected");
-        t.selectedNode = selected;
-        if (selected)
-            dom.addCssClass(selected, "ace_selected");
-    });
-    var hideHoverMarker = function() { setHoverMarker(-1) };
-    var setHoverMarker = function(row, suppressRedraw) {
-        if (row !== hoverMarker.start.row) {
-            hoverMarker.start.row = hoverMarker.end.row = row;
-            if (!suppressRedraw)
-                popup.session._emit("changeBackMarker");
-            popup._emit("changeHoverMarker");
-        }
-    };
-    popup.getHoveredRow = function() {
-        return hoverMarker.start.row;
-    };
-    
-    event.addListener(popup.container, "mouseout", hideHoverMarker);
-    popup.on("hide", hideHoverMarker);
-    popup.on("changeSelection", hideHoverMarker);
-    
-    popup.session.doc.getLength = function() {
-        return popup.data.length;
-    };
-    popup.session.doc.getLine = function(i) {
-        var data = popup.data[i];
-        if (typeof data == "string")
-            return data;
-        return (data && data.value) || "";
-    };
-
-    var bgTokenizer = popup.session.bgTokenizer;
-    bgTokenizer.$tokenizeRow = function(i) {
-        var data = popup.data[i];
-        var tokens = [];
-        if (!data)
-            return tokens;
-        if (typeof data == "string")
-            data = {value: data};
-        if (!data.caption)
-            data.caption = data.value;
-
-        var last = -1;
-        var flag, c;
-        for (var i = 0; i < data.caption.length; i++) {
-            c = data.caption[i];
-            flag = data.matchMask & (1 << i) ? 1 : 0;
-            if (last !== flag) {
-                tokens.push({type: data.className || "" + ( flag ? "completion-highlight" : ""), value: c});
-                last = flag;
-            } else {
-                tokens[tokens.length - 1].value += c;
-            }
-        }
-
-        if (data.meta) {
-            var maxW = popup.renderer.$size.scrollerWidth / popup.renderer.layerConfig.characterWidth;
-            if (data.meta.length + data.caption.length < maxW - 2)
-                tokens.push({type: "rightAlignedText", value: data.meta});
-        }
-        return tokens;
-    };
-    bgTokenizer.$updateOnChange = noop;
-    bgTokenizer.start = noop;
-    
-    popup.session.$computeWidth = function() {
-        return this.screenWidth = 0;
-    }
-
-    // public
-    popup.isOpen = false;
-    popup.isTopdown = false;
-    
-    popup.data = [];
-    popup.setData = function(list) {
-        popup.data = list || [];
-        popup.setValue(lang.stringRepeat("\n", list.length), -1);
-        popup.setRow(0);
-    };
-    popup.getData = function(row) {
-        return popup.data[row];
-    };
-
-    popup.getRow = function() {
-        return selectionMarker.start.row;
-    };
-    popup.setRow = function(line) {
-        line = Math.max(-1, Math.min(this.data.length, line));
-        if (selectionMarker.start.row != line) {
-            popup.selection.clearSelection();
-            selectionMarker.start.row = selectionMarker.end.row = line || 0;
-            popup.session._emit("changeBackMarker");
-            popup.moveCursorTo(line || 0, 0);
-            if (popup.isOpen)
-                popup._signal("select");
-        }
-    };
-    
-    popup.on("changeSelection", function() {
-        if (popup.isOpen)
-            popup.setRow(popup.selection.lead.row);
-    });
-
-    popup.hide = function() {
-        this.container.style.display = "none";
-        this._signal("hide");
-        popup.isOpen = false;
-    };
-    popup.show = function(pos, lineHeight, topdownOnly) {
-        var el = this.container;
-        var screenHeight = window.innerHeight;
-        var screenWidth = window.innerWidth;
-        var renderer = this.renderer;
-        // var maxLines = Math.min(renderer.$maxLines, this.session.getLength());
-        var maxH = renderer.$maxLines * lineHeight * 1.4;
-        var top = pos.top + this.$borderSize;
-        if (top + maxH > screenHeight - lineHeight && !topdownOnly) {
-            el.style.top = "";
-            el.style.bottom = screenHeight - top + "px";
-            popup.isTopdown = false;
-        } else {
-            top += lineHeight;
-            el.style.top = top + "px";
-            el.style.bottom = "";
-            popup.isTopdown = true;
-        }
-
-        el.style.display = "";
-        this.renderer.$textLayer.checkForSizeChanges();
-        
-        var left = pos.left;
-        if (left + el.offsetWidth > screenWidth)
-            left = screenWidth - el.offsetWidth;
-            
-        el.style.left = left + "px";
-        
-        this._signal("show");
-        lastMouseEvent = null;
-        popup.isOpen = true;
-    };
-    
-    popup.getTextLeftOffset = function() {
-        return this.$borderSize + this.renderer.$padding + this.$imageSize;
-    };
-    
-    popup.$imageSize = 0;
-    popup.$borderSize = 1;
-
-    return popup;
-};
-
-dom.importCssString("\
-.ace_autocomplete.ace-tm .ace_marker-layer .ace_active-line {\
-    background-color: #CAD6FA;\
-    z-index: 1;\
-}\
-.ace_autocomplete.ace-tm .ace_line-hover {\
-    border: 1px solid #abbffe;\
-    margin-top: -1px;\
-    background: rgba(233,233,253,0.4);\
-}\
-.ace_autocomplete .ace_line-hover {\
-    position: absolute;\
-    z-index: 2;\
-}\
-.ace_rightAlignedText {\
-    color: gray;\
-    display: inline-block;\
-    position: absolute;\
-    right: 4px;\
-    text-align: right;\
-    z-index: -1;\
-}\
-.ace_autocomplete .ace_completion-highlight{\
-    color: #000;\
-    text-shadow: 0 0 0.01em;\
-}\
-.ace_autocomplete {\
-    width: 280px;\
-    z-index: 200000;\
-    background: #fbfbfb;\
-    color: #444;\
-    border: 1px lightgray solid;\
-    position: fixed;\
-    box-shadow: 2px 3px 5px rgba(0,0,0,.2);\
-    line-height: 1.4;\
-}");
-
-exports.AcePopup = AcePopup;
-
-});

+ 0 - 78
services/web/public/js/ace/autocomplete/text_completer.js

@@ -1,78 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2012, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-    var Range = require("ace/range").Range;
-    
-    var splitRegex = /[^a-zA-Z_0-9\$\-]+/;
-
-    function getWordIndex(doc, pos) {
-        var textBefore = doc.getTextRange(Range.fromPoints({row: 0, column:0}, pos));
-        return textBefore.split(splitRegex).length - 1;
-    }
-
-    /**
-     * Does a distance analysis of the word `prefix` at position `pos` in `doc`.
-     * @return Map
-     */
-    function wordDistance(doc, pos) {
-        var prefixPos = getWordIndex(doc, pos);
-        var words = doc.getValue().split(splitRegex);
-        var wordScores = Object.create(null);
-        
-        var currentWord = words[prefixPos];
-
-        words.forEach(function(word, idx) {
-            if (!word || word === currentWord) return;
-
-            var distance = Math.abs(prefixPos - idx);
-            var score = words.length - distance;
-            if (wordScores[word]) {
-                wordScores[word] = Math.max(score, wordScores[word]);
-            } else {
-                wordScores[word] = score;
-            }
-        });
-        return wordScores;
-    }
-
-    exports.getCompletions = function(editor, session, pos, prefix, callback) {
-        var wordScore = wordDistance(session, pos, prefix);
-        var wordList = Object.keys(wordScore);
-        callback(null, wordList.map(function(word) {
-            return {
-                name: word,
-                value: word,
-                score: wordScore[word],
-                meta: "local"
-            };
-        }));
-    };
-});

+ 0 - 74
services/web/public/js/ace/autocomplete/util.js

@@ -1,74 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2012, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-exports.parForEach = function(array, fn, callback) {
-    var completed = 0;
-    var arLength = array.length;
-    if (arLength === 0)
-        callback();
-    for (var i = 0; i < arLength; i++) {
-        fn(array[i], function(result, err) {
-            completed++;
-            if (completed === arLength)
-                callback(result, err);
-        });
-    }
-}
-
-var ID_REGEX = /[a-zA-Z_0-9\$-]/;
-
-exports.retrievePrecedingIdentifier = function(text, pos, regex) {
-    regex = regex || ID_REGEX;
-    var buf = [];
-    for (var i = pos-1; i >= 0; i--) {
-        if (regex.test(text[i]))
-            buf.push(text[i]);
-        else
-            break;
-    }
-    return buf.reverse().join("");
-}
-
-exports.retrieveFollowingIdentifier = function(text, pos, regex) {
-    regex = regex || ID_REGEX;
-    var buf = [];
-    for (var i = pos; i < text.length; i++) {
-        if (regex.test(text[i]))
-            buf.push(text[i]);
-        else
-            break;
-    }
-    return buf;
-}
-
-});

+ 0 - 255
services/web/public/js/ace/background_tokenizer.js

@@ -1,255 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var oop = require("./lib/oop");
-var EventEmitter = require("./lib/event_emitter").EventEmitter;
-
-
-/**
- * 
- *
- * Tokenizes the current [[Document `Document`]] in the background, and caches the tokenized rows for future use. 
- * 
- * If a certain row is changed, everything below that row is re-tokenized.
- *
- * @class BackgroundTokenizer
- **/
-
-/**
- * Creates a new `BackgroundTokenizer` object.
- * @param {Tokenizer} tokenizer The tokenizer to use
- * @param {Editor} editor The editor to associate with
- *
- * 
- * 
- * @constructor
- **/
-
-var BackgroundTokenizer = function(tokenizer, editor) {
-    this.running = false;
-    this.lines = [];
-    this.states = [];
-    this.currentLine = 0;
-    this.tokenizer = tokenizer;
-
-    var self = this;
-
-    this.$worker = function() {
-        if (!self.running) { return; }
-
-        var workerStart = new Date();
-        var currentLine = self.currentLine;
-        var endLine = -1;
-        var doc = self.doc;
-
-        while (self.lines[currentLine])
-            currentLine++;
-
-        var startLine = currentLine;
-
-        var len = doc.getLength();
-        var processedLines = 0;
-        self.running = false;
-        while (currentLine < len) {
-            self.$tokenizeRow(currentLine);
-            endLine = currentLine;
-            do {
-                currentLine++;
-            } while (self.lines[currentLine]);
-
-            // only check every 5 lines
-            processedLines ++;
-            if ((processedLines % 5 == 0) && (new Date() - workerStart) > 20) {                
-                self.running = setTimeout(self.$worker, 20);
-                self.currentLine = currentLine;
-                return;
-            }
-        }
-        self.currentLine = currentLine;
-        
-        if (startLine <= endLine)
-            self.fireUpdateEvent(startLine, endLine);
-    };
-};
-
-(function(){
-
-    oop.implement(this, EventEmitter);
-
-    /**
-     * Sets a new tokenizer for this object.
-     *
-     * @param {Tokenizer} tokenizer The new tokenizer to use
-     *
-     **/
-    this.setTokenizer = function(tokenizer) {
-        this.tokenizer = tokenizer;
-        this.lines = [];
-        this.states = [];
-
-        this.start(0);
-    };
-
-    /**
-     * Sets a new document to associate with this object.
-     * @param {Document} doc The new document to associate with
-     **/
-    this.setDocument = function(doc) {
-        this.doc = doc;
-        this.lines = [];
-        this.states = [];
-
-        this.stop();
-    };
-
-     /**
-     * Fires whenever the background tokeniziers between a range of rows are going to be updated.
-     * 
-     * @event update
-     * @param {Object} e An object containing two properties, `first` and `last`, which indicate the rows of the region being updated.
-     *
-     **/
-    /**
-     * Emits the `'update'` event. `firstRow` and `lastRow` are used to define the boundaries of the region to be updated.
-     * @param {Number} firstRow The starting row region
-     * @param {Number} lastRow The final row region
-     *
-     **/
-    this.fireUpdateEvent = function(firstRow, lastRow) {
-        var data = {
-            first: firstRow,
-            last: lastRow
-        };
-        this._emit("update", {data: data});
-    };
-
-    /**
-     * Starts tokenizing at the row indicated.
-     *
-     * @param {Number} startRow The row to start at
-     *
-     **/
-    this.start = function(startRow) {
-        this.currentLine = Math.min(startRow || 0, this.currentLine, this.doc.getLength());
-
-        // remove all cached items below this line
-        this.lines.splice(this.currentLine, this.lines.length);
-        this.states.splice(this.currentLine, this.states.length);
-
-        this.stop();
-        // pretty long delay to prevent the tokenizer from interfering with the user
-        this.running = setTimeout(this.$worker, 700);
-    };
-    
-    this.scheduleStart = function() {
-        if (!this.running)
-            this.running = setTimeout(this.$worker, 700);
-    }
-
-    this.$updateOnChange = function(delta) {
-        var range = delta.range;
-        var startRow = range.start.row;
-        var len = range.end.row - startRow;
-
-        if (len === 0) {
-            this.lines[startRow] = null;
-        } else if (delta.action == "removeText" || delta.action == "removeLines") {
-            this.lines.splice(startRow, len + 1, null);
-            this.states.splice(startRow, len + 1, null);
-        } else {
-            var args = Array(len + 1);
-            args.unshift(startRow, 1);
-            this.lines.splice.apply(this.lines, args);
-            this.states.splice.apply(this.states, args);
-        }
-
-        this.currentLine = Math.min(startRow, this.currentLine, this.doc.getLength());
-
-        this.stop();
-    };
-
-    /**
-     * Stops tokenizing.
-     *
-     **/
-    this.stop = function() {
-        if (this.running)
-            clearTimeout(this.running);
-        this.running = false;
-    };
-
-    /**
-     * Gives list of tokens of the row. (tokens are cached)
-     * 
-     * @param {Number} row The row to get tokens at
-     *
-     * 
-     *
-     **/
-    this.getTokens = function(row) {
-        return this.lines[row] || this.$tokenizeRow(row);
-    };
-
-    /**
-     * [Returns the state of tokenization at the end of a row.]{: #BackgroundTokenizer.getState}
-     *
-     * @param {Number} row The row to get state at
-     **/
-    this.getState = function(row) {
-        if (this.currentLine == row)
-            this.$tokenizeRow(row);
-        return this.states[row] || "start";
-    };
-
-    this.$tokenizeRow = function(row) {
-        var line = this.doc.getLine(row);
-        var state = this.states[row - 1];
-
-        var data = this.tokenizer.getLineTokens(line, state, row);
-
-        if (this.states[row] + "" !== data.state + "") {
-            this.states[row] = data.state;
-            this.lines[row + 1] = null;
-            if (this.currentLine > row + 1)
-                this.currentLine = row + 1;
-        } else if (this.currentLine == row) {
-            this.currentLine = row + 1;
-        }
-
-        return this.lines[row] = data.tokens;
-    };
-
-}).call(BackgroundTokenizer.prototype);
-
-exports.BackgroundTokenizer = BackgroundTokenizer;
-});

+ 0 - 85
services/web/public/js/ace/background_tokenizer_test.js

@@ -1,85 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var EditSession = require("./edit_session").EditSession;
-var JavaScriptMode = require("./mode/javascript").Mode;
-var Range = require("./range").Range;
-var assert = require("./test/assertions");
-
-function forceTokenize(session){
-    for (var i = 0, l = session.getLength(); i < l; i++)
-        session.getTokens(i)
-}
-
-function testStates(session, states) {
-    for (var i = 0, l = session.getLength(); i < l; i++)
-        assert.equal(session.bgTokenizer.states[i], states[i])
-    assert.ok(l == states.length)
-}
-
-module.exports = {
-
-    "test background tokenizer update on session change" : function() {
-        var doc = new EditSession([
-            "/*",
-            "*/",
-            "var juhu"
-        ]);
-        doc.setMode("./mode/javascript")  
-        
-        forceTokenize(doc)
-        testStates(doc, ["comment_regex_allowed", "start", "no_regex"])
-        
-        doc.remove(new Range(0,2,1,2))
-        testStates(doc, [null, "no_regex"])
-        
-        forceTokenize(doc)
-        testStates(doc, ["comment_regex_allowed", "comment_regex_allowed"])
-        
-        doc.insert({row:0, column:2}, "\n*/")
-        testStates(doc, [undefined, undefined, "comment_regex_allowed"])
-        
-        forceTokenize(doc)
-        testStates(doc, ["comment_regex_allowed", "start", "no_regex"])
-    }
-};
-
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec()
-}

+ 0 - 112
services/web/public/js/ace/commands/command_manager.js

@@ -1,112 +0,0 @@
-define(function(require, exports, module) {
-"use strict";
-
-var oop = require("../lib/oop");
-var HashHandler = require("../keyboard/hash_handler").HashHandler;
-var EventEmitter = require("../lib/event_emitter").EventEmitter;
-
-/**
- * @class CommandManager
- *
- *
- **/
-
-/**
- * new CommandManager(platform, commands)
- * @param {String} platform Identifier for the platform; must be either `'mac'` or `'win'`
- * @param {Array} commands A list of commands
- *
- **/
-
-var CommandManager = function(platform, commands) {
-    HashHandler.call(this, commands, platform);
-    this.byName = this.commands;
-    this.setDefaultHandler("exec", function(e) {
-        return e.command.exec(e.editor, e.args || {});
-    });
-};
-
-oop.inherits(CommandManager, HashHandler);
-
-(function() {
-
-    oop.implement(this, EventEmitter);
-
-    this.exec = function(command, editor, args) {
-        if (typeof command === 'string')
-            command = this.commands[command];
-
-        if (!command)
-            return false;
-
-        if (editor && editor.$readOnly && !command.readOnly)
-            return false;
-
-        var e = {editor: editor, command: command, args: args};
-        var retvalue = this._emit("exec", e);
-        this._signal("afterExec", e);
-
-        return retvalue === false ? false : true;
-    };
-
-    this.toggleRecording = function(editor) {
-        if (this.$inReplay)
-            return;
-
-        editor && editor._emit("changeStatus");
-        if (this.recording) {
-            this.macro.pop();
-            this.removeEventListener("exec", this.$addCommandToMacro);
-
-            if (!this.macro.length)
-                this.macro = this.oldMacro;
-
-            return this.recording = false;
-        }
-        if (!this.$addCommandToMacro) {
-            this.$addCommandToMacro = function(e) {
-                this.macro.push([e.command, e.args]);
-            }.bind(this);
-        }
-
-        this.oldMacro = this.macro;
-        this.macro = [];
-        this.on("exec", this.$addCommandToMacro);
-        return this.recording = true;
-    };
-
-    this.replay = function(editor) {
-        if (this.$inReplay || !this.macro)
-            return;
-
-        if (this.recording)
-            return this.toggleRecording(editor);
-
-        try {
-            this.$inReplay = true;
-            this.macro.forEach(function(x) {
-                if (typeof x == "string")
-                    this.exec(x, editor);
-                else
-                    this.exec(x[0], editor, x[1]);
-            }, this);
-        } finally {
-            this.$inReplay = false;
-        }
-    };
-
-    this.trimMacro = function(m) {
-        return m.map(function(x){
-            if (typeof x[0] != "string")
-                x[0] = x[0].name;
-            if (!x[1])
-                x = x[0];
-            return x;
-        });
-    };
-
-}).call(CommandManager.prototype);
-
-exports.CommandManager = CommandManager;
-
-});

+ 0 - 199
services/web/public/js/ace/commands/command_manager_test.js

@@ -1,199 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var CommandManager = require("./command_manager").CommandManager;
-var keys = require("../lib/keys");
-var assert = require("../test/assertions");
-
-module.exports = {
-
-    setUp: function() {
-        this.command = {
-            name: "gotoline",
-            bindKey: {
-                mac: "Command-L",
-                win: "Ctrl-L"
-            },
-            called: false,
-            exec: function(editor) { this.called = true; }
-        };
-
-        this.cm = new CommandManager("mac", [this.command]);
-    },
-
-    "test: register command": function() {
-        this.cm.exec("gotoline");
-        assert.ok(this.command.called);
-    },
-
-    "test: mac hotkeys": function() {
-        var command = this.cm.findKeyCommand(keys.KEY_MODS.command, "l");
-        assert.equal(command, this.command);
-
-        var command = this.cm.findKeyCommand(keys.KEY_MODS.ctrl, "l");
-        assert.equal(command, undefined);
-    },
-
-    "test: win hotkeys": function() {
-        var cm = new CommandManager("win", [this.command]);
-
-        var command = cm.findKeyCommand(keys.KEY_MODS.command, "l");
-        assert.equal(command, undefined);
-
-        var command = cm.findKeyCommand(keys.KEY_MODS.ctrl, "l");
-        assert.equal(command, this.command);
-    },
-
-    "test: remove command by object": function() {
-        this.cm.removeCommand(this.command);
-
-        this.cm.exec("gotoline");
-        assert.ok(!this.command.called);
-
-        var command = this.cm.findKeyCommand(keys.KEY_MODS.command, "l");
-        assert.equal(command, null);
-    },
-
-    "test: remove command by name": function() {
-        this.cm.removeCommand("gotoline");
-
-        this.cm.exec("gotoline");
-        assert.ok(!this.command.called);
-
-        var command = this.cm.findKeyCommand(keys.KEY_MODS.command, "l");
-        assert.equal(command, null);
-    },
-
-    "test: adding a new command with the same name as an existing one should remove the old one first": function() {
-        var command = {
-            name: "gotoline",
-            bindKey: {
-                mac: "Command-L",
-                win: "Ctrl-L"
-            },
-            called: false,
-            exec: function(editor) { this.called = true; }
-        };
-        this.cm.addCommand(command);
-
-        this.cm.exec("gotoline");
-        assert.ok(command.called);
-        assert.ok(!this.command.called);
-
-        assert.equal(this.cm.findKeyCommand(keys.KEY_MODS.command, "l"), command);
-    },
-
-    "test: adding commands and recording a macro": function() {
-        var called = "";
-        this.cm.addCommands({
-            togglerecording: function(editor) {
-                editor.cm.toggleRecording(editor);
-            },
-            replay: function(editor) {
-                editor.cm.replay();
-            },
-            cm1: function(editor, arg) {
-                called += "1" + (arg || "");
-            },
-            cm2: function(editor) {
-                called += "2";
-            }
-        });
-        
-        
-        var statusUpdateEmitted = false;
-        this._emit = function() {statusUpdateEmitted = true};
-
-        this.cm.exec("togglerecording", this);
-        assert.ok(this.cm.recording);
-        assert.ok(statusUpdateEmitted);
-
-        this.cm.exec("cm1", this, "-");
-        this.cm.exec("cm2");
-        this.cm.exec("replay", this);
-        assert.ok(!this.cm.recording);
-        assert.equal(called, "1-2");
-
-        called = "";
-        this.cm.exec("replay", this);
-        assert.equal(called, "1-2");
-    },
-
-    "test: bindkeys": function() {
-        this.cm.bindKeys({
-            "Ctrl-L|Command-C": "cm1",
-            "Ctrl-R": "cm2"
-        });
-
-        var command = this.cm.findKeyCommand(keys.KEY_MODS.command, "c");
-        assert.equal(command, "cm1");
-
-        var command = this.cm.findKeyCommand(keys.KEY_MODS.ctrl, "r");
-        assert.equal(command, "cm2");
-
-        this.cm.bindKeys({
-            "Ctrl-R": null
-        });
-
-        var command = this.cm.findKeyCommand(keys.KEY_MODS.ctrl, "r");
-        assert.equal(command, null);
-    },
-
-    "test: binding keys without modifiers": function() {
-        this.cm.bindKeys({
-            "R": "cm1",
-            "Shift-r": "cm2",
-            "Return": "cm4",
-            "Enter": "cm3"
-        });
-
-        var command = this.cm.findKeyCommand(-1, "r");
-        assert.equal(command, "cm1");
-
-        var command = this.cm.findKeyCommand(-1, "R");
-        assert.equal(command, "cm2");
-
-        var command = this.cm.findKeyCommand(0, "return");
-        assert.equal(command, "cm3");
-    }
-};
-
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec();
-}

+ 0 - 542
services/web/public/js/ace/commands/default_commands.js

@@ -1,542 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var lang = require("../lib/lang");
-var config = require("../config");
-
-function bindKey(win, mac) {
-    return {win: win, mac: mac};
-}
-
-/*
-    multiSelectAction: "forEach"|"forEachLine"|function|undefined,
-    scrollIntoView: true|"cursor"|"center"|"selectionPart"
-*/
-exports.commands = [{
-    name: "showSettingsMenu",
-    bindKey: bindKey("Ctrl-,", "Command-,"),
-    exec: function(editor) {
-        config.loadModule("ace/ext/settings_menu", function(module) {
-            module.init(editor);
-            editor.showSettingsMenu();
-        });
-    },
-    readOnly: true
-}, {
-    name: "selectall",
-    bindKey: bindKey("Ctrl-A", "Command-A"),
-    exec: function(editor) { editor.selectAll(); },
-    readOnly: true
-}, {
-    name: "centerselection",
-    bindKey: bindKey(null, "Ctrl-L"),
-    exec: function(editor) { editor.centerSelection(); },
-    readOnly: true
-}, {
-    name: "gotoline",
-    bindKey: bindKey("Ctrl-L", "Command-L"),
-    exec: function(editor) {
-        var line = parseInt(prompt("Enter line number:"), 10);
-        if (!isNaN(line)) {
-            editor.gotoLine(line);
-        }
-    },
-    readOnly: true
-}, {
-    name: "fold",
-    bindKey: bindKey("Alt-L|Ctrl-F1", "Command-Alt-L|Command-F1"),
-    exec: function(editor) { editor.session.toggleFold(false); },
-    scrollIntoView: "center",
-    readOnly: true
-}, {
-    name: "unfold",
-    bindKey: bindKey("Alt-Shift-L|Ctrl-Shift-F1", "Command-Alt-Shift-L|Command-Shift-F1"),
-    exec: function(editor) { editor.session.toggleFold(true); },
-    scrollIntoView: "center",
-    readOnly: true
-}, {
-    name: "toggleFoldWidget",
-    bindKey: bindKey("F2", "F2"),
-    exec: function(editor) { editor.session.toggleFoldWidget(); },
-    scrollIntoView: "center",
-    readOnly: true
-}, {
-    name: "toggleParentFoldWidget",
-    bindKey: bindKey("Alt-F2", "Alt-F2"),
-    exec: function(editor) { editor.session.toggleFoldWidget(true); },
-    scrollIntoView: "center",
-    readOnly: true
-}, {
-    name: "foldall",
-    bindKey: bindKey("Ctrl-Alt-0", "Ctrl-Command-Option-0"),
-    exec: function(editor) { editor.session.foldAll(); },
-    scrollIntoView: "center",
-    readOnly: true
-}, {
-    name: "foldOther",
-    bindKey: bindKey("Alt-0", "Command-Option-0"),
-    exec: function(editor) { 
-        editor.session.foldAll();
-        editor.session.unfold(editor.selection.getAllRanges());
-    },
-    scrollIntoView: "center",
-    readOnly: true
-}, {
-    name: "unfoldall",
-    bindKey: bindKey("Alt-Shift-0", "Command-Option-Shift-0"),
-    exec: function(editor) { editor.session.unfold(); },
-    scrollIntoView: "center",
-    readOnly: true
-}, {
-    name: "findnext",
-    bindKey: bindKey("Ctrl-K", "Command-G"),
-    exec: function(editor) { editor.findNext(); },
-    readOnly: true
-}, {
-    name: "findprevious",
-    bindKey: bindKey("Ctrl-Shift-K", "Command-Shift-G"),
-    exec: function(editor) { editor.findPrevious(); },
-    readOnly: true
-}, {
-    name: "selectOrFindNext",
-    bindKey: bindKey("ALt-K", "Ctrl-G"),
-    exec: function(editor) {
-        if (editor.selection.isEmpty())
-            editor.selection.selectWord();
-        else
-            editor.findNext(); 
-    },
-    readOnly: true
-}, {
-    name: "selectOrFindPrevious",
-    bindKey: bindKey("Alt-Shift-K", "Ctrl-Shift-G"),
-    exec: function(editor) { 
-        if (editor.selection.isEmpty())
-            editor.selection.selectWord();
-        else
-            editor.findPrevious();
-    },
-    readOnly: true
-}, {
-    name: "find",
-    bindKey: bindKey("Ctrl-F", "Command-F"),
-    exec: function(editor) {
-        config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor)});
-    },
-    readOnly: true
-}, {
-    name: "overwrite",
-    bindKey: "Insert",
-    exec: function(editor) { editor.toggleOverwrite(); },
-    readOnly: true
-}, {
-    name: "selecttostart",
-    bindKey: bindKey("Ctrl-Shift-Home", "Command-Shift-Up"),
-    exec: function(editor) { editor.getSelection().selectFileStart(); },
-    multiSelectAction: "forEach",
-    readOnly: true,
-    aceCommandGroup: "fileJump"
-}, {
-    name: "gotostart",
-    bindKey: bindKey("Ctrl-Home", "Command-Home|Command-Up"),
-    exec: function(editor) { editor.navigateFileStart(); },
-    multiSelectAction: "forEach",
-    readOnly: true,
-    aceCommandGroup: "fileJump"
-}, {
-    name: "selectup",
-    bindKey: bindKey("Shift-Up", "Shift-Up"),
-    exec: function(editor) { editor.getSelection().selectUp(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "golineup",
-    bindKey: bindKey("Up", "Up|Ctrl-P"),
-    exec: function(editor, args) { editor.navigateUp(args.times); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selecttoend",
-    bindKey: bindKey("Ctrl-Shift-End", "Command-Shift-Down"),
-    exec: function(editor) { editor.getSelection().selectFileEnd(); },
-    multiSelectAction: "forEach",
-    readOnly: true,
-    aceCommandGroup: "fileJump"
-}, {
-    name: "gotoend",
-    bindKey: bindKey("Ctrl-End", "Command-End|Command-Down"),
-    exec: function(editor) { editor.navigateFileEnd(); },
-    multiSelectAction: "forEach",
-    readOnly: true,
-    aceCommandGroup: "fileJump"
-}, {
-    name: "selectdown",
-    bindKey: bindKey("Shift-Down", "Shift-Down"),
-    exec: function(editor) { editor.getSelection().selectDown(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "golinedown",
-    bindKey: bindKey("Down", "Down|Ctrl-N"),
-    exec: function(editor, args) { editor.navigateDown(args.times); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selectwordleft",
-    bindKey: bindKey("Ctrl-Shift-Left", "Option-Shift-Left"),
-    exec: function(editor) { editor.getSelection().selectWordLeft(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "gotowordleft",
-    bindKey: bindKey("Ctrl-Left", "Option-Left"),
-    exec: function(editor) { editor.navigateWordLeft(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selecttolinestart",
-    bindKey: bindKey("Alt-Shift-Left", "Command-Shift-Left"),
-    exec: function(editor) { editor.getSelection().selectLineStart(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "gotolinestart",
-    bindKey: bindKey("Alt-Left|Home", "Command-Left|Home|Ctrl-A"),
-    exec: function(editor) { editor.navigateLineStart(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selectleft",
-    bindKey: bindKey("Shift-Left", "Shift-Left"),
-    exec: function(editor) { editor.getSelection().selectLeft(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "gotoleft",
-    bindKey: bindKey("Left", "Left|Ctrl-B"),
-    exec: function(editor, args) { editor.navigateLeft(args.times); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selectwordright",
-    bindKey: bindKey("Ctrl-Shift-Right", "Option-Shift-Right"),
-    exec: function(editor) { editor.getSelection().selectWordRight(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "gotowordright",
-    bindKey: bindKey("Ctrl-Right", "Option-Right"),
-    exec: function(editor) { editor.navigateWordRight(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selecttolineend",
-    bindKey: bindKey("Alt-Shift-Right", "Command-Shift-Right"),
-    exec: function(editor) { editor.getSelection().selectLineEnd(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "gotolineend",
-    bindKey: bindKey("Alt-Right|End", "Command-Right|End|Ctrl-E"),
-    exec: function(editor) { editor.navigateLineEnd(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selectright",
-    bindKey: bindKey("Shift-Right", "Shift-Right"),
-    exec: function(editor) { editor.getSelection().selectRight(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "gotoright",
-    bindKey: bindKey("Right", "Right|Ctrl-F"),
-    exec: function(editor, args) { editor.navigateRight(args.times); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selectpagedown",
-    bindKey: "Shift-PageDown",
-    exec: function(editor) { editor.selectPageDown(); },
-    readOnly: true
-}, {
-    name: "pagedown",
-    bindKey: bindKey(null, "Option-PageDown"),
-    exec: function(editor) { editor.scrollPageDown(); },
-    readOnly: true
-}, {
-    name: "gotopagedown",
-    bindKey: bindKey("PageDown", "PageDown|Ctrl-V"),
-    exec: function(editor) { editor.gotoPageDown(); },
-    readOnly: true
-}, {
-    name: "selectpageup",
-    bindKey: "Shift-PageUp",
-    exec: function(editor) { editor.selectPageUp(); },
-    readOnly: true
-}, {
-    name: "pageup",
-    bindKey: bindKey(null, "Option-PageUp"),
-    exec: function(editor) { editor.scrollPageUp(); },
-    readOnly: true
-}, {
-    name: "gotopageup",
-    bindKey: "PageUp",
-    exec: function(editor) { editor.gotoPageUp(); },
-    readOnly: true
-}, {
-    name: "scrollup",
-    bindKey: bindKey("Ctrl-Up", null),
-    exec: function(e) { e.renderer.scrollBy(0, -2 * e.renderer.layerConfig.lineHeight); },
-    readOnly: true
-}, {
-    name: "scrolldown",
-    bindKey: bindKey("Ctrl-Down", null),
-    exec: function(e) { e.renderer.scrollBy(0, 2 * e.renderer.layerConfig.lineHeight); },
-    readOnly: true
-}, {
-    name: "selectlinestart",
-    bindKey: "Shift-Home",
-    exec: function(editor) { editor.getSelection().selectLineStart(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selectlineend",
-    bindKey: "Shift-End",
-    exec: function(editor) { editor.getSelection().selectLineEnd(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "togglerecording",
-    bindKey: bindKey("Ctrl-Alt-E", "Command-Option-E"),
-    exec: function(editor) { editor.commands.toggleRecording(editor); },
-    readOnly: true
-}, {
-    name: "replaymacro",
-    bindKey: bindKey("Ctrl-Shift-E", "Command-Shift-E"),
-    exec: function(editor) { editor.commands.replay(editor); },
-    readOnly: true
-}, {
-    name: "jumptomatching",
-    bindKey: bindKey("Ctrl-P", "Ctrl-Shift-P"),
-    exec: function(editor) { editor.jumpToMatching(); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, {
-    name: "selecttomatching",
-    bindKey: bindKey("Ctrl-Shift-P", null),
-    exec: function(editor) { editor.jumpToMatching(true); },
-    multiSelectAction: "forEach",
-    readOnly: true
-}, 
-
-// commands disabled in readOnly mode
-{
-    name: "cut",
-    exec: function(editor) {
-        var range = editor.getSelectionRange();
-        editor._emit("cut", range);
-
-        if (!editor.selection.isEmpty()) {
-            editor.session.remove(range);
-            editor.clearSelection();
-        }
-    },
-    multiSelectAction: "forEach"
-}, {
-    name: "removeline",
-    bindKey: bindKey("Ctrl-D", "Command-D"),
-    exec: function(editor) { editor.removeLines(); },
-    multiSelectAction: "forEachLine"
-}, {
-    name: "duplicateSelection",
-    bindKey: bindKey("Ctrl-Shift-D", "Command-Shift-D"),
-    exec: function(editor) { editor.duplicateSelection(); },
-    multiSelectAction: "forEach"
-}, {
-    name: "sortlines",
-    bindKey: bindKey("Ctrl-Alt-S", "Command-Alt-S"),
-    exec: function(editor) { editor.sortLines(); },
-    multiSelectAction: "forEachLine"
-}, {
-    name: "togglecomment",
-    bindKey: bindKey("Ctrl-/", "Command-/"),
-    exec: function(editor) { editor.toggleCommentLines(); },
-    multiSelectAction: "forEachLine",
-    scrollIntoView: "selectionPart"
-}, {
-    name: "toggleBlockComment",
-    bindKey: bindKey("Ctrl-Shift-/", "Command-Shift-/"),
-    exec: function(editor) { editor.toggleBlockComment(); },
-    multiSelectAction: "forEach"
-}, {
-    name: "modifyNumberUp",
-    bindKey: bindKey("Ctrl-Shift-Up", "Alt-Shift-Up"),
-    exec: function(editor) { editor.modifyNumber(1); },
-    multiSelectAction: "forEach"
-}, {
-    name: "modifyNumberDown",
-    bindKey: bindKey("Ctrl-Shift-Down", "Alt-Shift-Down"),
-    exec: function(editor) { editor.modifyNumber(-1); },
-    multiSelectAction: "forEach"
-}, {
-    name: "replace",
-    bindKey: bindKey("Ctrl-H", "Command-Option-F"),
-    exec: function(editor) {
-        config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor, true)});
-    }
-}, {
-    name: "undo",
-    bindKey: bindKey("Ctrl-Z", "Command-Z"),
-    exec: function(editor) { editor.undo(); }
-}, {
-    name: "redo",
-    bindKey: bindKey("Ctrl-Shift-Z|Ctrl-Y", "Command-Shift-Z|Command-Y"),
-    exec: function(editor) { editor.redo(); }
-}, {
-    name: "copylinesup",
-    bindKey: bindKey("Alt-Shift-Up", "Command-Option-Up"),
-    exec: function(editor) { editor.copyLinesUp(); }
-}, {
-    name: "movelinesup",
-    bindKey: bindKey("Alt-Up", "Option-Up"),
-    exec: function(editor) { editor.moveLinesUp(); }
-}, {
-    name: "copylinesdown",
-    bindKey: bindKey("Alt-Shift-Down", "Command-Option-Down"),
-    exec: function(editor) { editor.copyLinesDown(); }
-}, {
-    name: "movelinesdown",
-    bindKey: bindKey("Alt-Down", "Option-Down"),
-    exec: function(editor) { editor.moveLinesDown(); }
-}, {
-    name: "del",
-    bindKey: bindKey("Delete", "Delete|Ctrl-D|Shift-Delete"),
-    exec: function(editor) { editor.remove("right"); },
-    multiSelectAction: "forEach"
-}, {
-    name: "backspace",
-    bindKey: bindKey(
-        "Shift-Backspace|Backspace",
-        "Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"
-    ),
-    exec: function(editor) { editor.remove("left"); },
-    multiSelectAction: "forEach"
-}, {
-    name: "cut_or_delete",
-    bindKey: bindKey("Shift-Delete", null),
-    exec: function(editor) { 
-        if (editor.selection.isEmpty()) {
-            editor.remove("left");
-        } else {
-            return false;
-        }
-    },
-    multiSelectAction: "forEach"
-}, {
-    name: "removetolinestart",
-    bindKey: bindKey("Alt-Backspace", "Command-Backspace"),
-    exec: function(editor) { editor.removeToLineStart(); },
-    multiSelectAction: "forEach"
-}, {
-    name: "removetolineend",
-    bindKey: bindKey("Alt-Delete", "Ctrl-K"),
-    exec: function(editor) { editor.removeToLineEnd(); },
-    multiSelectAction: "forEach"
-}, {
-    name: "removewordleft",
-    bindKey: bindKey("Ctrl-Backspace", "Alt-Backspace|Ctrl-Alt-Backspace"),
-    exec: function(editor) { editor.removeWordLeft(); },
-    multiSelectAction: "forEach"
-}, {
-    name: "removewordright",
-    bindKey: bindKey("Ctrl-Delete", "Alt-Delete"),
-    exec: function(editor) { editor.removeWordRight(); },
-    multiSelectAction: "forEach"
-}, {
-    name: "outdent",
-    bindKey: bindKey("Shift-Tab", "Shift-Tab"),
-    exec: function(editor) { editor.blockOutdent(); },
-    multiSelectAction: "forEach",
-    scrollIntoView: "selectionPart"
-}, {
-    name: "indent",
-    bindKey: bindKey("Tab", "Tab"),
-    exec: function(editor) { editor.indent(); },
-    multiSelectAction: "forEach",
-    scrollIntoView: "selectionPart"
-}, {
-    name: "blockoutdent",
-    bindKey: bindKey("Ctrl-[", "Ctrl-["),
-    exec: function(editor) { editor.blockOutdent(); },
-    multiSelectAction: "forEachLine",
-    scrollIntoView: "selectionPart"
-}, {
-    name: "blockindent",
-    bindKey: bindKey("Ctrl-]", "Ctrl-]"),
-    exec: function(editor) { editor.blockIndent(); },
-    multiSelectAction: "forEachLine",
-    scrollIntoView: "selectionPart"
-}, {
-    name: "insertstring",
-    exec: function(editor, str) { editor.insert(str); },
-    multiSelectAction: "forEach",
-    scrollIntoView: "cursor"
-}, {
-    name: "inserttext",
-    exec: function(editor, args) {
-        editor.insert(lang.stringRepeat(args.text  || "", args.times || 1));
-    },
-    multiSelectAction: "forEach"
-}, {
-    name: "splitline",
-    bindKey: bindKey(null, "Ctrl-O"),
-    exec: function(editor) { editor.splitLine(); },
-    multiSelectAction: "forEach"
-}, {
-    name: "transposeletters",
-    bindKey: bindKey("Ctrl-T", "Ctrl-T"),
-    exec: function(editor) { editor.transposeLetters(); },
-    multiSelectAction: function(editor) {editor.transposeSelections(1); }
-}, {
-    name: "touppercase",
-    bindKey: bindKey("Ctrl-U", "Ctrl-U"),
-    exec: function(editor) { editor.toUpperCase(); },
-    multiSelectAction: "forEach"
-}, {
-    name: "tolowercase",
-    bindKey: bindKey("Ctrl-Shift-U", "Ctrl-Shift-U"),
-    exec: function(editor) { editor.toLowerCase(); },
-    multiSelectAction: "forEach"
-}];
-
-});

+ 0 - 180
services/web/public/js/ace/commands/incremental_search_commands.js

@@ -1,180 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-
-var config = require("../config");
-var oop = require("../lib/oop");
-var HashHandler = require("../keyboard/hash_handler").HashHandler;
-var occurStartCommand = require("./occur_commands").occurStartCommand;
-
-// These commands can be installed in a normal key handler to start iSearch:
-exports.iSearchStartCommands = [{
-    name: "iSearch",
-    bindKey: {win: "Ctrl-F", mac: "Command-F"},
-    exec: function(editor, options) {
-        config.loadModule(["core", "ace/incremental_search"], function(e) {
-            var iSearch = e.iSearch = e.iSearch || new e.IncrementalSearch();
-            iSearch.activate(editor, options.backwards);
-            if (options.jumpToFirstMatch) iSearch.next(options);
-        });
-    },
-    readOnly: true
-}, {
-    name: "iSearchBackwards",
-    exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {backwards: true}); },
-    readOnly: true
-}, {
-    name: "iSearchAndGo",
-    bindKey: {win: "Ctrl-K", mac: "Command-G"},
-    exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {jumpToFirstMatch: true, useCurrentOrPrevSearch: true}); },
-    readOnly: true
-}, {
-    name: "iSearchBackwardsAndGo",
-    bindKey: {win: "Ctrl-Shift-K", mac: "Command-Shift-G"},
-    exec: function(editor) { editor.execCommand('iSearch', {jumpToFirstMatch: true, backwards: true, useCurrentOrPrevSearch: true}); },
-    readOnly: true
-}];
-
-// These commands are only available when incremental search mode is active:
-exports.iSearchCommands = [{
-    name: "restartSearch",
-    bindKey: {win: "Ctrl-F", mac: "Command-F"},
-    exec: function(iSearch) {
-        iSearch.cancelSearch(true);
-    },
-    readOnly: true,
-    isIncrementalSearchCommand: true
-}, {
-    name: "searchForward",
-    bindKey: {win: "Ctrl-S|Ctrl-K", mac: "Ctrl-S|Command-G"},
-    exec: function(iSearch, options) {
-        options.useCurrentOrPrevSearch = true;
-        iSearch.next(options);
-    },
-    readOnly: true,
-    isIncrementalSearchCommand: true
-}, {
-    name: "searchBackward",
-    bindKey: {win: "Ctrl-R|Ctrl-Shift-K", mac: "Ctrl-R|Command-Shift-G"},
-    exec: function(iSearch, options) {
-        options.useCurrentOrPrevSearch = true;
-        options.backwards = true;
-        iSearch.next(options);
-    },
-    readOnly: true,
-    isIncrementalSearchCommand: true
-}, {
-    name: "extendSearchTerm",
-    exec: function(iSearch, string) {
-        iSearch.addChar(string);
-    },
-    readOnly: true,
-    isIncrementalSearchCommand: true
-}, {
-    name: "extendSearchTermSpace",
-    bindKey: "space",
-    exec: function(iSearch) { iSearch.addChar(' '); },
-    readOnly: true,
-    isIncrementalSearchCommand: true
-}, {
-    name: "shrinkSearchTerm",
-    bindKey: "backspace",
-    exec: function(iSearch) {
-        iSearch.removeChar();
-    },
-    readOnly: true,
-    isIncrementalSearchCommand: true
-}, {
-    name: 'confirmSearch',
-    bindKey: 'return',
-    exec: function(iSearch) { iSearch.deactivate(); },
-    readOnly: true,
-    isIncrementalSearchCommand: true
-}, {
-    name: 'cancelSearch',
-    bindKey: 'esc|Ctrl-G',
-    exec: function(iSearch) { iSearch.deactivate(true); },
-    readOnly: true,
-    isIncrementalSearchCommand: true
-}, {
-    name: 'occurisearch',
-    bindKey: 'Ctrl-O',
-    exec: function(iSearch) {
-        var options = oop.mixin({}, iSearch.$options);
-        iSearch.deactivate();
-        occurStartCommand.exec(iSearch.$editor, options);
-    },
-    readOnly: true,
-    isIncrementalSearchCommand: true
-}];
-
-function IncrementalSearchKeyboardHandler(iSearch) {
-    this.$iSearch = iSearch;
-}
-
-oop.inherits(IncrementalSearchKeyboardHandler, HashHandler);
-
-;(function() {
-
-    this.attach = function(editor) {
-        var iSearch = this.$iSearch;
-        HashHandler.call(this, exports.iSearchCommands, editor.commands.platform);
-        this.$commandExecHandler = editor.commands.addEventListener('exec', function(e) {
-            if (!e.command.isIncrementalSearchCommand) return undefined;
-            e.stopPropagation();
-            e.preventDefault();
-            return e.command.exec(iSearch, e.args || {});
-        });
-    }
-
-    this.detach = function(editor) {
-        if (!this.$commandExecHandler) return;
-        editor.commands.removeEventListener('exec', this.$commandExecHandler);
-        delete this.$commandExecHandler;
-    }
-
-    var handleKeyboard$super = this.handleKeyboard;
-    this.handleKeyboard = function(data, hashId, key, keyCode) {
-        var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
-        if (cmd.command) { return cmd; }
-        if (hashId == -1) {
-            var extendCmd = this.commands.extendSearchTerm;
-            if (extendCmd) { return {command: extendCmd, args: key}; }
-        }
-        return {command: "null", passEvent: hashId == 0 || hashId == 4};
-    }
-
-}).call(IncrementalSearchKeyboardHandler.prototype);
-
-
-exports.IncrementalSearchKeyboardHandler = IncrementalSearchKeyboardHandler;
-
-});

+ 0 - 97
services/web/public/js/ace/commands/multi_select_commands.js

@@ -1,97 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-
-// commands to enter multiselect mode
-exports.defaultCommands = [{
-    name: "addCursorAbove",
-    exec: function(editor) { editor.selectMoreLines(-1); },
-    bindKey: {win: "Ctrl-Alt-Up", mac: "Ctrl-Alt-Up"},
-    readonly: true
-}, {
-    name: "addCursorBelow",
-    exec: function(editor) { editor.selectMoreLines(1); },
-    bindKey: {win: "Ctrl-Alt-Down", mac: "Ctrl-Alt-Down"},
-    readonly: true
-}, {
-    name: "addCursorAboveSkipCurrent",
-    exec: function(editor) { editor.selectMoreLines(-1, true); },
-    bindKey: {win: "Ctrl-Alt-Shift-Up", mac: "Ctrl-Alt-Shift-Up"},
-    readonly: true
-}, {
-    name: "addCursorBelowSkipCurrent",
-    exec: function(editor) { editor.selectMoreLines(1, true); },
-    bindKey: {win: "Ctrl-Alt-Shift-Down", mac: "Ctrl-Alt-Shift-Down"},
-    readonly: true
-}, {
-    name: "selectMoreBefore",
-    exec: function(editor) { editor.selectMore(-1); },
-    bindKey: {win: "Ctrl-Alt-Left", mac: "Ctrl-Alt-Left"},
-    readonly: true
-}, {
-    name: "selectMoreAfter",
-    exec: function(editor) { editor.selectMore(1); },
-    bindKey: {win: "Ctrl-Alt-Right", mac: "Ctrl-Alt-Right"},
-    readonly: true
-}, {
-    name: "selectNextBefore",
-    exec: function(editor) { editor.selectMore(-1, true); },
-    bindKey: {win: "Ctrl-Alt-Shift-Left", mac: "Ctrl-Alt-Shift-Left"},
-    readonly: true
-}, {
-    name: "selectNextAfter",
-    exec: function(editor) { editor.selectMore(1, true); },
-    bindKey: {win: "Ctrl-Alt-Shift-Right", mac: "Ctrl-Alt-Shift-Right"},
-    readonly: true
-}, {
-    name: "splitIntoLines",
-    exec: function(editor) { editor.multiSelect.splitIntoLines(); },
-    bindKey: {win: "Ctrl-Alt-L", mac: "Ctrl-Alt-L"},
-    readonly: true
-}, {
-    name: "alignCursors",
-    exec: function(editor) { editor.alignCursors(); },
-    bindKey: {win: "Ctrl-Alt-A", mac: "Ctrl-Alt-A"}
-}];
-
-// commands active only in multiselect mode
-exports.multiSelectCommands = [{
-    name: "singleSelection",
-    bindKey: "esc",
-    exec: function(editor) { editor.exitMultiSelectMode(); },
-    readonly: true,
-    isAvailable: function(editor) {return editor && editor.inMultiSelectMode}
-}];
-
-var HashHandler = require("../keyboard/hash_handler").HashHandler;
-exports.keyboardHandler = new HashHandler(exports.multiSelectCommands);
-
-});

+ 0 - 110
services/web/public/js/ace/commands/occur_commands.js

@@ -1,110 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-
-var config = require("../config"),
-    Occur = require("../occur").Occur;
-
-// These commands can be installed in a normal command handler to start occur:
-var occurStartCommand = {
-    name: "occur",
-    exec: function(editor, options) {
-        var alreadyInOccur = !!editor.session.$occur;
-        var occurSessionActive = new Occur().enter(editor, options);
-        if (occurSessionActive && !alreadyInOccur)
-            OccurKeyboardHandler.installIn(editor);
-    },
-    readOnly: true
-};
-
-var occurCommands = [{
-    name: "occurexit",
-    bindKey: 'esc|Ctrl-G',
-    exec: function(editor) {
-        var occur = editor.session.$occur;
-        if (!occur) return;
-        occur.exit(editor, {});
-        if (!editor.session.$occur) OccurKeyboardHandler.uninstallFrom(editor);
-    },
-    readOnly: true
-}, {
-    name: "occuraccept",
-    bindKey: 'enter',
-    exec: function(editor) {
-        var occur = editor.session.$occur;
-        if (!occur) return;
-        occur.exit(editor, {translatePosition: true});
-        if (!editor.session.$occur) OccurKeyboardHandler.uninstallFrom(editor);
-    },
-    readOnly: true
-}];
-
-var HashHandler = require("../keyboard/hash_handler").HashHandler;
-var oop = require("../lib/oop");
-
-
-function OccurKeyboardHandler() {}
-
-oop.inherits(OccurKeyboardHandler, HashHandler);
-
-;(function() {
-
-    this.isOccurHandler = true;
-
-    this.attach = function(editor) {
-        HashHandler.call(this, occurCommands, editor.commands.platform);
-        this.$editor = editor;
-    }
-
-    var handleKeyboard$super = this.handleKeyboard;
-    this.handleKeyboard = function(data, hashId, key, keyCode) {
-        var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
-        return (cmd && cmd.command) ? cmd : undefined;
-    }
-
-}).call(OccurKeyboardHandler.prototype);
-
-OccurKeyboardHandler.installIn = function(editor) {
-    var handler = new this();
-    editor.keyBinding.addKeyboardHandler(handler);
-    editor.commands.addCommands(occurCommands);
-}
-
-OccurKeyboardHandler.uninstallFrom = function(editor) {
-    editor.commands.removeCommands(occurCommands);
-    var handler = editor.getKeyboardHandler();
-    if (handler.isOccurHandler)
-        editor.keyBinding.removeKeyboardHandler(handler);
-}
-
-exports.occurStartCommand = occurStartCommand;
-
-});

+ 0 - 295
services/web/public/js/ace/config.js

@@ -1,295 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"no use strict";
-
-var lang = require("./lib/lang");
-var oop = require("./lib/oop");
-var net = require("./lib/net");
-var EventEmitter = require("./lib/event_emitter").EventEmitter;
-
-var global = (function() {
-    return this;
-})();
-
-var options = {
-    packaged: false,
-    workerPath: null,
-    modePath: null,
-    themePath: null,
-    basePath: "",
-    suffix: ".js",
-    $moduleUrls: {}
-};
-
-exports.get = function(key) {
-    if (!options.hasOwnProperty(key))
-        throw new Error("Unknown config key: " + key);
-
-    return options[key];
-};
-
-exports.set = function(key, value) {
-    if (!options.hasOwnProperty(key))
-        throw new Error("Unknown config key: " + key);
-
-    options[key] = value;
-};
-
-exports.all = function() {
-    return lang.copyObject(options);
-};
-
-// module loading
-oop.implement(exports, EventEmitter);
-
-exports.moduleUrl = function(name, component) {
-    if (options.$moduleUrls[name])
-        return options.$moduleUrls[name];
-
-    var parts = name.split("/");
-    component = component || parts[parts.length - 2] || "";
-    
-    // todo make this configurable or get rid of '-'
-    var sep = component == "snippets" ? "/" : "-";
-    var base = parts[parts.length - 1];    
-    if (sep == "-") {
-        var re = new RegExp("^" + component + "[\\-_]|[\\-_]" + component + "$", "g");
-        base = base.replace(re, "");
-    }
-
-    if ((!base || base == component) && parts.length > 1)
-        base = parts[parts.length - 2];
-    var path = options[component + "Path"];
-    if (path == null) {
-        path = options.basePath;
-    } else if (sep == "/") {
-        component = sep = "";
-    }
-    if (path && path.slice(-1) != "/")
-        path += "/";
-    return path + component + sep + base + this.get("suffix");
-};
-
-exports.setModuleUrl = function(name, subst) {
-    return options.$moduleUrls[name] = subst;
-};
-
-exports.$loading = {};
-exports.loadModule = function(moduleName, onLoad) {
-    var module, moduleType;
-    if (Array.isArray(moduleName)) {
-        moduleType = moduleName[0];
-        moduleName = moduleName[1];
-    }
-
-    try {
-        module = require(moduleName);
-    } catch (e) {}
-    // require(moduleName) can return empty object if called after require([moduleName], callback)
-    if (module && !exports.$loading[moduleName])
-        return onLoad && onLoad(module);
-
-    if (!exports.$loading[moduleName])
-        exports.$loading[moduleName] = [];
-
-    exports.$loading[moduleName].push(onLoad);
-
-    if (exports.$loading[moduleName].length > 1)
-        return;
-
-    var afterLoad = function() {
-        require([moduleName], function(module) {
-            exports._emit("load.module", {name: moduleName, module: module});
-            var listeners = exports.$loading[moduleName];
-            exports.$loading[moduleName] = null;
-            listeners.forEach(function(onLoad) {
-                onLoad && onLoad(module);
-            });
-        });
-    };
-
-    if (!exports.get("packaged"))
-        return afterLoad();
-    net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad);
-};
-
-
-// initialization
-exports.init = function() {
-    options.packaged = require.packaged || module.packaged || (global.define && define.packaged);
-
-    if (!global.document)
-        return "";
-
-    var scriptOptions = {};
-    var scriptUrl = "";
-
-    var scripts = document.getElementsByTagName("script");
-    for (var i=0; i<scripts.length; i++) {
-        var script = scripts[i];
-
-        var src = script.src || script.getAttribute("src");
-        if (!src)
-            continue;
-
-        var attributes = script.attributes;
-        for (var j=0, l=attributes.length; j < l; j++) {
-            var attr = attributes[j];
-            if (attr.name.indexOf("data-ace-") === 0) {
-                scriptOptions[deHyphenate(attr.name.replace(/^data-ace-/, ""))] = attr.value;
-            }
-        }
-
-        var m = src.match(/^(.*)\/ace(\-\w+)?\.js(\?|$)/);
-        if (m)
-            scriptUrl = m[1];
-    }
-
-    if (scriptUrl) {
-        scriptOptions.base = scriptOptions.base || scriptUrl;
-        scriptOptions.packaged = true;
-    }
-
-    scriptOptions.basePath = scriptOptions.base;
-    scriptOptions.workerPath = scriptOptions.workerPath || scriptOptions.base;
-    scriptOptions.modePath = scriptOptions.modePath || scriptOptions.base;
-    scriptOptions.themePath = scriptOptions.themePath || scriptOptions.base;
-    delete scriptOptions.base;
-
-    for (var key in scriptOptions)
-        if (typeof scriptOptions[key] !== "undefined")
-            exports.set(key, scriptOptions[key]);
-};
-
-function deHyphenate(str) {
-    return str.replace(/-(.)/g, function(m, m1) { return m1.toUpperCase(); });
-}
-
-var optionsProvider = {
-    setOptions: function(optList) {
-        Object.keys(optList).forEach(function(key) {
-            this.setOption(key, optList[key]);
-        }, this);
-    },
-    getOptions: function(optionNames) {
-        var result = {};
-        if (!optionNames) {
-            optionNames = Object.keys(this.$options);
-        } else if (!Array.isArray(optionNames)) {
-            result = optionNames;
-            optionNames = Object.keys(result);
-        }
-        optionNames.forEach(function(key) {
-            result[key] = this.getOption(key);
-        }, this);
-        return result;
-    },
-    setOption: function(name, value) {
-        if (this["$" + name] === value)
-            return;
-        var opt = this.$options[name];
-        if (!opt) {
-            if (typeof console != "undefined" && console.warn)
-                console.warn('misspelled option "' + name + '"');
-            return undefined;
-        }
-        if (opt.forwardTo)
-            return this[opt.forwardTo] && this[opt.forwardTo].setOption(name, value);
-
-        if (!opt.handlesSet)
-            this["$" + name] = value;
-        if (opt && opt.set)
-            opt.set.call(this, value);
-    },
-    getOption: function(name) {
-        var opt = this.$options[name];
-        if (!opt) {
-            if (typeof console != "undefined" && console.warn)
-                console.warn('misspelled option "' + name + '"');
-            return undefined;
-        }
-        if (opt.forwardTo)
-            return this[opt.forwardTo] && this[opt.forwardTo].getOption(name);
-        return opt && opt.get ? opt.get.call(this) : this["$" + name];
-    }
-};
-
-var defaultOptions = {};
-/*
- * option {name, value, initialValue, setterName, set, get }
- */
-exports.defineOptions = function(obj, path, options) {
-    if (!obj.$options)
-        defaultOptions[path] = obj.$options = {};
-
-    Object.keys(options).forEach(function(key) {
-        var opt = options[key];
-        if (typeof opt == "string")
-            opt = {forwardTo: opt};
-
-        opt.name || (opt.name = key);
-        obj.$options[opt.name] = opt;
-        if ("initialValue" in opt)
-            obj["$" + opt.name] = opt.initialValue;
-    });
-
-    // implement option provider interface
-    oop.implement(obj, optionsProvider);
-
-    return this;
-};
-
-exports.resetOptions = function(obj) {
-    Object.keys(obj.$options).forEach(function(key) {
-        var opt = obj.$options[key];
-        if ("value" in opt)
-            obj.setOption(key, opt.value);
-    });
-};
-
-exports.setDefaultValue = function(path, name, value) {
-    var opts = defaultOptions[path] || (defaultOptions[path] = {});
-    if (opts[name]) {
-        if (opts.forwardTo)
-            exports.setDefaultValue(opts.forwardTo, name, value);
-        else
-            opts[name].value = value;
-    }
-};
-
-exports.setDefaultValues = function(path, optionHash) {
-    Object.keys(optionHash).forEach(function(key) {
-        exports.setDefaultValue(path, key, optionHash[key]);
-    });
-};
-
-});

+ 0 - 128
services/web/public/js/ace/config_test.js

@@ -1,128 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var config = require("./config");
-var assert = require("./test/assertions");
-
-module.exports = {
-
-    "test: path resolution" : function() {
-        config.set("packaged", "true");
-        var url = config.moduleUrl("kr_theme", "theme");
-        assert.equal(url, "theme-kr.js");
-        
-        config.set("basePath", "a/b");
-        url = config.moduleUrl("m/theme", "theme");
-        assert.equal(url, "a/b/theme-m.js");
-        
-        url = config.moduleUrl("m/theme", "ext");
-        assert.equal(url, "a/b/ext-theme.js");
-        
-        config.set("workerPath", "c/");
-        url = config.moduleUrl("foo/1", "worker");
-        assert.equal(url, "c/worker-1.js");
-        
-        config.setModuleUrl("foo/1", "a/b1.js");
-        url = config.moduleUrl("foo/1", "theme");
-        assert.equal(url, "a/b1.js");
-        
-        url = config.moduleUrl("snippets/js");
-        assert.equal(url, "a/b/snippets/js.js");
-        
-        config.setModuleUrl("snippets/js", "_.js");
-        url = config.moduleUrl("snippets/js");
-        assert.equal(url, "_.js");
-        
-        url = config.moduleUrl("ace/ext/textarea");
-        assert.equal(url, "a/b/ext-textarea.js");
-        
-        assert.equal();
-    },
-    "test: define options" : function() {
-        var o = {};
-        config.defineOptions(o, "test_object", {
-            opt1: {
-                set: function(val) {
-                    this.x = val;
-                },
-                value: 7,
-            },
-            initialValue: {
-                set: function(val) {
-                    this.x = val;
-                },
-                initialValue: 8,
-            },
-            opt2: {
-                get: function(val) {
-                    return this.x;
-                }
-            },
-            forwarded: "model"
-        });
-        o.model = {};
-        config.defineOptions(o.model, "model", {
-            forwarded: {value: 1}
-        });
-        
-        config.resetOptions(o);
-        config.resetOptions(o.model);
-        assert.equal(o.getOption("opt1"), 7);
-        assert.equal(o.getOption("opt2"), 7);
-        o.setOption("opt1", 8);
-        assert.equal(o.getOption("opt1"), 8);
-        assert.equal(o.getOption("opt2"), 8);
-        
-        assert.equal(o.getOption("forwarded"), 1);
-        
-        assert.equal(o.getOption("new"), undefined);
-        o.setOption("new", 0);
-        assert.equal(o.getOption("new"), undefined);
-        
-
-        assert.equal(o.getOption("initialValue"), 8);
-        o.setOption("initialValue", 7);
-        assert.equal(o.getOption("opt2"), 7);
-
-    }
-};
-
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec()
-}

BIN
services/web/public/js/ace/css/codefolding-fold-button-states.png


Разница между файлами не показана из-за своего большого размера
+ 0 - 85
services/web/public/js/ace/css/editor.css


BIN
services/web/public/js/ace/css/expand-marker.png


+ 0 - 642
services/web/public/js/ace/document.js

@@ -1,642 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var oop = require("./lib/oop");
-var EventEmitter = require("./lib/event_emitter").EventEmitter;
-var Range = require("./range").Range;
-var Anchor = require("./anchor").Anchor;
-
-/**
- * Contains the text of the document. Document can be attached to several [[EditSession `EditSession`]]s. 
- *
- * At its core, `Document`s are just an array of strings, with each row in the document matching up to the array index.
- *
- * @class Document
- **/
-
- /**
- *
- * Creates a new `Document`. If `text` is included, the `Document` contains those strings; otherwise, it's empty.
- * @param {String | Array} text The starting text
- * @constructor
- **/
-
-var Document = function(text) {
-    this.$lines = [];
-
-    // There has to be one line at least in the document. If you pass an empty
-    // string to the insert function, nothing will happen. Workaround.
-    if (text.length == 0) {
-        this.$lines = [""];
-    } else if (Array.isArray(text)) {
-        this._insertLines(0, text);
-    } else {
-        this.insert({row: 0, column:0}, text);
-    }
-};
-
-(function() {
-
-    oop.implement(this, EventEmitter);
-
-    /**
-    * Replaces all the lines in the current `Document` with the value of `text`.
-    *
-    * @param {String} text The text to use
-    **/
-    this.setValue = function(text) {
-        var len = this.getLength();
-        this.remove(new Range(0, 0, len, this.getLine(len-1).length));
-        this.insert({row: 0, column:0}, text);
-    };
-
-    /**
-    * Returns all the lines in the document as a single string, joined by the new line character.
-    **/
-    this.getValue = function() {
-        return this.getAllLines().join(this.getNewLineCharacter());
-    };
-
-    /** 
-    * Creates a new `Anchor` to define a floating point in the document.
-    * @param {Number} row The row number to use
-    * @param {Number} column The column number to use
-    *
-    **/
-    this.createAnchor = function(row, column) {
-        return new Anchor(this, row, column);
-    };
-
-    /** 
-    * Splits a string of text on any newline (`\n`) or carriage-return ('\r') characters.
-    *
-    * @method $split
-    * @param {String} text The text to work with
-    * @returns {String} A String array, with each index containing a piece of the original `text` string.
-    *
-    **/
-
-    // check for IE split bug
-    if ("aaa".split(/a/).length == 0)
-        this.$split = function(text) {
-            return text.replace(/\r\n|\r/g, "\n").split("\n");
-        }
-    else
-        this.$split = function(text) {
-            return text.split(/\r\n|\r|\n/);
-        };
-
-
-    this.$detectNewLine = function(text) {
-        var match = text.match(/^.*?(\r\n|\r|\n)/m);
-        this.$autoNewLine = match ? match[1] : "\n";
-    };
-
-    /**
-    * Returns the newline character that's being used, depending on the value of `newLineMode`. 
-    * @returns {String} If `newLineMode == windows`, `\r\n` is returned.  
-    *  If `newLineMode == unix`, `\n` is returned.  
-    *  If `newLineMode == auto`, the value of `autoNewLine` is returned.
-    *
-    **/
-    this.getNewLineCharacter = function() {
-        switch (this.$newLineMode) {
-          case "windows":
-            return "\r\n";
-          case "unix":
-            return "\n";
-          default:
-            return this.$autoNewLine;
-        }
-    };
-
-    this.$autoNewLine = "\n";
-    this.$newLineMode = "auto";
-    /**
-     * [Sets the new line mode.]{: #Document.setNewLineMode.desc}
-     * @param {String} newLineMode [The newline mode to use; can be either `windows`, `unix`, or `auto`]{: #Document.setNewLineMode.param}
-     *
-     **/
-    this.setNewLineMode = function(newLineMode) {
-        if (this.$newLineMode === newLineMode)
-            return;
-
-        this.$newLineMode = newLineMode;
-    };
-
-    /**
-    * [Returns the type of newlines being used; either `windows`, `unix`, or `auto`]{: #Document.getNewLineMode}
-    * @returns {String}
-    **/
-    this.getNewLineMode = function() {
-        return this.$newLineMode;
-    };
-
-    /**
-    * Returns `true` if `text` is a newline character (either `\r\n`, `\r`, or `\n`).
-    * @param {String} text The text to check
-    *
-    **/
-    this.isNewLine = function(text) {
-        return (text == "\r\n" || text == "\r" || text == "\n");
-    };
-
-    /**
-    * Returns a verbatim copy of the given line as it is in the document
-    * @param {Number} row The row index to retrieve
-    *
-    **/
-    this.getLine = function(row) {
-        return this.$lines[row] || "";
-    };
-
-    /**
-    * Returns an array of strings of the rows between `firstRow` and `lastRow`. This function is inclusive of `lastRow`.
-    * @param {Number} firstRow The first row index to retrieve
-    * @param {Number} lastRow The final row index to retrieve
-    *
-    **/
-    this.getLines = function(firstRow, lastRow) {
-        return this.$lines.slice(firstRow, lastRow + 1);
-    };
-
-    /**
-    * Returns all lines in the document as string array.
-    **/
-    this.getAllLines = function() {
-        return this.getLines(0, this.getLength());
-    };
-
-    /**
-    * Returns the number of rows in the document.
-    **/
-    this.getLength = function() {
-        return this.$lines.length;
-    };
-
-    /**
-    * [Given a range within the document, this function returns all the text within that range as a single string.]{: #Document.getTextRange.desc}
-    * @param {Range} range The range to work with
-    * 
-    * @returns {String}
-    **/
-    this.getTextRange = function(range) {
-        if (range.start.row == range.end.row) {
-            return this.getLine(range.start.row)
-                .substring(range.start.column, range.end.column);
-        }
-        var lines = this.getLines(range.start.row, range.end.row);
-        lines[0] = (lines[0] || "").substring(range.start.column);
-        var l = lines.length - 1;
-        if (range.end.row - range.start.row == l)
-            lines[l] = lines[l].substring(0, range.end.column);
-        return lines.join(this.getNewLineCharacter());
-    };
-
-    this.$clipPosition = function(position) {
-        var length = this.getLength();
-        if (position.row >= length) {
-            position.row = Math.max(0, length - 1);
-            position.column = this.getLine(length-1).length;
-        } else if (position.row < 0)
-            position.row = 0;
-        return position;
-    };
-
-    /**
-    * Inserts a block of `text` at the indicated `position`.
-    * @param {Object} position The position to start inserting at; it's an object that looks like `{ row: row, column: column}`
-    * @param {String} text A chunk of text to insert
-    * @returns {Object} The position ({row, column}) of the last line of `text`. If the length of `text` is 0, this function simply returns `position`. 
-    *
-    **/
-    this.insert = function(position, text) {
-        if (!text || text.length === 0)
-            return position;
-
-        position = this.$clipPosition(position);
-
-        // only detect new lines if the document has no line break yet
-        if (this.getLength() <= 1)
-            this.$detectNewLine(text);
-
-        var lines = this.$split(text);
-        var firstLine = lines.splice(0, 1)[0];
-        var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0];
-
-        position = this.insertInLine(position, firstLine);
-        if (lastLine !== null) {
-            position = this.insertNewLine(position); // terminate first line
-            position = this._insertLines(position.row, lines);
-            position = this.insertInLine(position, lastLine || "");
-        }
-        return position;
-    };
-
-    /**
-     * Fires whenever the document changes.
-     *
-     * Several methods trigger different `"change"` events. Below is a list of each action type, followed by each property that's also available:
-     *
-     *  * `"insertLines"` (emitted by [[Document.insertLines]])
-     *    * `range`: the [[Range]] of the change within the document
-     *    * `lines`: the lines in the document that are changing
-     *  * `"insertText"` (emitted by [[Document.insertNewLine]])
-     *    * `range`: the [[Range]] of the change within the document
-     *    * `text`: the text that's being added
-     *  * `"removeLines"` (emitted by [[Document.insertLines]])
-     *    * `range`: the [[Range]] of the change within the document
-     *    * `lines`: the lines in the document that were removed
-     *    * `nl`: the new line character (as defined by [[Document.getNewLineCharacter]])
-     *  * `"removeText"` (emitted by [[Document.removeInLine]] and [[Document.removeNewLine]])
-     *    * `range`: the [[Range]] of the change within the document
-     *    * `text`: the text that's being removed
-     *
-     * @event change
-     * @param {Object} e Contains at least one property called `"action"`. `"action"` indicates the action that triggered the change. Each action also has a set of additional properties.
-     *
-     **/
-    /**
-    * Inserts the elements in `lines` into the document, starting at the row index given by `row`. This method also triggers the `'change'` event.
-    * @param {Number} row The index of the row to insert at
-    * @param {Array} lines An array of strings
-    * @returns {Object} Contains the final row and column, like this:  
-    *   ```
-    *   {row: endRow, column: 0}
-    *   ```  
-    *   If `lines` is empty, this function returns an object containing the current row, and column, like this:  
-    *   ``` 
-    *   {row: row, column: 0}
-    *   ```
-    *
-    **/
-    this.insertLines = function(row, lines) {
-        if (row >= this.getLength())
-            return this.insert({row: row, column: 0}, "\n" + lines.join("\n"));
-        return this._insertLines(Math.max(row, 0), lines);
-    };
-    this._insertLines = function(row, lines) {
-        if (lines.length == 0)
-            return {row: row, column: 0};
-
-        // apply doesn't work for big arrays (smallest threshold is on safari 0xFFFF)
-        // to circumvent that we have to break huge inserts into smaller chunks here
-        if (lines.length > 0xFFFF) {
-            var end = this._insertLines(row, lines.slice(0xFFFF));
-            lines = lines.slice(0, 0xFFFF);
-        }
-
-        var args = [row, 0];
-        args.push.apply(args, lines);
-        this.$lines.splice.apply(this.$lines, args);
-
-        var range = new Range(row, 0, row + lines.length, 0);
-        var delta = {
-            action: "insertLines",
-            range: range,
-            lines: lines
-        };
-        this._emit("change", { data: delta });
-        return end || range.end;
-    };
-
-    /**
-    * Inserts a new line into the document at the current row's `position`. This method also triggers the `'change'` event. 
-    * @param {Object} position The position to insert at
-    * @returns {Object} Returns an object containing the final row and column, like this:<br/>
-    *    ```
-    *    {row: endRow, column: 0}
-    *    ```
-    *
-    **/
-    this.insertNewLine = function(position) {
-        position = this.$clipPosition(position);
-        var line = this.$lines[position.row] || "";
-
-        this.$lines[position.row] = line.substring(0, position.column);
-        this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length));
-
-        var end = {
-            row : position.row + 1,
-            column : 0
-        };
-
-        var delta = {
-            action: "insertText",
-            range: Range.fromPoints(position, end),
-            text: this.getNewLineCharacter()
-        };
-        this._emit("change", { data: delta });
-
-        return end;
-    };
-
-    /**
-    * Inserts `text` into the `position` at the current row. This method also triggers the `'change'` event.
-    * @param {Object} position The position to insert at; it's an object that looks like `{ row: row, column: column}`
-    * @param {String} text A chunk of text
-    * @returns {Object} Returns an object containing the final row and column, like this:  
-    *     ```
-    *     {row: endRow, column: 0}
-    *     ```
-    *
-    **/
-    this.insertInLine = function(position, text) {
-        if (text.length == 0)
-            return position;
-
-        var line = this.$lines[position.row] || "";
-
-        this.$lines[position.row] = line.substring(0, position.column) + text
-                + line.substring(position.column);
-
-        var end = {
-            row : position.row,
-            column : position.column + text.length
-        };
-
-        var delta = {
-            action: "insertText",
-            range: Range.fromPoints(position, end),
-            text: text
-        };
-        this._emit("change", { data: delta });
-
-        return end;
-    };
-
-    /**
-    * Removes the `range` from the document.
-    * @param {Range} range A specified Range to remove
-    * @returns {Object} Returns the new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`.
-    *
-    **/
-    this.remove = function(range) {
-        if (!range instanceof Range)
-            range = Range.fromPoints(range.start, range.end);
-        // clip to document
-        range.start = this.$clipPosition(range.start);
-        range.end = this.$clipPosition(range.end);
-
-        if (range.isEmpty())
-            return range.start;
-
-        var firstRow = range.start.row;
-        var lastRow = range.end.row;
-
-        if (range.isMultiLine()) {
-            var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1;
-            var lastFullRow = lastRow - 1;
-
-            if (range.end.column > 0)
-                this.removeInLine(lastRow, 0, range.end.column);
-
-            if (lastFullRow >= firstFullRow)
-                this._removeLines(firstFullRow, lastFullRow);
-
-            if (firstFullRow != firstRow) {
-                this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length);
-                this.removeNewLine(range.start.row);
-            }
-        }
-        else {
-            this.removeInLine(firstRow, range.start.column, range.end.column);
-        }
-        return range.start;
-    };
-
-    /**
-    * Removes the specified columns from the `row`. This method also triggers the `'change'` event.
-    * @param {Number} row The row to remove from
-    * @param {Number} startColumn The column to start removing at 
-    * @param {Number} endColumn The column to stop removing at
-    * @returns {Object} Returns an object containing `startRow` and `startColumn`, indicating the new row and column values.<br/>If `startColumn` is equal to `endColumn`, this function returns nothing.
-    *
-    **/
-    this.removeInLine = function(row, startColumn, endColumn) {
-        if (startColumn == endColumn)
-            return;
-
-        var range = new Range(row, startColumn, row, endColumn);
-        var line = this.getLine(row);
-        var removed = line.substring(startColumn, endColumn);
-        var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length);
-        this.$lines.splice(row, 1, newLine);
-
-        var delta = {
-            action: "removeText",
-            range: range,
-            text: removed
-        };
-        this._emit("change", { data: delta });
-        return range.start;
-    };
-
-    /**
-    * Removes a range of full lines. This method also triggers the `'change'` event.
-    * @param {Number} firstRow The first row to be removed
-    * @param {Number} lastRow The last row to be removed
-    * @returns {[String]} Returns all the removed lines.
-    *
-    **/
-    this.removeLines = function(firstRow, lastRow) {
-        if (firstRow < 0 || lastRow >= this.getLength())
-            return this.remove(new Range(firstRow, 0, lastRow + 1, 0));
-        return this._removeLines(firstRow, lastRow);
-    };
-
-    this._removeLines = function(firstRow, lastRow) {
-        var range = new Range(firstRow, 0, lastRow + 1, 0);
-        var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);
-
-        var delta = {
-            action: "removeLines",
-            range: range,
-            nl: this.getNewLineCharacter(),
-            lines: removed
-        };
-        this._emit("change", { data: delta });
-        return removed;
-    };
-
-    /**
-    * Removes the new line between `row` and the row immediately following it. This method also triggers the `'change'` event.
-    * @param {Number} row The row to check
-    *
-    **/
-    this.removeNewLine = function(row) {
-        var firstLine = this.getLine(row);
-        var secondLine = this.getLine(row+1);
-
-        var range = new Range(row, firstLine.length, row+1, 0);
-        var line = firstLine + secondLine;
-
-        this.$lines.splice(row, 2, line);
-
-        var delta = {
-            action: "removeText",
-            range: range,
-            text: this.getNewLineCharacter()
-        };
-        this._emit("change", { data: delta });
-    };
-
-    /**
-    * Replaces a range in the document with the new `text`.
-    * @param {Range} range A specified Range to replace
-    * @param {String} text The new text to use as a replacement
-    * @returns {Object} Returns an object containing the final row and column, like this:
-    *     {row: endRow, column: 0}
-    * If the text and range are empty, this function returns an object containing the current `range.start` value.
-    * If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value.
-    *
-    **/
-    this.replace = function(range, text) {
-        if (!range instanceof Range)
-            range = Range.fromPoints(range.start, range.end);
-        if (text.length == 0 && range.isEmpty())
-            return range.start;
-
-        // Shortcut: If the text we want to insert is the same as it is already
-        // in the document, we don't have to replace anything.
-        if (text == this.getTextRange(range))
-            return range.end;
-
-        this.remove(range);
-        if (text) {
-            var end = this.insert(range.start, text);
-        }
-        else {
-            end = range.start;
-        }
-
-        return end;
-    };
-
-    /**
-    * Applies all the changes previously accumulated. These can be either `'includeText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`.
-    **/
-    this.applyDeltas = function(deltas) {
-        for (var i=0; i<deltas.length; i++) {
-            var delta = deltas[i];
-            var range = Range.fromPoints(delta.range.start, delta.range.end);
-
-            if (delta.action == "insertLines")
-                this.insertLines(range.start.row, delta.lines);
-            else if (delta.action == "insertText")
-                this.insert(range.start, delta.text);
-            else if (delta.action == "removeLines")
-                this._removeLines(range.start.row, range.end.row - 1);
-            else if (delta.action == "removeText")
-                this.remove(range);
-        }
-    };
-
-    /**
-    * Reverts any changes previously applied. These can be either `'includeText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`.
-    **/
-    this.revertDeltas = function(deltas) {
-        for (var i=deltas.length-1; i>=0; i--) {
-            var delta = deltas[i];
-
-            var range = Range.fromPoints(delta.range.start, delta.range.end);
-
-            if (delta.action == "insertLines")
-                this._removeLines(range.start.row, range.end.row - 1);
-            else if (delta.action == "insertText")
-                this.remove(range);
-            else if (delta.action == "removeLines")
-                this._insertLines(range.start.row, delta.lines);
-            else if (delta.action == "removeText")
-                this.insert(range.start, delta.text);
-        }
-    };
-
-    /**
-     * Converts an index position in a document to a `{row, column}` object.
-     *
-     * Index refers to the "absolute position" of a character in the document. For example:
-     *
-     * ```javascript
-     * var x = 0; // 10 characters, plus one for newline
-     * var y = -1;
-     * ```
-     * 
-     * Here, `y` is an index 15: 11 characters for the first row, and 5 characters until `y` in the second.
-     *
-     * @param {Number} index An index to convert
-     * @param {Number} startRow=0 The row from which to start the conversion
-     * @returns {Object} A `{row, column}` object of the `index` position
-     */
-    this.indexToPosition = function(index, startRow) {
-        var lines = this.$lines || this.getAllLines();
-        var newlineLength = this.getNewLineCharacter().length;
-        for (var i = startRow || 0, l = lines.length; i < l; i++) {
-            index -= lines[i].length + newlineLength;
-            if (index < 0)
-                return {row: i, column: index + lines[i].length + newlineLength};
-        }
-        return {row: l-1, column: lines[l-1].length};
-    };
-
-    /**
-     * Converts the `{row, column}` position in a document to the character's index.
-     *
-     * Index refers to the "absolute position" of a character in the document. For example:
-     *
-     * ```javascript
-     * var x = 0; // 10 characters, plus one for newline
-     * var y = -1;
-     * ```
-     * 
-     * Here, `y` is an index 15: 11 characters for the first row, and 5 characters until `y` in the second.
-     *
-     * @param {Object} pos The `{row, column}` to convert
-     * @param {Number} startRow=0 The row from which to start the conversion
-     * @returns {Number} The index position in the document
-     */
-    this.positionToIndex = function(pos, startRow) {
-        var lines = this.$lines || this.getAllLines();
-        var newlineLength = this.getNewLineCharacter().length;
-        var index = 0;
-        var row = Math.min(pos.row, lines.length);
-        for (var i = startRow || 0; i < row; ++i)
-            index += lines[i].length + newlineLength;
-
-        return index + pos.column;
-    };
-
-}).call(Document.prototype);
-
-exports.Document = Document;
-});

+ 0 - 306
services/web/public/js/ace/document_test.js

@@ -1,306 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-    require("./test/mockdom");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var Document = require("./document").Document;
-var Range = require("./range").Range;
-var assert = require("./test/assertions");
-
-module.exports = {
-
-    "test: insert text in line" : function() {
-        var doc = new Document(["12", "34"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.insert({row: 0, column: 1}, "juhu");
-        assert.equal(doc.getValue(), ["1juhu2", "34"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["12", "34"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["1juhu2", "34"].join("\n"));
-    },
-
-    "test: insert new line" : function() {
-        var doc = new Document(["12", "34"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.insertNewLine({row: 0, column: 1});
-        assert.equal(doc.getValue(), ["1", "2", "34"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["12", "34"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["1", "2", "34"].join("\n"));
-    },
-
-    "test: insert lines at the beginning" : function() {
-        var doc = new Document(["12", "34"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.insertLines(0, ["aa", "bb"]);
-        assert.equal(doc.getValue(), ["aa", "bb", "12", "34"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["12", "34"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["aa", "bb", "12", "34"].join("\n"));
-    },
-
-    "test: insert lines at the end" : function() {
-        var doc = new Document(["12", "34"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.insertLines(2, ["aa", "bb"]);
-        assert.equal(doc.getValue(), ["12", "34", "aa", "bb"].join("\n"));
-    },
-
-    "test: insert lines in the middle" : function() {
-        var doc = new Document(["12", "34"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.insertLines(1, ["aa", "bb"]);
-        assert.equal(doc.getValue(), ["12", "aa", "bb", "34"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["12", "34"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["12", "aa", "bb", "34"].join("\n"));
-    },
-
-    "test: insert multi line string at the start" : function() {
-        var doc = new Document(["12", "34"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.insert({row: 0, column: 0}, "aa\nbb\ncc");
-        assert.equal(doc.getValue(), ["aa", "bb", "cc12", "34"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["12", "34"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["aa", "bb", "cc12", "34"].join("\n"));
-    },
-
-    "test: insert multi line string at the end" : function() {
-        var doc = new Document(["12", "34"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.insert({row: 2, column: 0}, "aa\nbb\ncc");
-        assert.equal(doc.getValue(), ["12", "34aa", "bb", "cc"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["12", "34"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["12", "34aa", "bb", "cc"].join("\n"));
-    },
-
-    "test: insert multi line string in the middle" : function() {
-        var doc = new Document(["12", "34"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.insert({row: 0, column: 1}, "aa\nbb\ncc");
-        assert.equal(doc.getValue(), ["1aa", "bb", "cc2", "34"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["12", "34"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["1aa", "bb", "cc2", "34"].join("\n"));
-    },
-
-    "test: delete in line" : function() {
-        var doc = new Document(["1234", "5678"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.remove(new Range(0, 1, 0, 3));
-        assert.equal(doc.getValue(), ["14", "5678"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["1234", "5678"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["14", "5678"].join("\n"));
-    },
-
-    "test: delete new line" : function() {
-        var doc = new Document(["1234", "5678"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.remove(new Range(0, 4, 1, 0));
-        assert.equal(doc.getValue(), ["12345678"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["1234", "5678"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["12345678"].join("\n"));
-    },
-
-    "test: delete multi line range line" : function() {
-        var doc = new Document(["1234", "5678", "abcd"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.remove(new Range(0, 2, 2, 2));
-        assert.equal(doc.getValue(), ["12cd"].join("\n"));
-
-        var d = deltas.concat();
-        doc.revertDeltas(d);
-        assert.equal(doc.getValue(), ["1234", "5678", "abcd"].join("\n"));
-
-        doc.applyDeltas(d);
-        assert.equal(doc.getValue(), ["12cd"].join("\n"));
-    },
-
-    "test: delete full lines" : function() {
-        var doc = new Document(["1234", "5678", "abcd"]);
-
-        var deltas = [];
-        doc.on("change", function(e) { deltas.push(e.data); });
-
-        doc.remove(new Range(1, 0, 3, 0));
-        assert.equal(doc.getValue(), ["1234", ""].join("\n"));
-    },
-
-    "test: remove lines should return the removed lines" : function() {
-        var doc = new Document(["1234", "5678", "abcd"]);
-
-        var removed = doc.removeLines(1, 2);
-        assert.equal(removed.join("\n"), ["5678", "abcd"].join("\n"));
-    },
-
-    "test: should handle unix style new lines" : function() {
-        var doc = new Document(["1", "2", "3"]);
-        assert.equal(doc.getValue(), ["1", "2", "3"].join("\n"));
-    },
-
-    "test: should handle windows style new lines" : function() {
-        var doc = new Document(["1", "2", "3"].join("\r\n"));
-
-        doc.setNewLineMode("unix");
-        assert.equal(doc.getValue(), ["1", "2", "3"].join("\n"));
-    },
-
-    "test: set new line mode to 'windows' should use '\\r\\n' as new lines": function() {
-        var doc = new Document(["1", "2", "3"].join("\n"));
-        doc.setNewLineMode("windows");
-        assert.equal(doc.getValue(), ["1", "2", "3"].join("\r\n"));
-    },
-
-    "test: set new line mode to 'unix' should use '\\n' as new lines": function() {
-        var doc = new Document(["1", "2", "3"].join("\r\n"));
-
-        doc.setNewLineMode("unix");
-        assert.equal(doc.getValue(), ["1", "2", "3"].join("\n"));
-    },
-
-    "test: set new line mode to 'auto' should detect the incoming nl type": function() {
-        var doc = new Document(["1", "2", "3"].join("\n"));
-
-        doc.setNewLineMode("auto");
-        assert.equal(doc.getValue(), ["1", "2", "3"].join("\n"));
-
-        var doc = new Document(["1", "2", "3"].join("\r\n"));
-
-        doc.setNewLineMode("auto");
-        assert.equal(doc.getValue(), ["1", "2", "3"].join("\r\n"));
-
-        doc.replace(new Range(0, 0, 2, 1), ["4", "5", "6"].join("\n"));
-        assert.equal(["4", "5", "6"].join("\n"), doc.getValue());
-    },
-
-    "test: set value": function() {
-        var doc = new Document("1");
-        assert.equal("1", doc.getValue());
-
-        doc.setValue(doc.getValue());
-        assert.equal("1", doc.getValue());
-
-        var doc = new Document("1\n2");
-        assert.equal("1\n2", doc.getValue());
-
-        doc.setValue(doc.getValue());
-        assert.equal("1\n2", doc.getValue());
-    },
-
-    "test: empty document has to contain one line": function() {
-        var doc = new Document("");
-        assert.equal(doc.$lines.length, 1);
-    }
-};
-
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec()
-}

+ 0 - 2541
services/web/public/js/ace/edit_session.js

@@ -1,2541 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var oop = require("./lib/oop");
-var lang = require("./lib/lang");
-var config = require("./config");
-var EventEmitter = require("./lib/event_emitter").EventEmitter;
-var Selection = require("./selection").Selection;
-var TextMode = require("./mode/text").Mode;
-var Range = require("./range").Range;
-var Document = require("./document").Document;
-var BackgroundTokenizer = require("./background_tokenizer").BackgroundTokenizer;
-var SearchHighlight = require("./search_highlight").SearchHighlight;
-
-/**
- * Stores all the data about [[Editor `Editor`]] state providing easy way to change editors state.
- *
- * `EditSession` can be attached to only one [[Document `Document`]]. Same `Document` can be attached to several `EditSession`s.
- * @class EditSession
- **/
-
-//{ events
-/**
- *
- * Emitted when the document changes.
- * @event change
- * @param {Object} e An object containing a `delta` of information about the change.
- **/
-/**
- * Emitted when the tab size changes, via [[EditSession.setTabSize]].
- *
- * @event changeTabSize
- **/
-/**
- * Emitted when the ability to overwrite text changes, via [[EditSession.setOverwrite]].
- *
- * @event changeOverwrite
- **/
-/**
- * Emitted when the gutter changes, either by setting or removing breakpoints, or when the gutter decorations change.
- *
- * @event changeBreakpoint
- **/
-/**
- * Emitted when a front marker changes.
- *
- * @event changeFrontMarker
- **/
-/**
- * Emitted when a back marker changes.
- *
- * @event changeBackMarker
- **/
-/**
- * Emitted when an annotation changes, like through [[EditSession.setAnnotations]].
- *
- * @event changeAnnotation
- **/
-/**
- * Emitted when a background tokenizer asynchronously processes new rows.
- * @event tokenizerUpdate
- *
- * @param {Object} e An object containing one property, `"data"`, that contains information about the changing rows
- *
- **/
-/**
- * Emitted when the current mode changes.
- *
- * @event changeMode
- *
- **/
-/**
- * Emitted when the wrap mode changes.
- *
- * @event changeWrapMode
- *
- **/
-/**
- * Emitted when the wrapping limit changes.
- *
- * @event changeWrapLimit
- *
- **/
-/**
- * Emitted when a code fold is added or removed.
- *
- * @event changeFold
- *
- **/
- /**
- * Emitted when the scroll top changes.
- * @event changeScrollTop
- *
- * @param {Number} scrollTop The new scroll top value
- **/
-/**
- * Emitted when the scroll left changes.
- * @event changeScrollLeft
- *
- * @param {Number} scrollLeft The new scroll left value
- **/
-//}
-
-/**
- *
- * Sets up a new `EditSession` and associates it with the given `Document` and `TextMode`.
- * @param {Document | String} text [If `text` is a `Document`, it associates the `EditSession` with it. Otherwise, a new `Document` is created, with the initial text]{: #textParam}
- * @param {TextMode} mode [The inital language mode to use for the document]{: #modeParam}
- *
- * @constructor
- **/
-
-var EditSession = function(text, mode) {
-    this.$breakpoints = [];
-    this.$decorations = [];
-    this.$frontMarkers = {};
-    this.$backMarkers = {};
-    this.$markerId = 1;
-    this.$undoSelect = true;
-
-    this.$foldData = [];
-    this.$foldData.toString = function() {
-        return this.join("\n");
-    }
-    this.on("changeFold", this.onChangeFold.bind(this));
-    this.$onChange = this.onChange.bind(this);
-
-    if (typeof text != "object" || !text.getLine)
-        text = new Document(text);
-
-    this.setDocument(text);
-    this.selection = new Selection(this);
-
-    config.resetOptions(this);
-    this.setMode(mode);
-    config._emit("session", this);
-};
-
-
-(function() {
-
-    oop.implement(this, EventEmitter);
-
-    /**
-     * Sets the `EditSession` to point to a new `Document`. If a `BackgroundTokenizer` exists, it also points to `doc`.
-     *
-     * @param {Document} doc The new `Document` to use
-     *
-     **/
-    this.setDocument = function(doc) {
-        if (this.doc)
-            this.doc.removeListener("change", this.$onChange);
-
-        this.doc = doc;
-        doc.on("change", this.$onChange);
-
-        if (this.bgTokenizer)
-            this.bgTokenizer.setDocument(this.getDocument());
-
-        this.resetCaches();
-    };
-
-    /**
-     * Returns the `Document` associated with this session.
-     * @return {Document}
-     **/
-    this.getDocument = function() {
-        return this.doc;
-    };
-
-    /**
-     * @param {Number} row The row to work with
-     *
-     **/
-    this.$resetRowCache = function(docRow) {
-        if (!docRow) {
-            this.$docRowCache = [];
-            this.$screenRowCache = [];
-            return;
-        }
-        var l = this.$docRowCache.length;
-        var i = this.$getRowCacheIndex(this.$docRowCache, docRow) + 1;
-        if (l > i) {
-            this.$docRowCache.splice(i, l);
-            this.$screenRowCache.splice(i, l);
-        }
-    };
-
-    this.$getRowCacheIndex = function(cacheArray, val) {
-        var low = 0;
-        var hi = cacheArray.length - 1;
-
-        while (low <= hi) {
-            var mid = (low + hi) >> 1;
-            var c = cacheArray[mid];
-
-            if (val > c)
-                low = mid + 1;
-            else if (val < c)
-                hi = mid - 1;
-            else
-                return mid;
-        }
-
-        return low -1;
-    };
-
-    this.resetCaches = function() {
-        this.$modified = true;
-        this.$wrapData = [];
-        this.$rowLengthCache = [];
-        this.$resetRowCache(0);
-        if (this.bgTokenizer)
-            this.bgTokenizer.start(0);
-    };
-
-    this.onChangeFold = function(e) {
-        var fold = e.data;
-        this.$resetRowCache(fold.start.row);
-    };
-
-    this.onChange = function(e) {
-        var delta = e.data;
-        this.$modified = true;
-
-        this.$resetRowCache(delta.range.start.row);
-
-        var removedFolds = this.$updateInternalDataOnChange(e);
-        if (!this.$fromUndo && this.$undoManager && !delta.ignore) {
-            this.$deltasDoc.push(delta);
-            if (removedFolds && removedFolds.length != 0) {
-                this.$deltasFold.push({
-                    action: "removeFolds",
-                    folds:  removedFolds
-                });
-            }
-
-            this.$informUndoManager.schedule();
-        }
-
-        this.bgTokenizer.$updateOnChange(delta);
-        this._emit("change", e);
-    };
-
-    /**
-    * Sets the session text.
-    * @param {String} text The new text to place
-    *
-    *
-    *
-    **/
-    this.setValue = function(text) {
-        this.doc.setValue(text);
-        this.selection.moveCursorTo(0, 0);
-        this.selection.clearSelection();
-
-        this.$resetRowCache(0);
-        this.$deltas = [];
-        this.$deltasDoc = [];
-        this.$deltasFold = [];
-        this.getUndoManager().reset();
-    };
-
-    /**
-    * Returns the current [[Document `Document`]] as a string.
-    * @method toString
-    * @returns {String}
-    * @alias EditSession.getValue
-    *
-    **/
-
-    /**
-    * Returns the current [[Document `Document`]] as a string.
-    * @method getValue
-    * @returns {String}
-    * @alias EditSession.toString
-    **/
-    this.getValue =
-    this.toString = function() {
-        return this.doc.getValue();
-    };
-
-    /**
-    * Returns the string of the current selection.
-    **/
-    this.getSelection = function() {
-        return this.selection;
-    };
-
-    /**
-     * {:BackgroundTokenizer.getState}
-     * @param {Number} row The row to start at
-     *
-     * @related BackgroundTokenizer.getState
-     **/
-    this.getState = function(row) {
-        return this.bgTokenizer.getState(row);
-    };
-
-    /**
-     * Starts tokenizing at the row indicated. Returns a list of objects of the tokenized rows.
-     * @param {Number} row The row to start at
-     *
-     *
-     *
-     **/
-    this.getTokens = function(row) {
-        return this.bgTokenizer.getTokens(row);
-    };
-
-    /**
-    * Returns an object indicating the token at the current row. The object has two properties: `index` and `start`.
-    * @param {Number} row The row number to retrieve from
-    * @param {Number} column The column number to retrieve from
-    *
-    *
-    **/
-    this.getTokenAt = function(row, column) {
-        var tokens = this.bgTokenizer.getTokens(row);
-        var token, c = 0;
-        if (column == null) {
-            i = tokens.length - 1;
-            c = this.getLine(row).length;
-        } else {
-            for (var i = 0; i < tokens.length; i++) {
-                c += tokens[i].value.length;
-                if (c >= column)
-                    break;
-            }
-        }
-        token = tokens[i];
-        if (!token)
-            return null;
-        token.index = i;
-        token.start = c - token.value.length;
-        return token;
-    };
-
-    /**
-    * Sets the undo manager.
-    * @param {UndoManager} undoManager The new undo manager
-    *
-    *
-    **/
-    this.setUndoManager = function(undoManager) {
-        this.$undoManager = undoManager;
-        this.$deltas = [];
-        this.$deltasDoc = [];
-        this.$deltasFold = [];
-
-        if (this.$informUndoManager)
-            this.$informUndoManager.cancel();
-
-        if (undoManager) {
-            var self = this;
-
-            this.$syncInformUndoManager = function() {
-                self.$informUndoManager.cancel();
-
-                if (self.$deltasFold.length) {
-                    self.$deltas.push({
-                        group: "fold",
-                        deltas: self.$deltasFold
-                    });
-                    self.$deltasFold = [];
-                }
-
-                if (self.$deltasDoc.length) {
-                    self.$deltas.push({
-                        group: "doc",
-                        deltas: self.$deltasDoc
-                    });
-                    self.$deltasDoc = [];
-                }
-
-                if (self.$deltas.length > 0) {
-                    undoManager.execute({
-                        action: "aceupdate",
-                        args: [self.$deltas, self],
-                        merge: self.mergeUndoDeltas
-                    });
-                }
-                self.mergeUndoDeltas = false;
-                self.$deltas = [];
-            }
-            this.$informUndoManager = lang.delayedCall(this.$syncInformUndoManager);
-        }
-    };
-
-    /**
-     * starts a new group in undo history
-     **/
-    this.markUndoGroup = function() {
-        if (this.$syncInformUndoManager)
-            this.$syncInformUndoManager();
-    };
-    
-    this.$defaultUndoManager = {
-        undo: function() {},
-        redo: function() {},
-        reset: function() {}
-    };
-
-    /**
-    * Returns the current undo manager.
-    **/
-    this.getUndoManager = function() {
-        return this.$undoManager || this.$defaultUndoManager;
-    };
-
-    /**
-    * Returns the current value for tabs. If the user is using soft tabs, this will be a series of spaces (defined by [[EditSession.getTabSize `getTabSize()`]]); otherwise it's simply `'\t'`.
-    **/
-    this.getTabString = function() {
-        if (this.getUseSoftTabs()) {
-            return lang.stringRepeat(" ", this.getTabSize());
-        } else {
-            return "\t";
-        }
-    };
-
-    /**
-    /**
-    * Pass `true` to enable the use of soft tabs. Soft tabs means you're using spaces instead of the tab character (`'\t'`).
-    * @param {Boolean} useSoftTabs Value indicating whether or not to use soft tabs
-    **/
-    this.setUseSoftTabs = function(val) {
-        this.setOption("useSoftTabs", val);
-    };
-    /**
-    * Returns `true` if soft tabs are being used, `false` otherwise.
-    * @returns {Boolean}
-    **/
-    this.getUseSoftTabs = function() {
-        // todo might need more general way for changing settings from mode, but this is ok for now
-        return this.$useSoftTabs && !this.$mode.$indentWithTabs;
-    };
-    /**
-    * Set the number of spaces that define a soft tab; for example, passing in `4` transforms the soft tabs to be equivalent to four spaces. This function also emits the `changeTabSize` event.
-    * @param {Number} tabSize The new tab size
-    **/
-    this.setTabSize = function(tabSize) {
-        this.setOption("tabSize", tabSize)
-    };
-    /**
-    * Returns the current tab size.
-    **/
-    this.getTabSize = function() {
-        return this.$tabSize;
-    };
-
-    /**
-    * Returns `true` if the character at the position is a soft tab.
-    * @param {Object} position The position to check
-    *
-    *
-    **/
-    this.isTabStop = function(position) {
-        return this.$useSoftTabs && (position.column % this.$tabSize == 0);
-    };
-
-    this.$overwrite = false;
-    /**
-    * Pass in `true` to enable overwrites in your session, or `false` to disable.
-    *
-    * If overwrites is enabled, any text you enter will type over any text after it. If the value of `overwrite` changes, this function also emites the `changeOverwrite` event.
-    *
-    * @param {Boolean} overwrite Defines wheter or not to set overwrites
-    *
-    *
-    **/
-    this.setOverwrite = function(overwrite) {
-        this.setOption("overwrite", overwrite)
-    };
-
-    /**
-    * Returns `true` if overwrites are enabled; `false` otherwise.
-    **/
-    this.getOverwrite = function() {
-        return this.$overwrite;
-    };
-
-    /**
-    * Sets the value of overwrite to the opposite of whatever it currently is.
-    **/
-    this.toggleOverwrite = function() {
-        this.setOverwrite(!this.$overwrite);
-    };
-
-    /**
-    * Adds `className` to the `row`, to be used for CSS stylings and whatnot.
-    * @param {Number} row The row number
-    * @param {String} className The class to add
-    *
-    *
-    **/
-    this.addGutterDecoration = function(row, className) {
-        if (!this.$decorations[row])
-            this.$decorations[row] = "";
-        this.$decorations[row] += " " + className;
-        this._emit("changeBreakpoint", {});
-    };
-
-    /**
-    * Removes `className` from the `row`.
-    * @param {Number} row The row number
-    * @param {String} className The class to add
-    *
-    *
-    **/
-    this.removeGutterDecoration = function(row, className) {
-        this.$decorations[row] = (this.$decorations[row] || "").replace(" " + className, "");
-        this._emit("changeBreakpoint", {});
-    };
-
-    /**
-    * Returns an array of numbers, indicating which rows have breakpoints.
-    * @returns {[Number]}
-    **/
-    this.getBreakpoints = function() {
-        return this.$breakpoints;
-    };
-
-    /**
-    * Sets a breakpoint on every row number given by `rows`. This function also emites the `'changeBreakpoint'` event.
-    * @param {Array} rows An array of row indices
-    *
-    *
-    *
-    **/
-    this.setBreakpoints = function(rows) {
-        this.$breakpoints = [];
-        for (var i=0; i<rows.length; i++) {
-            this.$breakpoints[rows[i]] = "ace_breakpoint";
-        }
-        this._emit("changeBreakpoint", {});
-    };
-
-    /**
-    * Removes all breakpoints on the rows. This function also emites the `'changeBreakpoint'` event.
-    **/
-    this.clearBreakpoints = function() {
-        this.$breakpoints = [];
-        this._emit("changeBreakpoint", {});
-    };
-
-    /**
-    * Sets a breakpoint on the row number given by `rows`. This function also emites the `'changeBreakpoint'` event.
-    * @param {Number} row A row index
-    * @param {String} className Class of the breakpoint
-    *
-    *
-    **/
-    this.setBreakpoint = function(row, className) {
-        if (className === undefined)
-            className = "ace_breakpoint";
-        if (className)
-            this.$breakpoints[row] = className;
-        else
-            delete this.$breakpoints[row];
-        this._emit("changeBreakpoint", {});
-    };
-
-    /**
-    * Removes a breakpoint on the row number given by `rows`. This function also emites the `'changeBreakpoint'` event.
-    * @param {Number} row A row index
-    *
-    *
-    **/
-    this.clearBreakpoint = function(row) {
-        delete this.$breakpoints[row];
-        this._emit("changeBreakpoint", {});
-    };
-
-    /**
-    * Adds a new marker to the given `Range`. If `inFront` is `true`, a front marker is defined, and the `'changeFrontMarker'` event fires; otherwise, the `'changeBackMarker'` event fires.
-    * @param {Range} range Define the range of the marker
-    * @param {String} clazz Set the CSS class for the marker
-    * @param {Function | String} type Identify the type of the marker
-    * @param {Boolean} inFront Set to `true` to establish a front marker
-    *
-    *
-    * @return {Number} The new marker id
-    **/
-    this.addMarker = function(range, clazz, type, inFront) {
-        var id = this.$markerId++;
-
-        var marker = {
-            range : range,
-            type : type || "line",
-            renderer: typeof type == "function" ? type : null,
-            clazz : clazz,
-            inFront: !!inFront,
-            id: id
-        }
-
-        if (inFront) {
-            this.$frontMarkers[id] = marker;
-            this._emit("changeFrontMarker")
-        } else {
-            this.$backMarkers[id] = marker;
-            this._emit("changeBackMarker")
-        }
-
-        return id;
-    };
-
-    /**
-     * Adds a dynamic marker to the session.
-     * @param {Object} marker object with update method
-     * @param {Boolean} inFront Set to `true` to establish a front marker
-     *
-     *
-     * @return {Object} The added marker
-     **/
-    this.addDynamicMarker = function(marker, inFront) {
-        if (!marker.update)
-            return;
-        var id = this.$markerId++;
-        marker.id = id;
-        marker.inFront = !!inFront;
-
-        if (inFront) {
-            this.$frontMarkers[id] = marker;
-            this._emit("changeFrontMarker")
-        } else {
-            this.$backMarkers[id] = marker;
-            this._emit("changeBackMarker")
-        }
-
-        return marker;
-    };
-
-    /**
-    * Removes the marker with the specified ID. If this marker was in front, the `'changeFrontMarker'` event is emitted. If the marker was in the back, the `'changeBackMarker'` event is emitted.
-    * @param {Number} markerId A number representing a marker
-    *
-    *
-    *
-    **/
-    this.removeMarker = function(markerId) {
-        var marker = this.$frontMarkers[markerId] || this.$backMarkers[markerId];
-        if (!marker)
-            return;
-
-        var markers = marker.inFront ? this.$frontMarkers : this.$backMarkers;
-        if (marker) {
-            delete (markers[markerId]);
-            this._emit(marker.inFront ? "changeFrontMarker" : "changeBackMarker");
-        }
-    };
-
-    /**
-    * Returns an array containing the IDs of all the markers, either front or back.
-    * @param {Boolean} inFront If `true`, indicates you only want front markers; `false` indicates only back markers
-    *
-    * @returns {Array}
-    **/
-    this.getMarkers = function(inFront) {
-        return inFront ? this.$frontMarkers : this.$backMarkers;
-    };
-
-    this.highlight = function(re) {
-        if (!this.$searchHighlight) {
-            var highlight = new SearchHighlight(null, "ace_selected-word", "text");
-            this.$searchHighlight = this.addDynamicMarker(highlight);
-        }
-        this.$searchHighlight.setRegexp(re);
-    }
-
-    // experimental
-    this.highlightLines = function(startRow, endRow, clazz, inFront) {
-        if (typeof endRow != "number") {
-            clazz = endRow;
-            endRow = startRow;
-        }
-        if (!clazz)
-            clazz = "ace_step";
-
-        var range = new Range(startRow, 0, endRow, Infinity);
-        range.id = this.addMarker(range, clazz, "fullLine", inFront);
-        return range;
-    };
-
-    /*
-     * Error:
-     *  {
-     *    row: 12,
-     *    column: 2, //can be undefined
-     *    text: "Missing argument",
-     *    type: "error" // or "warning" or "info"
-     *  }
-     */
-    /**
-    * Sets annotations for the `EditSession`. This functions emits the `'changeAnnotation'` event.
-    * @param {Array} annotations A list of annotations
-    *
-    **/
-    this.setAnnotations = function(annotations) {
-        this.$annotations = annotations;
-        this._emit("changeAnnotation", {});
-    };
-
-    /**
-    * Returns the annotations for the `EditSession`.
-    * @returns {Array}
-    **/
-    this.getAnnotations = function() {
-        return this.$annotations || [];
-    };
-
-    /**
-    * Clears all the annotations for this session. This function also triggers the `'changeAnnotation'` event.
-    **/
-    this.clearAnnotations = function() {
-        this.setAnnotations([]);
-    };
-
-    /**
-    * If `text` contains either the newline (`\n`) or carriage-return ('\r') characters, `$autoNewLine` stores that value.
-    * @param {String} text A block of text
-    *
-    *
-    **/
-    this.$detectNewLine = function(text) {
-        var match = text.match(/^.*?(\r?\n)/m);
-        if (match) {
-            this.$autoNewLine = match[1];
-        } else {
-            this.$autoNewLine = "\n";
-        }
-    };
-
-    /**
-    * Given a starting row and column, this method returns the `Range` of the first word boundary it finds.
-    * @param {Number} row The row to start at
-    * @param {Number} column The column to start at
-    *
-    * @returns {Range}
-    **/
-    this.getWordRange = function(row, column) {
-        var line = this.getLine(row);
-
-        var inToken = false;
-        if (column > 0)
-            inToken = !!line.charAt(column - 1).match(this.tokenRe);
-
-        if (!inToken)
-            inToken = !!line.charAt(column).match(this.tokenRe);
-
-        if (inToken)
-            var re = this.tokenRe;
-        else if (/^\s+$/.test(line.slice(column-1, column+1)))
-            var re = /\s/;
-        else
-            var re = this.nonTokenRe;
-
-        var start = column;
-        if (start > 0) {
-            do {
-                start--;
-            }
-            while (start >= 0 && line.charAt(start).match(re));
-            start++;
-        }
-
-        var end = column;
-        while (end < line.length && line.charAt(end).match(re)) {
-            end++;
-        }
-
-        return new Range(row, start, row, end);
-    };
-
-    /**
-    * Gets the range of a word, including its right whitespace.
-    * @param {Number} row The row number to start from
-    * @param {Number} column The column number to start from
-    *
-    * @return {Range}
-    **/
-    this.getAWordRange = function(row, column) {
-        var wordRange = this.getWordRange(row, column);
-        var line = this.getLine(wordRange.end.row);
-
-        while (line.charAt(wordRange.end.column).match(/[ \t]/)) {
-            wordRange.end.column += 1;
-        }
-        return wordRange;
-    };
-
-    /**
-    * {:Document.setNewLineMode.desc}
-    * @param {String} newLineMode {:Document.setNewLineMode.param}
-    *
-    *
-    * @related Document.setNewLineMode
-    **/
-    this.setNewLineMode = function(newLineMode) {
-        this.doc.setNewLineMode(newLineMode);
-    };
-
-    /**
-    *
-    * Returns the current new line mode.
-    * @returns {String}
-    * @related Document.getNewLineMode
-    **/
-    this.getNewLineMode = function() {
-        return this.doc.getNewLineMode();
-    };
-
-    /**
-    * Identifies if you want to use a worker for the `EditSession`.
-    * @param {Boolean} useWorker Set to `true` to use a worker
-    *
-    **/
-    this.setUseWorker = function(useWorker) { this.setOption("useWorker", useWorker); };
-
-    /**
-    * Returns `true` if workers are being used.
-    **/
-    this.getUseWorker = function() { return this.$useWorker; };
-
-    /**
-    * Reloads all the tokens on the current session. This function calls [[BackgroundTokenizer.start `BackgroundTokenizer.start ()`]] to all the rows; it also emits the `'tokenizerUpdate'` event.
-    **/
-    this.onReloadTokenizer = function(e) {
-        var rows = e.data;
-        this.bgTokenizer.start(rows.first);
-        this._emit("tokenizerUpdate", e);
-    };
-
-    this.$modes = {};
-
-    /**
-    * Sets a new text mode for the `EditSession`. This method also emits the `'changeMode'` event. If a [[BackgroundTokenizer `BackgroundTokenizer`]] is set, the `'tokenizerUpdate'` event is also emitted.
-    * @param {TextMode} mode Set a new text mode
-    * @param {cb} optional callback
-    *
-    **/
-    this.$mode = null;
-    this.$modeId = null;
-    this.setMode = function(mode, cb) {
-        if (mode && typeof mode === "object") {
-            if (mode.getTokenizer)
-                return this.$onChangeMode(mode);
-            var options = mode;
-            var path = options.path;
-        } else {
-            path = mode || "ace/mode/text";
-        }
-
-        // this is needed if ace isn't on require path (e.g tests in node)
-        if (!this.$modes["ace/mode/text"])
-            this.$modes["ace/mode/text"] = new TextMode();
-
-        if (this.$modes[path] && !options) {
-            this.$onChangeMode(this.$modes[path]);
-            cb && cb();
-            return;
-        }
-        // load on demand
-        this.$modeId = path;
-        config.loadModule(["mode", path], function(m) {
-            if (this.$modeId !== path)
-                return cb && cb();
-            if (this.$modes[path] && !options)
-                return this.$onChangeMode(this.$modes[path]);
-            if (m && m.Mode) {
-                m = new m.Mode(options);
-                if (!options) {
-                    this.$modes[path] = m;
-                    m.$id = path;
-                }
-                this.$onChangeMode(m);
-                cb && cb();
-            }
-        }.bind(this));
-
-        // set mode to text until loading is finished
-        if (!this.$mode)
-            this.$onChangeMode(this.$modes["ace/mode/text"], true);
-    };
-
-    this.$onChangeMode = function(mode, $isPlaceholder) {
-        if (!$isPlaceholder)
-            this.$modeId = mode.$id;
-        if (this.$mode === mode) 
-            return;
-
-        this.$mode = mode;
-
-        this.$stopWorker();
-
-        if (this.$useWorker)
-            this.$startWorker();
-
-        var tokenizer = mode.getTokenizer();
-
-        if(tokenizer.addEventListener !== undefined) {
-            var onReloadTokenizer = this.onReloadTokenizer.bind(this);
-            tokenizer.addEventListener("update", onReloadTokenizer);
-        }
-
-        if (!this.bgTokenizer) {
-            this.bgTokenizer = new BackgroundTokenizer(tokenizer);
-            var _self = this;
-            this.bgTokenizer.addEventListener("update", function(e) {
-                _self._emit("tokenizerUpdate", e);
-            });
-        } else {
-            this.bgTokenizer.setTokenizer(tokenizer);
-        }
-
-        this.bgTokenizer.setDocument(this.getDocument());
-
-        this.tokenRe = mode.tokenRe;
-        this.nonTokenRe = mode.nonTokenRe;
-
-        
-        if (!$isPlaceholder) {
-            this.$options.wrapMethod.set.call(this, this.$wrapMethod);
-            this.$setFolding(mode.foldingRules);
-            this.bgTokenizer.start(0);
-            this._emit("changeMode");
-        }
-    };
-
-
-    this.$stopWorker = function() {
-        if (this.$worker)
-            this.$worker.terminate();
-
-        this.$worker = null;
-    };
-
-    this.$startWorker = function() {
-        if (typeof Worker !== "undefined" && !require.noWorker) {
-            try {
-                this.$worker = this.$mode.createWorker(this);
-            } catch (e) {
-                console.log("Could not load worker");
-                console.log(e);
-                this.$worker = null;
-            }
-        }
-        else
-            this.$worker = null;
-    };
-
-    /**
-    * Returns the current text mode.
-    * @returns {TextMode} The current text mode
-    **/
-    this.getMode = function() {
-        return this.$mode;
-    };
-
-    this.$scrollTop = 0;
-    /**
-    * This function sets the scroll top value. It also emits the `'changeScrollTop'` event.
-    * @param {Number} scrollTop The new scroll top value
-    *
-    **/
-    this.setScrollTop = function(scrollTop) {
-        // TODO: should we force integer lineheight instead? scrollTop = Math.round(scrollTop); 
-        if (this.$scrollTop === scrollTop || isNaN(scrollTop))
-            return;
-
-        this.$scrollTop = scrollTop;
-        this._signal("changeScrollTop", scrollTop);
-    };
-
-    /**
-    * [Returns the value of the distance between the top of the editor and the topmost part of the visible content.]{: #EditSession.getScrollTop}
-    * @returns {Number}
-    **/
-    this.getScrollTop = function() {
-        return this.$scrollTop;
-    };
-
-    this.$scrollLeft = 0;
-    /**
-    * [Sets the value of the distance between the left of the editor and the leftmost part of the visible content.]{: #EditSession.setScrollLeft}
-    **/
-    this.setScrollLeft = function(scrollLeft) {
-        // scrollLeft = Math.round(scrollLeft);
-        if (this.$scrollLeft === scrollLeft || isNaN(scrollLeft))
-            return;
-
-        this.$scrollLeft = scrollLeft;
-        this._signal("changeScrollLeft", scrollLeft);
-    };
-
-    /**
-    * [Returns the value of the distance between the left of the editor and the leftmost part of the visible content.]{: #EditSession.getScrollLeft}
-    * @returns {Number}
-    **/
-    this.getScrollLeft = function() {
-        return this.$scrollLeft;
-    };
-
-    /**
-    * Returns the width of the screen.
-    * @returns {Number}
-    **/
-    this.getScreenWidth = function() {
-        this.$computeWidth();
-        if (this.lineWidgets) 
-            return Math.max(this.getLineWidgetMaxWidth(), this.screenWidth);
-        return this.screenWidth;
-    };
-    
-    this.getLineWidgetMaxWidth = function() {
-        if (this.lineWidgetsWidth != null) return this.lineWidgetsWidth
-        var width = 0;
-        this.lineWidgets.forEach(function(w) {
-            if (w && w.screenWidth > width)
-                width = w.screenWidth;
-        });
-        return this.lineWidgetWidth = width;
-    }
-
-    this.$computeWidth = function(force) {
-        if (this.$modified || force) {
-            this.$modified = false;
-
-            if (this.$useWrapMode)
-                return this.screenWidth = this.$wrapLimit;
-
-            var lines = this.doc.getAllLines();
-            var cache = this.$rowLengthCache;
-            var longestScreenLine = 0;
-            var foldIndex = 0;
-            var foldLine = this.$foldData[foldIndex];
-            var foldStart = foldLine ? foldLine.start.row : Infinity;
-            var len = lines.length;
-
-            for (var i = 0; i < len; i++) {
-                if (i > foldStart) {
-                    i = foldLine.end.row + 1;
-                    if (i >= len)
-                        break;
-                    foldLine = this.$foldData[foldIndex++];
-                    foldStart = foldLine ? foldLine.start.row : Infinity;
-                }
-
-                if (cache[i] == null)
-                    cache[i] = this.$getStringScreenWidth(lines[i])[0];
-
-                if (cache[i] > longestScreenLine)
-                    longestScreenLine = cache[i];
-            }
-            this.screenWidth = longestScreenLine;
-        }
-    };
-
-    /**
-     * Returns a verbatim copy of the given line as it is in the document
-     * @param {Number} row The row to retrieve from
-     *
-    *
-     * @returns {String}
-    *
-    **/
-    this.getLine = function(row) {
-        return this.doc.getLine(row);
-    };
-
-    /**
-     * Returns an array of strings of the rows between `firstRow` and `lastRow`. This function is inclusive of `lastRow`.
-     * @param {Number} firstRow The first row index to retrieve
-     * @param {Number} lastRow The final row index to retrieve
-     *
-     * @returns {[String]}
-     *
-     **/
-    this.getLines = function(firstRow, lastRow) {
-        return this.doc.getLines(firstRow, lastRow);
-    };
-
-    /**
-     * Returns the number of rows in the document.
-     * @returns {Number}
-     **/
-    this.getLength = function() {
-        return this.doc.getLength();
-    };
-
-    /**
-     * {:Document.getTextRange.desc}
-     * @param {Range} range The range to work with
-     *
-     * @returns {String}
-     **/
-    this.getTextRange = function(range) {
-        return this.doc.getTextRange(range || this.selection.getRange());
-    };
-
-    /**
-     * Inserts a block of `text` and the indicated `position`.
-     * @param {Object} position The position {row, column} to start inserting at
-     * @param {String} text A chunk of text to insert
-     * @returns {Object} The position of the last line of `text`. If the length of `text` is 0, this function simply returns `position`.
-     *
-     *
-     **/
-    this.insert = function(position, text) {
-        return this.doc.insert(position, text);
-    };
-
-    /**
-     * Removes the `range` from the document.
-     * @param {Range} range A specified Range to remove
-     * @returns {Object} The new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`.
-     *
-     * @related Document.remove
-     *
-     **/
-    this.remove = function(range) {
-        return this.doc.remove(range);
-    };
-
-    /**
-     * Reverts previous changes to your document.
-     * @param {Array} deltas An array of previous changes
-     * @param {Boolean} dontSelect [If `true`, doesn't select the range of where the change occured]{: #dontSelect}
-    *
-     *
-     * @returns {Range}
-    **/
-    this.undoChanges = function(deltas, dontSelect) {
-        if (!deltas.length)
-            return;
-
-        this.$fromUndo = true;
-        var lastUndoRange = null;
-        for (var i = deltas.length - 1; i != -1; i--) {
-            var delta = deltas[i];
-            if (delta.group == "doc") {
-                this.doc.revertDeltas(delta.deltas);
-                lastUndoRange =
-                    this.$getUndoSelection(delta.deltas, true, lastUndoRange);
-            } else {
-                delta.deltas.forEach(function(foldDelta) {
-                    this.addFolds(foldDelta.folds);
-                }, this);
-            }
-        }
-        this.$fromUndo = false;
-        lastUndoRange &&
-            this.$undoSelect &&
-            !dontSelect &&
-            this.selection.setSelectionRange(lastUndoRange);
-        return lastUndoRange;
-    };
-
-    /**
-     * Re-implements a previously undone change to your document.
-     * @param {Array} deltas An array of previous changes
-     * @param {Boolean} dontSelect {:dontSelect}
-     *
-    *
-     * @returns {Range}
-    **/
-    this.redoChanges = function(deltas, dontSelect) {
-        if (!deltas.length)
-            return;
-
-        this.$fromUndo = true;
-        var lastUndoRange = null;
-        for (var i = 0; i < deltas.length; i++) {
-            var delta = deltas[i];
-            if (delta.group == "doc") {
-                this.doc.applyDeltas(delta.deltas);
-                lastUndoRange =
-                    this.$getUndoSelection(delta.deltas, false, lastUndoRange);
-            }
-        }
-        this.$fromUndo = false;
-        lastUndoRange &&
-            this.$undoSelect &&
-            !dontSelect &&
-            this.selection.setSelectionRange(lastUndoRange);
-        return lastUndoRange;
-    };
-
-    /**
-     * Enables or disables highlighting of the range where an undo occured.
-     * @param {Boolean} enable If `true`, selects the range of the reinserted change
-    *
-    **/
-    this.setUndoSelect = function(enable) {
-        this.$undoSelect = enable;
-    };
-
-    this.$getUndoSelection = function(deltas, isUndo, lastUndoRange) {
-        function isInsert(delta) {
-            var insert =
-                delta.action === "insertText" || delta.action === "insertLines";
-            return isUndo ? !insert : insert;
-        }
-
-        var delta = deltas[0];
-        var range, point;
-        var lastDeltaIsInsert = false;
-        if (isInsert(delta)) {
-            range = Range.fromPoints(delta.range.start, delta.range.end);
-            lastDeltaIsInsert = true;
-        } else {
-            range = Range.fromPoints(delta.range.start, delta.range.start);
-            lastDeltaIsInsert = false;
-        }
-
-        for (var i = 1; i < deltas.length; i++) {
-            delta = deltas[i];
-            if (isInsert(delta)) {
-                point = delta.range.start;
-                if (range.compare(point.row, point.column) == -1) {
-                    range.setStart(delta.range.start);
-                }
-                point = delta.range.end;
-                if (range.compare(point.row, point.column) == 1) {
-                    range.setEnd(delta.range.end);
-                }
-                lastDeltaIsInsert = true;
-            } else {
-                point = delta.range.start;
-                if (range.compare(point.row, point.column) == -1) {
-                    range =
-                        Range.fromPoints(delta.range.start, delta.range.start);
-                }
-                lastDeltaIsInsert = false;
-            }
-        }
-
-        // Check if this range and the last undo range has something in common.
-        // If true, merge the ranges.
-        if (lastUndoRange != null) {
-            if (Range.comparePoints(lastUndoRange.start, range.start) == 0) {
-                lastUndoRange.start.column += range.end.column - range.start.column;
-                lastUndoRange.end.column += range.end.column - range.start.column;
-            }
-
-            var cmp = lastUndoRange.compareRange(range);
-            if (cmp == 1) {
-                range.setStart(lastUndoRange.start);
-            } else if (cmp == -1) {
-                range.setEnd(lastUndoRange.end);
-            }
-        }
-
-        return range;
-    };
-
-    /**
-    * Replaces a range in the document with the new `text`.
-    *
-    * @param {Range} range A specified Range to replace
-    * @param {String} text The new text to use as a replacement
-    * @returns {Object} An object containing the final row and column, like this:
-    * ```
-    * {row: endRow, column: 0}
-    * ```
-    * If the text and range are empty, this function returns an object containing the current `range.start` value.
-    * If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value.
-    *
-    *
-    *
-    * @related Document.replace
-    *
-    *
-    **/
-    this.replace = function(range, text) {
-        return this.doc.replace(range, text);
-    };
-
-    /**
-    * Moves a range of text from the given range to the given position. `toPosition` is an object that looks like this:
-     *  ```json
-    *    { row: newRowLocation, column: newColumnLocation }
-     *  ```
-     * @param {Range} fromRange The range of text you want moved within the document
-     * @param {Object} toPosition The location (row and column) where you want to move the text to
-     * @returns {Range} The new range where the text was moved to.
-    *
-    *
-    *
-    **/
-    this.moveText = function(fromRange, toPosition, copy) {
-        var text = this.getTextRange(fromRange);
-        var folds = this.getFoldsInRange(fromRange);
-
-        var toRange = Range.fromPoints(toPosition, toPosition);
-        if (!copy) {
-            this.remove(fromRange);
-            var rowDiff = fromRange.start.row - fromRange.end.row;
-            var collDiff = rowDiff ? -fromRange.end.column : fromRange.start.column - fromRange.end.column;
-            if (collDiff) {
-                if (toRange.start.row == fromRange.end.row && toRange.start.column > fromRange.end.column)
-                    toRange.start.column += collDiff;
-                if (toRange.end.row == fromRange.end.row && toRange.end.column > fromRange.end.column)
-                    toRange.end.column += collDiff;
-            }
-            if (rowDiff && toRange.start.row >= fromRange.end.row) {
-                toRange.start.row += rowDiff;
-                toRange.end.row += rowDiff;
-            }
-        }
-
-        toRange.end = this.insert(toRange.start, text);
-        if (folds.length) {
-            var oldStart = fromRange.start;
-            var newStart = toRange.start;
-            var rowDiff = newStart.row - oldStart.row;
-            var collDiff = newStart.column - oldStart.column;
-            this.addFolds(folds.map(function(x) {
-                x = x.clone();
-                if (x.start.row == oldStart.row)
-                    x.start.column += collDiff;
-                if (x.end.row == oldStart.row)
-                    x.end.column += collDiff;
-                x.start.row += rowDiff;
-                x.end.row += rowDiff;
-                return x;
-            }));
-        }
-
-        return toRange;
-    };
-
-    /**
-    * Indents all the rows, from `startRow` to `endRow` (inclusive), by prefixing each row with the token in `indentString`.
-    *
-    * If `indentString` contains the `'\t'` character, it's replaced by whatever is defined by [[EditSession.getTabString `getTabString()`]].
-    * @param {Number} startRow Starting row
-    * @param {Number} endRow Ending row
-    * @param {String} indentString The indent token
-    *
-    *
-    **/
-    this.indentRows = function(startRow, endRow, indentString) {
-        indentString = indentString.replace(/\t/g, this.getTabString());
-        for (var row=startRow; row<=endRow; row++)
-            this.insert({row: row, column:0}, indentString);
-    };
-
-    /**
-    * Outdents all the rows defined by the `start` and `end` properties of `range`.
-    * @param {Range} range A range of rows
-    *
-    *
-    **/
-    this.outdentRows = function (range) {
-        var rowRange = range.collapseRows();
-        var deleteRange = new Range(0, 0, 0, 0);
-        var size = this.getTabSize();
-
-        for (var i = rowRange.start.row; i <= rowRange.end.row; ++i) {
-            var line = this.getLine(i);
-
-            deleteRange.start.row = i;
-            deleteRange.end.row = i;
-            for (var j = 0; j < size; ++j)
-                if (line.charAt(j) != ' ')
-                    break;
-            if (j < size && line.charAt(j) == '\t') {
-                deleteRange.start.column = j;
-                deleteRange.end.column = j + 1;
-            } else {
-                deleteRange.start.column = 0;
-                deleteRange.end.column = j;
-            }
-            this.remove(deleteRange);
-        }
-    };
-
-    this.$moveLines = function(firstRow, lastRow, dir) {
-        firstRow = this.getRowFoldStart(firstRow);
-        lastRow = this.getRowFoldEnd(lastRow);
-        if (dir < 0) {
-            var row = this.getRowFoldStart(firstRow + dir);
-            if (row < 0) return 0;
-            var diff = row-firstRow;
-        } else if (dir > 0) {
-            var row = this.getRowFoldEnd(lastRow + dir);
-            if (row > this.doc.getLength()-1) return 0;
-            var diff = row-lastRow;
-        } else {
-            firstRow = this.$clipRowToDocument(firstRow);
-            lastRow = this.$clipRowToDocument(lastRow);
-            var diff = lastRow - firstRow + 1;
-        }
-
-        var range = new Range(firstRow, 0, lastRow, Number.MAX_VALUE);
-        var folds = this.getFoldsInRange(range).map(function(x){
-            x = x.clone();
-            x.start.row += diff;
-            x.end.row += diff;
-            return x;
-        });
-
-        var lines = dir == 0
-            ? this.doc.getLines(firstRow, lastRow)
-            : this.doc.removeLines(firstRow, lastRow);
-        this.doc.insertLines(firstRow+diff, lines);
-        folds.length && this.addFolds(folds);
-        return diff;
-    };
-    /**
-    * Shifts all the lines in the document up one, starting from `firstRow` and ending at `lastRow`.
-    * @param {Number} firstRow The starting row to move up
-    * @param {Number} lastRow The final row to move up
-    * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1.
-    *
-    * @related Document.insertLines
-    *
-    **/
-    this.moveLinesUp = function(firstRow, lastRow) {
-        return this.$moveLines(firstRow, lastRow, -1);
-    };
-
-    /**
-    * Shifts all the lines in the document down one, starting from `firstRow` and ending at `lastRow`.
-    * @param {Number} firstRow The starting row to move down
-    * @param {Number} lastRow The final row to move down
-    * @returns {Number} If `firstRow` is less-than or equal to 0, this function returns 0. Otherwise, on success, it returns -1.
-    *
-    * @related Document.insertLines
-    **/
-    this.moveLinesDown = function(firstRow, lastRow) {
-        return this.$moveLines(firstRow, lastRow, 1);
-    };
-
-    /**
-    * Duplicates all the text between `firstRow` and `lastRow`.
-    * @param {Number} firstRow The starting row to duplicate
-    * @param {Number} lastRow The final row to duplicate
-    * @returns {Number} Returns the number of new rows added; in other words, `lastRow - firstRow + 1`.
-    *
-    *
-    **/
-    this.duplicateLines = function(firstRow, lastRow) {
-        return this.$moveLines(firstRow, lastRow, 0);
-    };
-
-
-    this.$clipRowToDocument = function(row) {
-        return Math.max(0, Math.min(row, this.doc.getLength()-1));
-    };
-
-    this.$clipColumnToRow = function(row, column) {
-        if (column < 0)
-            return 0;
-        return Math.min(this.doc.getLine(row).length, column);
-    };
-
-
-    this.$clipPositionToDocument = function(row, column) {
-        column = Math.max(0, column);
-
-        if (row < 0) {
-            row = 0;
-            column = 0;
-        } else {
-            var len = this.doc.getLength();
-            if (row >= len) {
-                row = len - 1;
-                column = this.doc.getLine(len-1).length;
-            } else {
-                column = Math.min(this.doc.getLine(row).length, column);
-            }
-        }
-
-        return {
-            row: row,
-            column: column
-        };
-    };
-
-    this.$clipRangeToDocument = function(range) {
-        if (range.start.row < 0) {
-            range.start.row = 0;
-            range.start.column = 0;
-        } else {
-            range.start.column = this.$clipColumnToRow(
-                range.start.row,
-                range.start.column
-            );
-        }
-
-        var len = this.doc.getLength() - 1;
-        if (range.end.row > len) {
-            range.end.row = len;
-            range.end.column = this.doc.getLine(len).length;
-        } else {
-            range.end.column = this.$clipColumnToRow(
-                range.end.row,
-                range.end.column
-            );
-        }
-        return range;
-    };
-
-    // WRAPMODE
-    this.$wrapLimit = 80;
-    this.$useWrapMode = false;
-    this.$wrapLimitRange = {
-        min : null,
-        max : null
-    };
-
-    /**
-     * Sets whether or not line wrapping is enabled. If `useWrapMode` is different than the current value, the `'changeWrapMode'` event is emitted.
-     * @param {Boolean} useWrapMode Enable (or disable) wrap mode
-     *
-    *
-    **/
-    this.setUseWrapMode = function(useWrapMode) {
-        if (useWrapMode != this.$useWrapMode) {
-            this.$useWrapMode = useWrapMode;
-            this.$modified = true;
-            this.$resetRowCache(0);
-
-            // If wrapMode is activaed, the wrapData array has to be initialized.
-            if (useWrapMode) {
-                var len = this.getLength();
-                this.$wrapData = [];
-                for (var i = 0; i < len; i++) {
-                    this.$wrapData.push([]);
-                }
-                this.$updateWrapData(0, len - 1);
-            }
-
-            this._emit("changeWrapMode");
-        }
-    };
-
-    /**
-    * Returns `true` if wrap mode is being used; `false` otherwise.
-    * @returns {Boolean}
-    **/
-    this.getUseWrapMode = function() {
-        return this.$useWrapMode;
-    };
-
-    // Allow the wrap limit to move freely between min and max. Either
-    // parameter can be null to allow the wrap limit to be unconstrained
-    // in that direction. Or set both parameters to the same number to pin
-    // the limit to that value.
-    /**
-     * Sets the boundaries of wrap. Either value can be `null` to have an unconstrained wrap, or, they can be the same number to pin the limit. If the wrap limits for `min` or `max` are different, this method also emits the `'changeWrapMode'` event.
-     * @param {Number} min The minimum wrap value (the left side wrap)
-     * @param {Number} max The maximum wrap value (the right side wrap)
-     *
-    *
-    **/
-    this.setWrapLimitRange = function(min, max) {
-        if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) {
-            this.$wrapLimitRange = {
-                min: min,
-                max: max
-            };
-            this.$modified = true;
-            // This will force a recalculation of the wrap limit
-            this._emit("changeWrapMode");
-        }
-    };
-
-    /**
-    * This should generally only be called by the renderer when a resize is detected.
-    * @param {Number} desiredLimit The new wrap limit
-    * @returns {Boolean}
-    *
-    * @private
-    **/
-    this.adjustWrapLimit = function(desiredLimit, $printMargin) {
-        var limits = this.$wrapLimitRange
-        if (limits.max < 0)
-            limits = {min: $printMargin, max: $printMargin};
-        var wrapLimit = this.$constrainWrapLimit(desiredLimit, limits.min, limits.max);
-        if (wrapLimit != this.$wrapLimit && wrapLimit > 1) {
-            this.$wrapLimit = wrapLimit;
-            this.$modified = true;
-            if (this.$useWrapMode) {
-                this.$updateWrapData(0, this.getLength() - 1);
-                this.$resetRowCache(0);
-                this._emit("changeWrapLimit");
-            }
-            return true;
-        }
-        return false;
-    };
-
-    this.$constrainWrapLimit = function(wrapLimit, min, max) {
-        if (min)
-            wrapLimit = Math.max(min, wrapLimit);
-
-        if (max)
-            wrapLimit = Math.min(max, wrapLimit);
-
-        return wrapLimit;
-    };
-
-    /**
-    * Returns the value of wrap limit.
-    * @returns {Number} The wrap limit.
-    **/
-    this.getWrapLimit = function() {
-        return this.$wrapLimit;
-    };
-    
-    /**
-     * Sets the line length for soft wrap in the editor. Lines will break
-     *  at a minimum of the given length minus 20 chars and at a maximum
-     *  of the given number of chars.
-     * @param {number} limit The maximum line length in chars, for soft wrapping lines.
-     */
-    this.setWrapLimit = function (limit) {
-        this.setWrapLimitRange(limit, limit);
-    };
-    
-    /**
-    * Returns an object that defines the minimum and maximum of the wrap limit; it looks something like this:
-    *
-    *     { min: wrapLimitRange_min, max: wrapLimitRange_max }
-    *
-    * @returns {Object}
-    **/
-    this.getWrapLimitRange = function() {
-        // Avoid unexpected mutation by returning a copy
-        return {
-            min : this.$wrapLimitRange.min,
-            max : this.$wrapLimitRange.max
-        };
-    };
-
-    this.$updateInternalDataOnChange = function(e) {
-        var useWrapMode = this.$useWrapMode;
-        var len;
-        var action = e.data.action;
-        var firstRow = e.data.range.start.row;
-        var lastRow = e.data.range.end.row;
-        var start = e.data.range.start;
-        var end = e.data.range.end;
-        var removedFolds = null;
-
-        if (action.indexOf("Lines") != -1) {
-            if (action == "insertLines") {
-                lastRow = firstRow + (e.data.lines.length);
-            } else {
-                lastRow = firstRow;
-            }
-            len = e.data.lines ? e.data.lines.length : lastRow - firstRow;
-        } else {
-            len = lastRow - firstRow;
-        }
-
-        this.$updating = true;
-        if (len != 0) {
-            if (action.indexOf("remove") != -1) {
-                this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len);
-
-                var foldLines = this.$foldData;
-                removedFolds = this.getFoldsInRange(e.data.range);
-                this.removeFolds(removedFolds);
-
-                var foldLine = this.getFoldLine(end.row);
-                var idx = 0;
-                if (foldLine) {
-                    foldLine.addRemoveChars(end.row, end.column, start.column - end.column);
-                    foldLine.shiftRow(-len);
-
-                    var foldLineBefore = this.getFoldLine(firstRow);
-                    if (foldLineBefore && foldLineBefore !== foldLine) {
-                        foldLineBefore.merge(foldLine);
-                        foldLine = foldLineBefore;
-                    }
-                    idx = foldLines.indexOf(foldLine) + 1;
-                }
-
-                for (idx; idx < foldLines.length; idx++) {
-                    var foldLine = foldLines[idx];
-                    if (foldLine.start.row >= end.row) {
-                        foldLine.shiftRow(-len);
-                    }
-                }
-
-                lastRow = firstRow;
-            } else {
-                var args;
-                if (useWrapMode) {
-                    args = [firstRow, 0];
-                    for (var i = 0; i < len; i++) args.push([]);
-                    this.$wrapData.splice.apply(this.$wrapData, args);
-                } else {
-                    args = Array(len);
-                    args.unshift(firstRow, 0);
-                    this.$rowLengthCache.splice.apply(this.$rowLengthCache, args);
-                }
-
-                // If some new line is added inside of a foldLine, then split
-                // the fold line up.
-                var foldLines = this.$foldData;
-                var foldLine = this.getFoldLine(firstRow);
-                var idx = 0;
-                if (foldLine) {
-                    var cmp = foldLine.range.compareInside(start.row, start.column)
-                    // Inside of the foldLine range. Need to split stuff up.
-                    if (cmp == 0) {
-                        foldLine = foldLine.split(start.row, start.column);
-                        foldLine.shiftRow(len);
-                        foldLine.addRemoveChars(
-                            lastRow, 0, end.column - start.column);
-                    } else
-                    // Infront of the foldLine but same row. Need to shift column.
-                    if (cmp == -1) {
-                        foldLine.addRemoveChars(firstRow, 0, end.column - start.column);
-                        foldLine.shiftRow(len);
-                    }
-                    // Nothing to do if the insert is after the foldLine.
-                    idx = foldLines.indexOf(foldLine) + 1;
-                }
-
-                for (idx; idx < foldLines.length; idx++) {
-                    var foldLine = foldLines[idx];
-                    if (foldLine.start.row >= firstRow) {
-                        foldLine.shiftRow(len);
-                    }
-                }
-            }
-        } else {
-            // Realign folds. E.g. if you add some new chars before a fold, the
-            // fold should "move" to the right.
-            len = Math.abs(e.data.range.start.column - e.data.range.end.column);
-            if (action.indexOf("remove") != -1) {
-                // Get all the folds in the change range and remove them.
-                removedFolds = this.getFoldsInRange(e.data.range);
-                this.removeFolds(removedFolds);
-
-                len = -len;
-            }
-            var foldLine = this.getFoldLine(firstRow);
-            if (foldLine) {
-                foldLine.addRemoveChars(firstRow, start.column, len);
-            }
-        }
-
-        if (useWrapMode && this.$wrapData.length != this.doc.getLength()) {
-            console.error("doc.getLength() and $wrapData.length have to be the same!");
-        }
-        this.$updating = false;
-
-        if (useWrapMode)
-            this.$updateWrapData(firstRow, lastRow);
-        else
-            this.$updateRowLengthCache(firstRow, lastRow);
-
-        return removedFolds;
-    };
-
-    this.$updateRowLengthCache = function(firstRow, lastRow, b) {
-        this.$rowLengthCache[firstRow] = null;
-        this.$rowLengthCache[lastRow] = null;
-    };
-
-    this.$updateWrapData = function(firstRow, lastRow) {
-        var lines = this.doc.getAllLines();
-        var tabSize = this.getTabSize();
-        var wrapData = this.$wrapData;
-        var wrapLimit = this.$wrapLimit;
-        var tokens;
-        var foldLine;
-
-        var row = firstRow;
-        lastRow = Math.min(lastRow, lines.length - 1);
-        while (row <= lastRow) {
-            foldLine = this.getFoldLine(row, foldLine);
-            if (!foldLine) {
-                tokens = this.$getDisplayTokens(lines[row]);
-                wrapData[row] = this.$computeWrapSplits(tokens, wrapLimit, tabSize);
-                row ++;
-            } else {
-                tokens = [];
-                foldLine.walk(function(placeholder, row, column, lastColumn) {
-                        var walkTokens;
-                        if (placeholder != null) {
-                            walkTokens = this.$getDisplayTokens(
-                                            placeholder, tokens.length);
-                            walkTokens[0] = PLACEHOLDER_START;
-                            for (var i = 1; i < walkTokens.length; i++) {
-                                walkTokens[i] = PLACEHOLDER_BODY;
-                            }
-                        } else {
-                            walkTokens = this.$getDisplayTokens(
-                                lines[row].substring(lastColumn, column),
-                                tokens.length);
-                        }
-                        tokens = tokens.concat(walkTokens);
-                    }.bind(this),
-                    foldLine.end.row,
-                    lines[foldLine.end.row].length + 1
-                );
-
-                wrapData[foldLine.start.row]
-                    = this.$computeWrapSplits(tokens, wrapLimit, tabSize);
-                row = foldLine.end.row + 1;
-            }
-        }
-    };
-
-    // "Tokens"
-    var CHAR = 1,
-        CHAR_EXT = 2,
-        PLACEHOLDER_START = 3,
-        PLACEHOLDER_BODY =  4,
-        PUNCTUATION = 9,
-        SPACE = 10,
-        TAB = 11,
-        TAB_SPACE = 12;
-
-
-    this.$computeWrapSplits = function(tokens, wrapLimit) {
-        if (tokens.length == 0) {
-            return [];
-        }
-
-        var splits = [];
-        var displayLength = tokens.length;
-        var lastSplit = 0, lastDocSplit = 0;
-
-        var isCode = this.$wrapAsCode;
-
-        function addSplit(screenPos) {
-            var displayed = tokens.slice(lastSplit, screenPos);
-
-            // The document size is the current size - the extra width for tabs
-            // and multipleWidth characters.
-            var len = displayed.length;
-            displayed.join("").
-                // Get all the TAB_SPACEs.
-                replace(/12/g, function() {
-                    len -= 1;
-                }).
-                // Get all the CHAR_EXT/multipleWidth characters.
-                replace(/2/g, function() {
-                    len -= 1;
-                });
-
-            lastDocSplit += len;
-            splits.push(lastDocSplit);
-            lastSplit = screenPos;
-        }
-
-        while (displayLength - lastSplit > wrapLimit) {
-            // This is, where the split should be.
-            var split = lastSplit + wrapLimit;
-
-            // If there is a space or tab at this split position, then making
-            // a split is simple.
-            if (tokens[split - 1] >= SPACE && tokens[split] >= SPACE) {
-                /* disabled see https://github.com/ajaxorg/ace/issues/1186
-                // Include all following spaces + tabs in this split as well.
-                while (tokens[split] >= SPACE) {
-                    split ++;
-                } */
-                addSplit(split);
-                continue;
-            }
-
-            // === ELSE ===
-            // Check if split is inside of a placeholder. Placeholder are
-            // not splitable. Therefore, seek the beginning of the placeholder
-            // and try to place the split beofre the placeholder's start.
-            if (tokens[split] == PLACEHOLDER_START || tokens[split] == PLACEHOLDER_BODY) {
-                // Seek the start of the placeholder and do the split
-                // before the placeholder. By definition there always
-                // a PLACEHOLDER_START between split and lastSplit.
-                for (split; split != lastSplit - 1; split--) {
-                    if (tokens[split] == PLACEHOLDER_START) {
-                        // split++; << No incremental here as we want to
-                        //  have the position before the Placeholder.
-                        break;
-                    }
-                }
-
-                // If the PLACEHOLDER_START is not the index of the
-                // last split, then we can do the split
-                if (split > lastSplit) {
-                    addSplit(split);
-                    continue;
-                }
-
-                // If the PLACEHOLDER_START IS the index of the last
-                // split, then we have to place the split after the
-                // placeholder. So, let's seek for the end of the placeholder.
-                split = lastSplit + wrapLimit;
-                for (split; split < tokens.length; split++) {
-                    if (tokens[split] != PLACEHOLDER_BODY) {
-                        break;
-                    }
-                }
-
-                // If spilt == tokens.length, then the placeholder is the last
-                // thing in the line and adding a new split doesn't make sense.
-                if (split == tokens.length) {
-                    break;  // Breaks the while-loop.
-                }
-
-                // Finally, add the split...
-                addSplit(split);
-                continue;
-            }
-
-            // === ELSE ===
-            // Search for the first non space/tab/placeholder/punctuation token backwards.
-            var minSplit = Math.max(split - (isCode ? 10 : wrapLimit-(wrapLimit>>2)), lastSplit - 1);
-            while (split > minSplit && tokens[split] < PLACEHOLDER_START) {
-                split --;
-            }
-            if (isCode) {
-                while (split > minSplit && tokens[split] < PLACEHOLDER_START) {
-                    split --;
-                }
-                while (split > minSplit && tokens[split] == PUNCTUATION) {
-                    split --;
-                }
-            } else {
-                while (split > minSplit && tokens[split] < SPACE) {
-                    split --;
-                }
-            }
-            // If we found one, then add the split.
-            if (split > minSplit) {
-                addSplit(++split);
-                continue;
-            }
-
-            // === ELSE ===
-            split = lastSplit + wrapLimit;
-            // The split is inside of a CHAR or CHAR_EXT token and no space
-            // around -> force a split.
-            addSplit(split);
-        }
-        return splits;
-    };
-
-    /**
-    * Given a string, returns an array of the display characters, including tabs and spaces.
-    * @param {String} str The string to check
-    * @param {Number} offset The value to start at
-    *
-    *
-    **/
-    this.$getDisplayTokens = function(str, offset) {
-        var arr = [];
-        var tabSize;
-        offset = offset || 0;
-
-        for (var i = 0; i < str.length; i++) {
-            var c = str.charCodeAt(i);
-            // Tab
-            if (c == 9) {
-                tabSize = this.getScreenTabSize(arr.length + offset);
-                arr.push(TAB);
-                for (var n = 1; n < tabSize; n++) {
-                    arr.push(TAB_SPACE);
-                }
-            }
-            // Space
-            else if (c == 32) {
-                arr.push(SPACE);
-            } else if((c > 39 && c < 48) || (c > 57 && c < 64)) {
-                arr.push(PUNCTUATION);
-            }
-            // full width characters
-            else if (c >= 0x1100 && isFullWidth(c)) {
-                arr.push(CHAR, CHAR_EXT);
-            } else {
-                arr.push(CHAR);
-            }
-        }
-        return arr;
-    };
-
-    /**
-     * Calculates the width of the string `str` on the screen while assuming that the string starts at the first column on the screen.
-    * @param {String} str The string to calculate the screen width of
-    * @param {Number} maxScreenColumn
-    * @param {Number} screenColumn
-    * @returns {[Number]} Returns an `int[]` array with two elements:<br/>
-    * The first position indicates the number of columns for `str` on screen.<br/>
-    * The second value contains the position of the document column that this function read until.
-    *
-    *
-    *
-    *
-    **/
-    this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) {
-        if (maxScreenColumn == 0)
-            return [0, 0];
-        if (maxScreenColumn == null)
-            maxScreenColumn = Infinity;
-        screenColumn = screenColumn || 0;
-
-        var c, column;
-        for (column = 0; column < str.length; column++) {
-            c = str.charCodeAt(column);
-            // tab
-            if (c == 9) {
-                screenColumn += this.getScreenTabSize(screenColumn);
-            }
-            // full width characters
-            else if (c >= 0x1100 && isFullWidth(c)) {
-                screenColumn += 2;
-            } else {
-                screenColumn += 1;
-            }
-            if (screenColumn > maxScreenColumn) {
-                break;
-            }
-        }
-
-        return [screenColumn, column];
-    };
-
-    this.lineWidgets = null;
-    /**
-    * Returns number of screenrows in a wrapped line.
-    * @param {Number} row The row number to check
-    *
-    * @returns {Number}
-    **/
-    this.getRowLength = function(row) {
-        if (this.lineWidgets)
-            var h = this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0;
-        else 
-            h = 0
-        if (!this.$useWrapMode || !this.$wrapData[row]) {
-            return 1 + h;
-        } else {
-            return this.$wrapData[row].length + 1 + h;
-        }
-    };
-    this.getRowLineCount = function(row) {
-        if (!this.$useWrapMode || !this.$wrapData[row]) {
-            return 1;
-        } else {
-            return this.$wrapData[row].length + 1;
-        }
-    };
-
-    /**
-     * Returns the position (on screen) for the last character in the provided screen row.
-     * @param {Number} screenRow The screen row to check
-     * @returns {Number}
-     *
-     * @related EditSession.documentToScreenColumn
-    **/
-    this.getScreenLastRowColumn = function(screenRow) {
-        var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE);
-        return this.documentToScreenColumn(pos.row, pos.column);
-    };
-
-    /**
-    * For the given document row and column, this returns the column position of the last screen row.
-    * @param {Number} docRow
-    *
-    * @param {Number} docColumn
-    **/
-    this.getDocumentLastRowColumn = function(docRow, docColumn) {
-        var screenRow = this.documentToScreenRow(docRow, docColumn);
-        return this.getScreenLastRowColumn(screenRow);
-    };
-
-    /**
-    * For the given document row and column, this returns the document position of the last row.
-    * @param {Number} docRow
-    * @param {Number} docColumn
-    *
-    *
-    **/
-    this.getDocumentLastRowColumnPosition = function(docRow, docColumn) {
-        var screenRow = this.documentToScreenRow(docRow, docColumn);
-        return this.screenToDocumentPosition(screenRow, Number.MAX_VALUE / 10);
-    };
-
-    /**
-    * For the given row, this returns the split data.
-    * @returns {String}
-    **/
-    this.getRowSplitData = function(row) {
-        if (!this.$useWrapMode) {
-            return undefined;
-        } else {
-            return this.$wrapData[row];
-        }
-    };
-
-    /**
-    * The distance to the next tab stop at the specified screen column.
-    * @param {Number} screenColumn The screen column to check
-    *
-    *
-    * @returns {Number}
-    **/
-    this.getScreenTabSize = function(screenColumn) {
-        return this.$tabSize - screenColumn % this.$tabSize;
-    };
-
-
-    this.screenToDocumentRow = function(screenRow, screenColumn) {
-        return this.screenToDocumentPosition(screenRow, screenColumn).row;
-    };
-
-
-    this.screenToDocumentColumn = function(screenRow, screenColumn) {
-        return this.screenToDocumentPosition(screenRow, screenColumn).column;
-    };
-
-    /**
-    * Converts characters coordinates on the screen to characters coordinates within the document. [This takes into account code folding, word wrap, tab size, and any other visual modifications.]{: #conversionConsiderations}
-    * @param {Number} screenRow The screen row to check
-    * @param {Number} screenColumn The screen column to check
-    * @returns {Object} The object returned has two properties: `row` and `column`.
-    *
-    *
-    * @related EditSession.documentToScreenPosition
-    *
-    **/
-    this.screenToDocumentPosition = function(screenRow, screenColumn) {
-        if (screenRow < 0)
-            return {row: 0, column: 0};
-
-        var line;
-        var docRow = 0;
-        var docColumn = 0;
-        var column;
-        var row = 0;
-        var rowLength = 0;
-
-        var rowCache = this.$screenRowCache;
-        var i = this.$getRowCacheIndex(rowCache, screenRow);
-        var l = rowCache.length;
-        if (l && i >= 0) {
-            var row = rowCache[i];
-            var docRow = this.$docRowCache[i];
-            var doCache = screenRow > rowCache[l - 1];
-        } else {
-            var doCache = !l;
-        }
-
-        var maxRow = this.getLength() - 1;
-        var foldLine = this.getNextFoldLine(docRow);
-        var foldStart = foldLine ? foldLine.start.row : Infinity;
-
-        while (row <= screenRow) {
-            rowLength = this.getRowLength(docRow);
-            if (row + rowLength > screenRow || docRow >= maxRow) {
-                break;
-            } else {
-                row += rowLength;
-                docRow++;
-                if (docRow > foldStart) {
-                    docRow = foldLine.end.row+1;
-                    foldLine = this.getNextFoldLine(docRow, foldLine);
-                    foldStart = foldLine ? foldLine.start.row : Infinity;
-                }
-            }
-
-            if (doCache) {
-                this.$docRowCache.push(docRow);
-                this.$screenRowCache.push(row);
-            }
-        }
-
-        if (foldLine && foldLine.start.row <= docRow) {
-            line = this.getFoldDisplayLine(foldLine);
-            docRow = foldLine.start.row;
-        } else if (row + rowLength <= screenRow || docRow > maxRow) {
-            // clip at the end of the document
-            return {
-                row: maxRow,
-                column: this.getLine(maxRow).length
-            }
-        } else {
-            line = this.getLine(docRow);
-            foldLine = null;
-        }
-
-        if (this.$useWrapMode) {
-            var splits = this.$wrapData[docRow];
-            if (splits) {
-                var splitIndex = Math.floor(screenRow - row);
-                column = splits[splitIndex];
-                if(splitIndex > 0 && splits.length) {
-                    docColumn = splits[splitIndex - 1] || splits[splits.length - 1];
-                    line = line.substring(docColumn);
-                }
-            }
-        }
-
-        docColumn += this.$getStringScreenWidth(line, screenColumn)[1];
-
-        // We remove one character at the end so that the docColumn
-        // position returned is not associated to the next row on the screen.
-        if (this.$useWrapMode && docColumn >= column)
-            docColumn = column - 1;
-
-        if (foldLine)
-            return foldLine.idxToPosition(docColumn);
-
-        return {row: docRow, column: docColumn};
-    };
-
-    /**
-    * Converts document coordinates to screen coordinates. {:conversionConsiderations}
-    * @param {Number} docRow The document row to check
-    * @param {Number} docColumn The document column to check
-    * @returns {Object} The object returned by this method has two properties: `row` and `column`.
-    *
-    *
-    * @related EditSession.screenToDocumentPosition
-    *
-    **/
-    this.documentToScreenPosition = function(docRow, docColumn) {
-        // Normalize the passed in arguments.
-        if (typeof docColumn === "undefined")
-            var pos = this.$clipPositionToDocument(docRow.row, docRow.column);
-        else
-            pos = this.$clipPositionToDocument(docRow, docColumn);
-
-        docRow = pos.row;
-        docColumn = pos.column;
-
-        var screenRow = 0;
-        var foldStartRow = null;
-        var fold = null;
-
-        // Clamp the docRow position in case it's inside of a folded block.
-        fold = this.getFoldAt(docRow, docColumn, 1);
-        if (fold) {
-            docRow = fold.start.row;
-            docColumn = fold.start.column;
-        }
-
-        var rowEnd, row = 0;
-
-
-        var rowCache = this.$docRowCache;
-        var i = this.$getRowCacheIndex(rowCache, docRow);
-        var l = rowCache.length;
-        if (l && i >= 0) {
-            var row = rowCache[i];
-            var screenRow = this.$screenRowCache[i];
-            var doCache = docRow > rowCache[l - 1];
-        } else {
-            var doCache = !l;
-        }
-
-        var foldLine = this.getNextFoldLine(row);
-        var foldStart = foldLine ?foldLine.start.row :Infinity;
-
-        while (row < docRow) {
-            if (row >= foldStart) {
-                rowEnd = foldLine.end.row + 1;
-                if (rowEnd > docRow)
-                    break;
-                foldLine = this.getNextFoldLine(rowEnd, foldLine);
-                foldStart = foldLine ?foldLine.start.row :Infinity;
-            }
-            else {
-                rowEnd = row + 1;
-            }
-
-            screenRow += this.getRowLength(row);
-            row = rowEnd;
-
-            if (doCache) {
-                this.$docRowCache.push(row);
-                this.$screenRowCache.push(screenRow);
-            }
-        }
-
-        // Calculate the text line that is displayed in docRow on the screen.
-        var textLine = "";
-        // Check if the final row we want to reach is inside of a fold.
-        if (foldLine && row >= foldStart) {
-            textLine = this.getFoldDisplayLine(foldLine, docRow, docColumn);
-            foldStartRow = foldLine.start.row;
-        } else {
-            textLine = this.getLine(docRow).substring(0, docColumn);
-            foldStartRow = docRow;
-        }
-        // Clamp textLine if in wrapMode.
-        if (this.$useWrapMode) {
-            var wrapRow = this.$wrapData[foldStartRow];
-            var screenRowOffset = 0;
-            while (textLine.length >= wrapRow[screenRowOffset]) {
-                screenRow ++;
-                screenRowOffset++;
-            }
-            textLine = textLine.substring(
-                wrapRow[screenRowOffset - 1] || 0, textLine.length
-            );
-        }
-
-        return {
-            row: screenRow,
-            column: this.$getStringScreenWidth(textLine)[0]
-        };
-    };
-
-    /**
-    * For the given document row and column, returns the screen column.
-    * @param {Number} row
-    * @param {Number} docColumn
-    * @returns {Number}
-    *
-    **/
-    this.documentToScreenColumn = function(row, docColumn) {
-        return this.documentToScreenPosition(row, docColumn).column;
-    };
-
-    /**
-    * For the given document row and column, returns the screen row.
-    * @param {Number} docRow
-    * @param {Number} docColumn
-    *
-    *
-    **/
-    this.documentToScreenRow = function(docRow, docColumn) {
-        return this.documentToScreenPosition(docRow, docColumn).row;
-    };
-
-    /**
-    * Returns the length of the screen.
-    * @returns {Number}
-    **/
-    this.getScreenLength = function() {
-        var screenRows = 0;
-        var fold = null;
-        if (!this.$useWrapMode) {
-            screenRows = this.getLength();
-
-            // Remove the folded lines again.
-            var foldData = this.$foldData;
-            for (var i = 0; i < foldData.length; i++) {
-                fold = foldData[i];
-                screenRows -= fold.end.row - fold.start.row;
-            }
-        } else {
-            var lastRow = this.$wrapData.length;
-            var row = 0, i = 0;
-            var fold = this.$foldData[i++];
-            var foldStart = fold ? fold.start.row :Infinity;
-
-            while (row < lastRow) {
-                screenRows += this.$wrapData[row].length + 1;
-                row ++;
-                if (row > foldStart) {
-                    row = fold.end.row+1;
-                    fold = this.$foldData[i++];
-                    foldStart = fold ?fold.start.row :Infinity;
-                }
-            }
-        }
-
-        // todo
-        if (this.lineWidgets)
-            screenRows += this.$getWidgetScreenLength();
-
-        return screenRows;
-    };
-
-    // For every keystroke this gets called once per char in the whole doc!!
-    // Wouldn't hurt to make it a bit faster for c >= 0x1100
-
-    /**
-     * @private
-     *
-     */
-    function isFullWidth(c) {
-        if (c < 0x1100)
-            return false;
-        return c >= 0x1100 && c <= 0x115F ||
-               c >= 0x11A3 && c <= 0x11A7 ||
-               c >= 0x11FA && c <= 0x11FF ||
-               c >= 0x2329 && c <= 0x232A ||
-               c >= 0x2E80 && c <= 0x2E99 ||
-               c >= 0x2E9B && c <= 0x2EF3 ||
-               c >= 0x2F00 && c <= 0x2FD5 ||
-               c >= 0x2FF0 && c <= 0x2FFB ||
-               c >= 0x3000 && c <= 0x303E ||
-               c >= 0x3041 && c <= 0x3096 ||
-               c >= 0x3099 && c <= 0x30FF ||
-               c >= 0x3105 && c <= 0x312D ||
-               c >= 0x3131 && c <= 0x318E ||
-               c >= 0x3190 && c <= 0x31BA ||
-               c >= 0x31C0 && c <= 0x31E3 ||
-               c >= 0x31F0 && c <= 0x321E ||
-               c >= 0x3220 && c <= 0x3247 ||
-               c >= 0x3250 && c <= 0x32FE ||
-               c >= 0x3300 && c <= 0x4DBF ||
-               c >= 0x4E00 && c <= 0xA48C ||
-               c >= 0xA490 && c <= 0xA4C6 ||
-               c >= 0xA960 && c <= 0xA97C ||
-               c >= 0xAC00 && c <= 0xD7A3 ||
-               c >= 0xD7B0 && c <= 0xD7C6 ||
-               c >= 0xD7CB && c <= 0xD7FB ||
-               c >= 0xF900 && c <= 0xFAFF ||
-               c >= 0xFE10 && c <= 0xFE19 ||
-               c >= 0xFE30 && c <= 0xFE52 ||
-               c >= 0xFE54 && c <= 0xFE66 ||
-               c >= 0xFE68 && c <= 0xFE6B ||
-               c >= 0xFF01 && c <= 0xFF60 ||
-               c >= 0xFFE0 && c <= 0xFFE6;
-    };
-
-}).call(EditSession.prototype);
-
-require("./edit_session/folding").Folding.call(EditSession.prototype);
-require("./edit_session/bracket_match").BracketMatch.call(EditSession.prototype);
-
-
-config.defineOptions(EditSession.prototype, "session", {
-    wrap: {
-        set: function(value) {
-            if (!value || value == "off")
-                value = false;
-            else if (value == "free")
-                value = true;
-            else if (value == "printMargin")
-                value = -1;
-            else if (typeof value == "string")
-                value = parseInt(value, 10) || false;
-
-            if (this.$wrap == value)
-                return;
-            if (!value) {
-                this.setUseWrapMode(false);
-            } else {
-                var col = typeof value == "number" ? value : null;
-                this.setWrapLimitRange(col, col);
-                this.setUseWrapMode(true);
-            }
-            this.$wrap = value;
-        },
-        get: function() {
-            return this.getUseWrapMode() ? this.getWrapLimitRange().min || "free" : "off";
-        },
-        handlesSet: true
-    },    
-    wrapMethod: {
-        // code|text|auto
-        set: function(val) {
-            val = val == "auto"
-                ? this.$mode.type != "text"
-                : val != "text";
-            if (val != this.$wrapAsCode) {
-                this.$wrapAsCode = val;
-                if (this.$useWrapMode) {
-                    this.$modified = true;
-                    this.$resetRowCache(0);
-                    this.$updateWrapData(0, this.getLength() - 1);
-                }
-            }
-        },
-        initialValue: "auto"
-    },
-    firstLineNumber: {
-        set: function() {this._emit("changeBreakpoint");},
-        initialValue: 1
-    },
-    useWorker: {
-        set: function(useWorker) {
-            this.$useWorker = useWorker;
-
-            this.$stopWorker();
-            if (useWorker)
-                this.$startWorker();
-        },
-        initialValue: true
-    },
-    useSoftTabs: {initialValue: true},
-    tabSize: {
-        set: function(tabSize) {
-            if (isNaN(tabSize) || this.$tabSize === tabSize) return;
-
-            this.$modified = true;
-            this.$rowLengthCache = [];
-            this.$tabSize = tabSize;
-            this._emit("changeTabSize");
-        },
-        initialValue: 4,
-        handlesSet: true
-    },
-    overwrite: {
-        set: function(val) {this._emit("changeOverwrite");},
-        initialValue: false
-    },
-    newLineMode: {
-        set: function(val) {this.doc.setNewLineMode(val)},
-        get: function() {return this.doc.getNewLineMode()},
-        handlesSet: true
-    }
-});
-
-exports.EditSession = EditSession;
-});

+ 0 - 219
services/web/public/js/ace/edit_session/bracket_match.js

@@ -1,219 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var TokenIterator = require("../token_iterator").TokenIterator;
-var Range = require("../range").Range;
-
-
-function BracketMatch() {
-
-    this.findMatchingBracket = function(position, chr) {
-        if (position.column == 0) return null;
-
-        var charBeforeCursor = chr || this.getLine(position.row).charAt(position.column-1);
-        if (charBeforeCursor == "") return null;
-
-        var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/);
-        if (!match)
-            return null;
-
-        if (match[1])
-            return this.$findClosingBracket(match[1], position);
-        else
-            return this.$findOpeningBracket(match[2], position);
-    };
-    
-    this.getBracketRange = function(pos) {
-        var line = this.getLine(pos.row);
-        var before = true, range;
-
-        var chr = line.charAt(pos.column-1);
-        var match = chr && chr.match(/([\(\[\{])|([\)\]\}])/);
-        if (!match) {
-            chr = line.charAt(pos.column);
-            pos = {row: pos.row, column: pos.column + 1};
-            match = chr && chr.match(/([\(\[\{])|([\)\]\}])/);
-            before = false;
-        }
-        if (!match)
-            return null;
-
-        if (match[1]) {
-            var bracketPos = this.$findClosingBracket(match[1], pos);
-            if (!bracketPos)
-                return null;
-            range = Range.fromPoints(pos, bracketPos);
-            if (!before) {
-                range.end.column++;
-                range.start.column--;
-            }
-            range.cursor = range.end;
-        } else {
-            var bracketPos = this.$findOpeningBracket(match[2], pos);
-            if (!bracketPos)
-                return null;
-            range = Range.fromPoints(bracketPos, pos);
-            if (!before) {
-                range.start.column++;
-                range.end.column--;
-            }
-            range.cursor = range.start;
-        }
-        
-        return range;
-    };
-
-    this.$brackets = {
-        ")": "(",
-        "(": ")",
-        "]": "[",
-        "[": "]",
-        "{": "}",
-        "}": "{"
-    };
-
-    this.$findOpeningBracket = function(bracket, position, typeRe) {
-        var openBracket = this.$brackets[bracket];
-        var depth = 1;
-
-        var iterator = new TokenIterator(this, position.row, position.column);
-        var token = iterator.getCurrentToken();
-        if (!token)
-            token = iterator.stepForward();
-        if (!token)
-            return;
-        
-         if (!typeRe){
-            typeRe = new RegExp(
-                "(\\.?" +
-                token.type.replace(".", "\\.").replace("rparen", ".paren")
-                + ")+"
-            );
-        }
-        
-        // Start searching in token, just before the character at position.column
-        var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2;
-        var value = token.value;
-        
-        while (true) {
-        
-            while (valueIndex >= 0) {
-                var chr = value.charAt(valueIndex);
-                if (chr == openBracket) {
-                    depth -= 1;
-                    if (depth == 0) {
-                        return {row: iterator.getCurrentTokenRow(),
-                            column: valueIndex + iterator.getCurrentTokenColumn()};
-                    }
-                }
-                else if (chr == bracket) {
-                    depth += 1;
-                }
-                valueIndex -= 1;
-            }
-
-            // Scan backward through the document, looking for the next token
-            // whose type matches typeRe
-            do {
-                token = iterator.stepBackward();
-            } while (token && !typeRe.test(token.type));
-
-            if (token == null)
-                break;
-                
-            value = token.value;
-            valueIndex = value.length - 1;
-        }
-        
-        return null;
-    };
-
-    this.$findClosingBracket = function(bracket, position, typeRe) {
-        var closingBracket = this.$brackets[bracket];
-        var depth = 1;
-
-        var iterator = new TokenIterator(this, position.row, position.column);
-        var token = iterator.getCurrentToken();
-        if (!token)
-            token = iterator.stepForward();
-        if (!token)
-            return;
-
-        if (!typeRe){
-            typeRe = new RegExp(
-                "(\\.?" +
-                token.type.replace(".", "\\.").replace("lparen", ".paren")
-                + ")+"
-            );
-        }
-
-        // Start searching in token, after the character at position.column
-        var valueIndex = position.column - iterator.getCurrentTokenColumn();
-
-        while (true) {
-
-            var value = token.value;
-            var valueLength = value.length;
-            while (valueIndex < valueLength) {
-                var chr = value.charAt(valueIndex);
-                if (chr == closingBracket) {
-                    depth -= 1;
-                    if (depth == 0) {
-                        return {row: iterator.getCurrentTokenRow(),
-                            column: valueIndex + iterator.getCurrentTokenColumn()};
-                    }
-                }
-                else if (chr == bracket) {
-                    depth += 1;
-                }
-                valueIndex += 1;
-            }
-
-            // Scan forward through the document, looking for the next token
-            // whose type matches typeRe
-            do {
-                token = iterator.stepForward();
-            } while (token && !typeRe.test(token.type));
-
-            if (token == null)
-                break;
-
-            valueIndex = 0;
-        }
-        
-        return null;
-    };
-}
-exports.BracketMatch = BracketMatch;
-
-});

+ 0 - 140
services/web/public/js/ace/edit_session/fold.js

@@ -1,140 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var Range = require("../range").Range;
-var RangeList = require("../range_list").RangeList;
-var oop = require("../lib/oop")
-/*
- * Simple fold-data struct.
- **/
-var Fold = exports.Fold = function(range, placeholder) {
-    this.foldLine = null;
-    this.placeholder = placeholder;
-    this.range = range;
-    this.start = range.start;
-    this.end = range.end;
-
-    this.sameRow = range.start.row == range.end.row;
-    this.subFolds = this.ranges = [];
-};
-
-oop.inherits(Fold, RangeList);
-
-(function() {
-
-    this.toString = function() {
-        return '"' + this.placeholder + '" ' + this.range.toString();
-    };
-
-    this.setFoldLine = function(foldLine) {
-        this.foldLine = foldLine;
-        this.subFolds.forEach(function(fold) {
-            fold.setFoldLine(foldLine);
-        });
-    };
-
-    this.clone = function() {
-        var range = this.range.clone();
-        var fold = new Fold(range, this.placeholder);
-        this.subFolds.forEach(function(subFold) {
-            fold.subFolds.push(subFold.clone());
-        });
-        fold.collapseChildren = this.collapseChildren;
-        return fold;
-    };
-
-    this.addSubFold = function(fold) {
-        if (this.range.isEqual(fold))
-            return;
-
-        if (!this.range.containsRange(fold))
-            throw new Error("A fold can't intersect already existing fold" + fold.range + this.range);
-
-        // transform fold to local coordinates
-        consumeRange(fold, this.start);
-
-        var row = fold.start.row, column = fold.start.column;
-        for (var i = 0, cmp = -1; i < this.subFolds.length; i++) {
-            cmp = this.subFolds[i].range.compare(row, column);
-            if (cmp != 1)
-                break;
-        }
-        var afterStart = this.subFolds[i];
-
-        if (cmp == 0)
-            return afterStart.addSubFold(fold);
-
-        // cmp == -1
-        var row = fold.range.end.row, column = fold.range.end.column;
-        for (var j = i, cmp = -1; j < this.subFolds.length; j++) {
-            cmp = this.subFolds[j].range.compare(row, column);
-            if (cmp != 1)
-                break;
-        }
-        var afterEnd = this.subFolds[j];
-
-        if (cmp == 0)
-            throw new Error("A fold can't intersect already existing fold" + fold.range + this.range);
-
-        var consumedFolds = this.subFolds.splice(i, j - i, fold);
-        fold.setFoldLine(this.foldLine);
-
-        return fold;
-    };
-    
-    this.restoreRange = function(range) {
-        return restoreRange(range, this.start);
-    };
-
-}).call(Fold.prototype);
-
-function consumePoint(point, anchor) {
-    point.row -= anchor.row;
-    if (point.row == 0)
-        point.column -= anchor.column;
-}
-function consumeRange(range, anchor) {
-    consumePoint(range.start, anchor);
-    consumePoint(range.end, anchor);
-}
-function restorePoint(point, anchor) {
-    if (point.row == 0)
-        point.column += anchor.column;
-    point.row += anchor.row;
-}
-function restoreRange(range, anchor) {
-    restorePoint(range.start, anchor);
-    restorePoint(range.end, anchor);
-}
-
-});

+ 0 - 268
services/web/public/js/ace/edit_session/fold_line.js

@@ -1,268 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var Range = require("../range").Range;
-
-/*
- * If an array is passed in, the folds are expected to be sorted already.
- */
-function FoldLine(foldData, folds) {
-    this.foldData = foldData;
-    if (Array.isArray(folds)) {
-        this.folds = folds;
-    } else {
-        folds = this.folds = [ folds ];
-    }
-
-    var last = folds[folds.length - 1]
-    this.range = new Range(folds[0].start.row, folds[0].start.column,
-                           last.end.row, last.end.column);
-    this.start = this.range.start;
-    this.end   = this.range.end;
-
-    this.folds.forEach(function(fold) {
-        fold.setFoldLine(this);
-    }, this);
-}
-
-(function() {
-    /*
-     * Note: This doesn't update wrapData!
-     */
-    this.shiftRow = function(shift) {
-        this.start.row += shift;
-        this.end.row += shift;
-        this.folds.forEach(function(fold) {
-            fold.start.row += shift;
-            fold.end.row += shift;
-        });
-    }
-
-    this.addFold = function(fold) {
-        if (fold.sameRow) {
-            if (fold.start.row < this.startRow || fold.endRow > this.endRow) {
-                throw new Error("Can't add a fold to this FoldLine as it has no connection");
-            }
-            this.folds.push(fold);
-            this.folds.sort(function(a, b) {
-                return -a.range.compareEnd(b.start.row, b.start.column);
-            });
-            if (this.range.compareEnd(fold.start.row, fold.start.column) > 0) {
-                this.end.row = fold.end.row;
-                this.end.column =  fold.end.column;
-            } else if (this.range.compareStart(fold.end.row, fold.end.column) < 0) {
-                this.start.row = fold.start.row;
-                this.start.column = fold.start.column;
-            }
-        } else if (fold.start.row == this.end.row) {
-            this.folds.push(fold);
-            this.end.row = fold.end.row;
-            this.end.column = fold.end.column;
-        } else if (fold.end.row == this.start.row) {
-            this.folds.unshift(fold);
-            this.start.row = fold.start.row;
-            this.start.column = fold.start.column;
-        } else {
-            throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");
-        }
-        fold.foldLine = this;
-    }
-
-    this.containsRow = function(row) {
-        return row >= this.start.row && row <= this.end.row;
-    }
-
-    this.walk = function(callback, endRow, endColumn) {
-        var lastEnd = 0,
-            folds = this.folds,
-            fold,
-            comp, stop, isNewRow = true;
-
-        if (endRow == null) {
-            endRow = this.end.row;
-            endColumn = this.end.column;
-        }
-
-        for (var i = 0; i < folds.length; i++) {
-            fold = folds[i];
-
-            comp = fold.range.compareStart(endRow, endColumn);
-            // This fold is after the endRow/Column.
-            if (comp == -1) {
-                callback(null, endRow, endColumn, lastEnd, isNewRow);
-                return;
-            }
-
-            stop = callback(null, fold.start.row, fold.start.column, lastEnd, isNewRow);
-            stop = !stop && callback(fold.placeholder, fold.start.row, fold.start.column, lastEnd);
-
-            // If the user requested to stop the walk or endRow/endColumn is
-            // inside of this fold (comp == 0), then end here.
-            if (stop || comp == 0) {
-                return;
-            }
-
-            // Note the new lastEnd might not be on the same line. However,
-            // it's the callback's job to recognize this.
-            isNewRow = !fold.sameRow;
-            lastEnd = fold.end.column;
-        }
-        callback(null, endRow, endColumn, lastEnd, isNewRow);
-    }
-
-    this.getNextFoldTo = function(row, column) {
-        var fold, cmp;
-        for (var i = 0; i < this.folds.length; i++) {
-            fold = this.folds[i];
-            cmp = fold.range.compareEnd(row, column);
-            if (cmp == -1) {
-                return {
-                    fold: fold,
-                    kind: "after"
-                };
-            } else if (cmp == 0) {
-                return {
-                    fold: fold,
-                    kind: "inside"
-                }
-            }
-        }
-        return null;
-    }
-
-    this.addRemoveChars = function(row, column, len) {
-        var ret = this.getNextFoldTo(row, column),
-            fold, folds;
-        if (ret) {
-            fold = ret.fold;
-            if (ret.kind == "inside"
-                && fold.start.column != column
-                && fold.start.row != row)
-            {
-                //throwing here breaks whole editor
-                //TODO: properly handle this
-                window.console && window.console.log(row, column, fold);
-            } else if (fold.start.row == row) {
-                folds = this.folds;
-                var i = folds.indexOf(fold);
-                if (i == 0) {
-                    this.start.column += len;
-                }
-                for (i; i < folds.length; i++) {
-                    fold = folds[i];
-                    fold.start.column += len;
-                    if (!fold.sameRow) {
-                        return;
-                    }
-                    fold.end.column += len;
-                }
-                this.end.column += len;
-            }
-        }
-    }
-
-    this.split = function(row, column) {
-        var fold = this.getNextFoldTo(row, column).fold;
-        var folds = this.folds;
-        var foldData = this.foldData;
-
-        if (!fold)
-            return null;
-
-        var i = folds.indexOf(fold);
-        var foldBefore = folds[i - 1];
-        this.end.row = foldBefore.end.row;
-        this.end.column = foldBefore.end.column;
-
-        // Remove the folds after row/column and create a new FoldLine
-        // containing these removed folds.
-        folds = folds.splice(i, folds.length - i);
-
-        var newFoldLine = new FoldLine(foldData, folds);
-        foldData.splice(foldData.indexOf(this) + 1, 0, newFoldLine);
-        return newFoldLine;
-    }
-
-    this.merge = function(foldLineNext) {
-        var folds = foldLineNext.folds;
-        for (var i = 0; i < folds.length; i++) {
-            this.addFold(folds[i]);
-        }
-        // Remove the foldLineNext - no longer needed, as
-        // it's merged now with foldLineNext.
-        var foldData = this.foldData;
-        foldData.splice(foldData.indexOf(foldLineNext), 1);
-    }
-
-    this.toString = function() {
-        var ret = [this.range.toString() + ": [" ];
-
-        this.folds.forEach(function(fold) {
-            ret.push("  " + fold.toString());
-        });
-        ret.push("]")
-        return ret.join("\n");
-    }
-
-    this.idxToPosition = function(idx) {
-        var lastFoldEndColumn = 0;
-        var fold;
-
-        for (var i = 0; i < this.folds.length; i++) {
-            var fold = this.folds[i];
-
-            idx -= fold.start.column - lastFoldEndColumn;
-            if (idx < 0) {
-                return {
-                    row: fold.start.row,
-                    column: fold.start.column + idx
-                };
-            }
-
-            idx -= fold.placeholder.length;
-            if (idx < 0) {
-                return fold.start;
-            }
-
-            lastFoldEndColumn = fold.end.column;
-        }
-
-        return {
-            row: this.end.row,
-            column: this.end.column + idx
-        };
-    }
-}).call(FoldLine.prototype);
-
-exports.FoldLine = FoldLine;
-});

+ 0 - 846
services/web/public/js/ace/edit_session/folding.js

@@ -1,846 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-var Range = require("../range").Range;
-var FoldLine = require("./fold_line").FoldLine;
-var Fold = require("./fold").Fold;
-var TokenIterator = require("../token_iterator").TokenIterator;
-
-function Folding() {
-    /*
-     * Looks up a fold at a given row/column. Possible values for side:
-     *   -1: ignore a fold if fold.start = row/column
-     *   +1: ignore a fold if fold.end = row/column
-     */
-    this.getFoldAt = function(row, column, side) {
-        var foldLine = this.getFoldLine(row);
-        if (!foldLine)
-            return null;
-
-        var folds = foldLine.folds;
-        for (var i = 0; i < folds.length; i++) {
-            var fold = folds[i];
-            if (fold.range.contains(row, column)) {
-                if (side == 1 && fold.range.isEnd(row, column)) {
-                    continue;
-                } else if (side == -1 && fold.range.isStart(row, column)) {
-                    continue;
-                }
-                return fold;
-            }
-        }
-    };
-
-    /*
-     * Returns all folds in the given range. Note, that this will return folds
-     *
-     */
-    this.getFoldsInRange = function(range) {
-        var start = range.start;
-        var end = range.end;
-        var foldLines = this.$foldData;
-        var foundFolds = [];
-
-        start.column += 1;
-        end.column -= 1;
-
-        for (var i = 0; i < foldLines.length; i++) {
-            var cmp = foldLines[i].range.compareRange(range);
-            if (cmp == 2) {
-                // Range is before foldLine. No intersection. This means,
-                // there might be other foldLines that intersect.
-                continue;
-            }
-            else if (cmp == -2) {
-                // Range is after foldLine. There can't be any other foldLines then,
-                // so let's give up.
-                break;
-            }
-
-            var folds = foldLines[i].folds;
-            for (var j = 0; j < folds.length; j++) {
-                var fold = folds[j];
-                cmp = fold.range.compareRange(range);
-                if (cmp == -2) {
-                    break;
-                } else if (cmp == 2) {
-                    continue;
-                } else
-                // WTF-state: Can happen due to -1/+1 to start/end column.
-                if (cmp == 42) {
-                    break;
-                }
-                foundFolds.push(fold);
-            }
-        }
-        start.column -= 1;
-        end.column += 1;
-
-        return foundFolds;
-    };
-
-    this.getFoldsInRangeList = function(ranges) {
-        if (Array.isArray(ranges)) {
-            var folds = [];
-            ranges.forEach(function(range) {
-                folds = folds.concat(this.getFoldsInRange(range));
-            }, this);
-        } else {
-            var folds = this.getFoldsInRange(ranges);
-        }
-        return folds;
-    }
-    
-    /*
-     * Returns all folds in the document
-     */
-    this.getAllFolds = function() {
-        var folds = [];
-        var foldLines = this.$foldData;
-        
-        for (var i = 0; i < foldLines.length; i++)
-            for (var j = 0; j < foldLines[i].folds.length; j++)
-                folds.push(foldLines[i].folds[j]);
-
-        return folds;
-    };
-
-    /*
-     * Returns the string between folds at the given position.
-     * E.g.
-     *  foo<fold>b|ar<fold>wolrd -> "bar"
-     *  foo<fold>bar<fold>wol|rd -> "world"
-     *  foo<fold>bar<fo|ld>wolrd -> <null>
-     *
-     * where | means the position of row/column
-     *
-     * The trim option determs if the return string should be trimed according
-     * to the "side" passed with the trim value:
-     *
-     * E.g.
-     *  foo<fold>b|ar<fold>wolrd -trim=-1> "b"
-     *  foo<fold>bar<fold>wol|rd -trim=+1> "rld"
-     *  fo|o<fold>bar<fold>wolrd -trim=00> "foo"
-     */
-    this.getFoldStringAt = function(row, column, trim, foldLine) {
-        foldLine = foldLine || this.getFoldLine(row);
-        if (!foldLine)
-            return null;
-
-        var lastFold = {
-            end: { column: 0 }
-        };
-        // TODO: Refactor to use getNextFoldTo function.
-        var str, fold;
-        for (var i = 0; i < foldLine.folds.length; i++) {
-            fold = foldLine.folds[i];
-            var cmp = fold.range.compareEnd(row, column);
-            if (cmp == -1) {
-                str = this
-                    .getLine(fold.start.row)
-                    .substring(lastFold.end.column, fold.start.column);
-                break;
-            }
-            else if (cmp === 0) {
-                return null;
-            }
-            lastFold = fold;
-        }
-        if (!str)
-            str = this.getLine(fold.start.row).substring(lastFold.end.column);
-
-        if (trim == -1)
-            return str.substring(0, column - lastFold.end.column);
-        else if (trim == 1)
-            return str.substring(column - lastFold.end.column);
-        else
-            return str;
-    };
-
-    this.getFoldLine = function(docRow, startFoldLine) {
-        var foldData = this.$foldData;
-        var i = 0;
-        if (startFoldLine)
-            i = foldData.indexOf(startFoldLine);
-        if (i == -1)
-            i = 0;
-        for (i; i < foldData.length; i++) {
-            var foldLine = foldData[i];
-            if (foldLine.start.row <= docRow && foldLine.end.row >= docRow) {
-                return foldLine;
-            } else if (foldLine.end.row > docRow) {
-                return null;
-            }
-        }
-        return null;
-    };
-
-    // returns the fold which starts after or contains docRow
-    this.getNextFoldLine = function(docRow, startFoldLine) {
-        var foldData = this.$foldData;
-        var i = 0;
-        if (startFoldLine)
-            i = foldData.indexOf(startFoldLine);
-        if (i == -1)
-            i = 0;
-        for (i; i < foldData.length; i++) {
-            var foldLine = foldData[i];
-            if (foldLine.end.row >= docRow) {
-                return foldLine;
-            }
-        }
-        return null;
-    };
-
-    this.getFoldedRowCount = function(first, last) {
-        var foldData = this.$foldData, rowCount = last-first+1;
-        for (var i = 0; i < foldData.length; i++) {
-            var foldLine = foldData[i],
-                end = foldLine.end.row,
-                start = foldLine.start.row;
-            if (end >= last) {
-                if(start < last) {
-                    if(start >= first)
-                        rowCount -= last-start;
-                    else
-                        rowCount = 0;//in one fold
-                }
-                break;
-            } else if(end >= first){
-                if (start >= first) //fold inside range
-                    rowCount -=  end-start;
-                else
-                    rowCount -=  end-first+1;
-            }
-        }
-        return rowCount;
-    };
-
-    this.$addFoldLine = function(foldLine) {
-        this.$foldData.push(foldLine);
-        this.$foldData.sort(function(a, b) {
-            return a.start.row - b.start.row;
-        });
-        return foldLine;
-    };
-
-    /**
-     * Adds a new fold.
-     *
-     * @returns
-     *      The new created Fold object or an existing fold object in case the
-     *      passed in range fits an existing fold exactly.
-     */
-    this.addFold = function(placeholder, range) {
-        var foldData = this.$foldData;
-        var added = false;
-        var fold;
-        
-        if (placeholder instanceof Fold)
-            fold = placeholder;
-        else {
-            fold = new Fold(range, placeholder);
-            fold.collapseChildren = range.collapseChildren;
-        }
-        this.$clipRangeToDocument(fold.range);
-
-        var startRow = fold.start.row;
-        var startColumn = fold.start.column;
-        var endRow = fold.end.row;
-        var endColumn = fold.end.column;
-
-        // --- Some checking ---
-        if (!(startRow < endRow || 
-            startRow == endRow && startColumn <= endColumn - 2))
-            throw new Error("The range has to be at least 2 characters width");
-
-        var startFold = this.getFoldAt(startRow, startColumn, 1);
-        var endFold = this.getFoldAt(endRow, endColumn, -1);
-        if (startFold && endFold == startFold)
-            return startFold.addSubFold(fold);
-
-        if (
-            (startFold && !startFold.range.isStart(startRow, startColumn))
-            || (endFold && !endFold.range.isEnd(endRow, endColumn))
-        ) {
-            throw new Error("A fold can't intersect already existing fold" + fold.range + startFold.range);
-        }
-
-        // Check if there are folds in the range we create the new fold for.
-        var folds = this.getFoldsInRange(fold.range);
-        if (folds.length > 0) {
-            // Remove the folds from fold data.
-            this.removeFolds(folds);
-            // Add the removed folds as subfolds on the new fold.
-            folds.forEach(function(subFold) {
-                fold.addSubFold(subFold);
-            });
-        }
-
-        for (var i = 0; i < foldData.length; i++) {
-            var foldLine = foldData[i];
-            if (endRow == foldLine.start.row) {
-                foldLine.addFold(fold);
-                added = true;
-                break;
-            } else if (startRow == foldLine.end.row) {
-                foldLine.addFold(fold);
-                added = true;
-                if (!fold.sameRow) {
-                    // Check if we might have to merge two FoldLines.
-                    var foldLineNext = foldData[i + 1];
-                    if (foldLineNext && foldLineNext.start.row == endRow) {
-                        // We need to merge!
-                        foldLine.merge(foldLineNext);
-                        break;
-                    }
-                }
-                break;
-            } else if (endRow <= foldLine.start.row) {
-                break;
-            }
-        }
-
-        if (!added)
-            foldLine = this.$addFoldLine(new FoldLine(this.$foldData, fold));
-
-        if (this.$useWrapMode)
-            this.$updateWrapData(foldLine.start.row, foldLine.start.row);
-        else
-            this.$updateRowLengthCache(foldLine.start.row, foldLine.start.row);
-
-        // Notify that fold data has changed.
-        this.$modified = true;
-        this._emit("changeFold", { data: fold, action: "add" });
-
-        return fold;
-    };
-
-    this.addFolds = function(folds) {
-        folds.forEach(function(fold) {
-            this.addFold(fold);
-        }, this);
-    };
-
-    this.removeFold = function(fold) {
-        var foldLine = fold.foldLine;
-        var startRow = foldLine.start.row;
-        var endRow = foldLine.end.row;
-
-        var foldLines = this.$foldData;
-        var folds = foldLine.folds;
-        // Simple case where there is only one fold in the FoldLine such that
-        // the entire fold line can get removed directly.
-        if (folds.length == 1) {
-            foldLines.splice(foldLines.indexOf(foldLine), 1);
-        } else
-        // If the fold is the last fold of the foldLine, just remove it.
-        if (foldLine.range.isEnd(fold.end.row, fold.end.column)) {
-            folds.pop();
-            foldLine.end.row = folds[folds.length - 1].end.row;
-            foldLine.end.column = folds[folds.length - 1].end.column;
-        } else
-        // If the fold is the first fold of the foldLine, just remove it.
-        if (foldLine.range.isStart(fold.start.row, fold.start.column)) {
-            folds.shift();
-            foldLine.start.row = folds[0].start.row;
-            foldLine.start.column = folds[0].start.column;
-        } else
-        // We know there are more then 2 folds and the fold is not at the edge.
-        // This means, the fold is somewhere in between.
-        //
-        // If the fold is in one row, we just can remove it.
-        if (fold.sameRow) {
-            folds.splice(folds.indexOf(fold), 1);
-        } else
-        // The fold goes over more then one row. This means remvoing this fold
-        // will cause the fold line to get splitted up. newFoldLine is the second part
-        {
-            var newFoldLine = foldLine.split(fold.start.row, fold.start.column);
-            folds = newFoldLine.folds;
-            folds.shift();
-            newFoldLine.start.row = folds[0].start.row;
-            newFoldLine.start.column = folds[0].start.column;
-        }
-
-        if (!this.$updating) {
-            if (this.$useWrapMode)
-                this.$updateWrapData(startRow, endRow);
-            else
-                this.$updateRowLengthCache(startRow, endRow);
-        }
-        
-        // Notify that fold data has changed.
-        this.$modified = true;
-        this._emit("changeFold", { data: fold, action: "remove" });
-    };
-
-    this.removeFolds = function(folds) {
-        // We need to clone the folds array passed in as it might be the folds
-        // array of a fold line and as we call this.removeFold(fold), folds
-        // are removed from folds and changes the current index.
-        var cloneFolds = [];
-        for (var i = 0; i < folds.length; i++) {
-            cloneFolds.push(folds[i]);
-        }
-
-        cloneFolds.forEach(function(fold) {
-            this.removeFold(fold);
-        }, this);
-        this.$modified = true;
-    };
-
-    this.expandFold = function(fold) {
-        this.removeFold(fold);
-        fold.subFolds.forEach(function(subFold) {
-            fold.restoreRange(subFold);
-            this.addFold(subFold);
-        }, this);
-        if (fold.collapseChildren > 0) {
-            this.foldAll(fold.start.row+1, fold.end.row, fold.collapseChildren-1);
-        }
-        fold.subFolds = [];
-    };
-
-    this.expandFolds = function(folds) {
-        folds.forEach(function(fold) {
-            this.expandFold(fold);
-        }, this);
-    };
-
-    this.unfold = function(location, expandInner) {
-        var range, folds;
-        if (location == null) {
-            range = new Range(0, 0, this.getLength(), 0);
-            expandInner = true;
-        } else if (typeof location == "number")
-            range = new Range(location, 0, location, this.getLine(location).length);
-        else if ("row" in location)
-            range = Range.fromPoints(location, location);
-        else
-            range = location;
-        
-        folds = this.getFoldsInRangeList(range);
-        if (expandInner) {
-            this.removeFolds(folds);
-        } else {
-            // TODO: might need to remove and add folds in one go instead of using
-            // expandFolds several times.
-            while (folds.length) {
-                this.expandFolds(folds);
-                folds = this.getFoldsInRangeList(range);
-            }
-        }
-    };
-
-    /*
-     * Checks if a given documentRow is folded. This is true if there are some
-     * folded parts such that some parts of the line is still visible.
-     **/
-    this.isRowFolded = function(docRow, startFoldRow) {
-        return !!this.getFoldLine(docRow, startFoldRow);
-    };
-
-    this.getRowFoldEnd = function(docRow, startFoldRow) {
-        var foldLine = this.getFoldLine(docRow, startFoldRow);
-        return foldLine ? foldLine.end.row : docRow;
-    };
-
-    this.getRowFoldStart = function(docRow, startFoldRow) {
-        var foldLine = this.getFoldLine(docRow, startFoldRow);
-        return foldLine ? foldLine.start.row : docRow;
-    };
-
-    this.getFoldDisplayLine = function(foldLine, endRow, endColumn, startRow, startColumn) {
-        if (startRow == null) {
-            startRow = foldLine.start.row;
-            startColumn = 0;
-        }
-
-        if (endRow == null) {
-            endRow = foldLine.end.row;
-            endColumn = this.getLine(endRow).length;
-        }
-
-        // Build the textline using the FoldLine walker.
-        var doc = this.doc;
-        var textLine = "";
-
-        foldLine.walk(function(placeholder, row, column, lastColumn) {
-            if (row < startRow)
-                return;
-            if (row == startRow) {
-                if (column < startColumn)
-                    return;
-                lastColumn = Math.max(startColumn, lastColumn);
-            }
-
-            if (placeholder != null) {
-                textLine += placeholder;
-            } else {
-                textLine += doc.getLine(row).substring(lastColumn, column);
-            }
-        }, endRow, endColumn);
-        return textLine;
-    };
-
-    this.getDisplayLine = function(row, endColumn, startRow, startColumn) {
-        var foldLine = this.getFoldLine(row);
-
-        if (!foldLine) {
-            var line;
-            line = this.doc.getLine(row);
-            return line.substring(startColumn || 0, endColumn || line.length);
-        } else {
-            return this.getFoldDisplayLine(
-                foldLine, row, endColumn, startRow, startColumn);
-        }
-    };
-
-    this.$cloneFoldData = function() {
-        var fd = [];
-        fd = this.$foldData.map(function(foldLine) {
-            var folds = foldLine.folds.map(function(fold) {
-                return fold.clone();
-            });
-            return new FoldLine(fd, folds);
-        });
-
-        return fd;
-    };
-
-    this.toggleFold = function(tryToUnfold) {
-        var selection = this.selection;
-        var range = selection.getRange();
-        var fold;
-        var bracketPos;
-
-        if (range.isEmpty()) {
-            var cursor = range.start;
-            fold = this.getFoldAt(cursor.row, cursor.column);
-
-            if (fold) {
-                this.expandFold(fold);
-                return;
-            } else if (bracketPos = this.findMatchingBracket(cursor)) {
-                if (range.comparePoint(bracketPos) == 1) {
-                    range.end = bracketPos;
-                } else {
-                    range.start = bracketPos;
-                    range.start.column++;
-                    range.end.column--;
-                }
-            } else if (bracketPos = this.findMatchingBracket({row: cursor.row, column: cursor.column + 1})) {
-                if (range.comparePoint(bracketPos) == 1)
-                    range.end = bracketPos;
-                else
-                    range.start = bracketPos;
-
-                range.start.column++;
-            } else {
-                range = this.getCommentFoldRange(cursor.row, cursor.column) || range;
-            }
-        } else {
-            var folds = this.getFoldsInRange(range);
-            if (tryToUnfold && folds.length) {
-                this.expandFolds(folds);
-                return;
-            } else if (folds.length == 1 ) {
-                fold = folds[0];
-            }
-        }
-
-        if (!fold)
-            fold = this.getFoldAt(range.start.row, range.start.column);
-
-        if (fold && fold.range.toString() == range.toString()) {
-            this.expandFold(fold);
-            return;
-        }
-
-        var placeholder = "...";
-        if (!range.isMultiLine()) {
-            placeholder = this.getTextRange(range);
-            if(placeholder.length < 4)
-                return;
-            placeholder = placeholder.trim().substring(0, 2) + "..";
-        }
-
-        this.addFold(placeholder, range);
-    };
-
-    this.getCommentFoldRange = function(row, column, dir) {
-        var iterator = new TokenIterator(this, row, column);
-        var token = iterator.getCurrentToken();
-        if (token && /^comment|string/.test(token.type)) {
-            var range = new Range();
-            var re = new RegExp(token.type.replace(/\..*/, "\\."));
-            if (dir != 1) {
-                do {
-                    token = iterator.stepBackward();
-                } while(token && re.test(token.type));
-                iterator.stepForward();
-            }
-            
-            range.start.row = iterator.getCurrentTokenRow();
-            range.start.column = iterator.getCurrentTokenColumn() + 2;
-
-            iterator = new TokenIterator(this, row, column);
-            
-            if (dir != -1) {
-                do {
-                    token = iterator.stepForward();
-                } while(token && re.test(token.type));
-                token = iterator.stepBackward();
-            } else
-                token = iterator.getCurrentToken();
-
-            range.end.row = iterator.getCurrentTokenRow();
-            range.end.column = iterator.getCurrentTokenColumn() + token.value.length - 2;
-            return range;
-        }
-    };
-
-    this.foldAll = function(startRow, endRow, depth) {
-        if (depth == undefined)
-            depth = 100000; // JSON.stringify doesn't hanle Infinity
-        var foldWidgets = this.foldWidgets;
-        if (!foldWidgets)
-            return; // mode doesn't support folding
-        endRow = endRow || this.getLength();
-        startRow = startRow || 0;
-        for (var row = startRow; row < endRow; row++) {
-            if (foldWidgets[row] == null)
-                foldWidgets[row] = this.getFoldWidget(row);
-            if (foldWidgets[row] != "start")
-                continue;
-
-            var range = this.getFoldWidgetRange(row);
-            // sometimes range can be incompatible with existing fold
-            // TODO change addFold to return null istead of throwing
-            if (range && range.isMultiLine()
-                && range.end.row <= endRow
-                && range.start.row >= startRow
-            ) {
-                row = range.end.row;
-                try {
-                    // addFold can change the range
-                    var fold = this.addFold("...", range);
-                    if (fold)
-                        fold.collapseChildren = depth;
-                } catch(e) {}
-            }
-        }
-    };
-    
-    // structured folding
-    this.$foldStyles = {
-        "manual": 1,
-        "markbegin": 1,
-        "markbeginend": 1
-    };
-    this.$foldStyle = "markbegin";
-    this.setFoldStyle = function(style) {
-        if (!this.$foldStyles[style])
-            throw new Error("invalid fold style: " + style + "[" + Object.keys(this.$foldStyles).join(", ") + "]");
-        
-        if (this.$foldStyle == style)
-            return;
-
-        this.$foldStyle = style;
-        
-        if (style == "manual")
-            this.unfold();
-        
-        // reset folding
-        var mode = this.$foldMode;
-        this.$setFolding(null);
-        this.$setFolding(mode);
-    };
-
-    this.$setFolding = function(foldMode) {
-        if (this.$foldMode == foldMode)
-            return;
-            
-        this.$foldMode = foldMode;
-        
-        this.removeListener('change', this.$updateFoldWidgets);
-        this._emit("changeAnnotation");
-        
-        if (!foldMode || this.$foldStyle == "manual") {
-            this.foldWidgets = null;
-            return;
-        }
-        
-        this.foldWidgets = [];
-        this.getFoldWidget = foldMode.getFoldWidget.bind(foldMode, this, this.$foldStyle);
-        this.getFoldWidgetRange = foldMode.getFoldWidgetRange.bind(foldMode, this, this.$foldStyle);
-        
-        this.$updateFoldWidgets = this.updateFoldWidgets.bind(this);
-        this.on('change', this.$updateFoldWidgets);
-        
-    };
-
-    this.getParentFoldRangeData = function (row, ignoreCurrent) {
-        var fw = this.foldWidgets;
-        if (!fw || (ignoreCurrent && fw[row]))
-            return {};
-
-        var i = row - 1, firstRange;
-        while (i >= 0) {
-            var c = fw[i];
-            if (c == null)
-                c = fw[i] = this.getFoldWidget(i);
-
-            if (c == "start") {
-                var range = this.getFoldWidgetRange(i);
-                if (!firstRange)
-                    firstRange = range;
-                if (range && range.end.row >= row)
-                    break;
-            }
-            i--;
-        }
-
-        return {
-            range: i !== -1 && range,
-            firstRange: firstRange
-        };
-    }
-
-    this.onFoldWidgetClick = function(row, e) {
-        e = e.domEvent;
-        var options = {
-            children: e.shiftKey,
-            all: e.ctrlKey || e.metaKey,
-            siblings: e.altKey
-        };
-        
-        var range = this.$toggleFoldWidget(row, options);
-        if (!range)
-            (e.target || e.srcElement).className += " ace_invalid";
-    };
-    
-    this.$toggleFoldWidget = function(row, options) {
-        if (!this.getFoldWidget)
-            return;
-        var type = this.getFoldWidget(row);
-        var line = this.getLine(row);
-
-        var dir = type === "end" ? -1 : 1;
-        var fold = this.getFoldAt(row, dir === -1 ? 0 : line.length, dir);
-
-        if (fold) {
-            if (options.children || options.all)
-                this.removeFold(fold);
-            else
-                this.expandFold(fold);
-            return;
-        }
-
-        var range = this.getFoldWidgetRange(row, true);
-        // sometimes singleline folds can be missed by the code above
-        if (range && !range.isMultiLine()) {
-            fold = this.getFoldAt(range.start.row, range.start.column, 1);
-            if (fold && range.isEqual(fold.range)) {
-                this.removeFold(fold);
-                return;
-            }
-        }
-        
-        if (options.siblings) {
-            var data = this.getParentFoldRangeData(row);
-            if (data.range) {
-                var startRow = data.range.start.row + 1;
-                var endRow = data.range.end.row;
-            }
-            this.foldAll(startRow, endRow, options.all ? 10000 : 0);
-        } else if (options.children) {
-            endRow = range ? range.end.row : this.getLength();
-            this.foldAll(row + 1, range.end.row, options.all ? 10000 : 0);
-        } else if (range) {
-            if (options.all) 
-                range.collapseChildren = 10000;
-            this.addFold("...", range);
-        }
-        
-        return range;
-    };
-    
-    
-    
-    this.toggleFoldWidget = function(toggleParent) {
-        var row = this.selection.getCursor().row;
-        row = this.getRowFoldStart(row);
-        var range = this.$toggleFoldWidget(row, {});
-        
-        if (range)
-            return;
-        // handle toggleParent
-        var data = this.getParentFoldRangeData(row, true);
-        range = data.range || data.firstRange;
-        
-        if (range) {
-            row = range.start.row;
-            var fold = this.getFoldAt(row, this.getLine(row).length, 1);
-
-            if (fold) {
-                this.removeFold(fold);
-            } else {
-                this.addFold("...", range);
-            }
-        }
-    };
-
-    this.updateFoldWidgets = function(e) {
-        var delta = e.data;
-        var range = delta.range;
-        var firstRow = range.start.row;
-        var len = range.end.row - firstRow;
-
-        if (len === 0) {
-            this.foldWidgets[firstRow] = null;
-        } else if (delta.action == "removeText" || delta.action == "removeLines") {
-            this.foldWidgets.splice(firstRow, len + 1, null);
-        } else {
-            var args = Array(len + 1);
-            args.unshift(firstRow, 1);
-            this.foldWidgets.splice.apply(this.foldWidgets, args);
-        }
-    };
-
-}
-
-exports.Folding = Folding;
-
-});

+ 0 - 1075
services/web/public/js/ace/edit_session_test.js

@@ -1,1075 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-    require("./test/mockdom");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var lang = require("./lib/lang");
-var EditSession = require("./edit_session").EditSession;
-var Editor = require("./editor").Editor;
-var UndoManager = require("./undomanager").UndoManager;
-var MockRenderer = require("./test/mockrenderer").MockRenderer;
-var Range = require("./range").Range;
-var assert = require("./test/assertions");
-var JavaScriptMode = require("./mode/javascript").Mode;
-
-function createFoldTestSession() {
-    var lines = [
-        "function foo(items) {",
-        "    for (var i=0; i<items.length; i++) {",
-        "        alert(items[i] + \"juhu\");",
-        "    }    // Real Tab.",
-        "}"
-    ];
-    var session = new EditSession(lines.join("\n"));
-    session.setUndoManager(new UndoManager());
-    session.addFold("args...", new Range(0, 13, 0, 18));
-    session.addFold("foo...", new Range(1, 10, 2, 10));
-    session.addFold("bar...", new Range(2, 20, 2, 25));
-    return session;
-}
-
-function assertArray(a, b) {
-    assert.equal(a+"", b+"");
-    assert.ok(a.length == b.length);
-    for (var i = 0; i < a.length; i++) {
-        assert.equal(a[i], b[i]);
-    }
-}
-
-module.exports = {
-
-   "test: find matching opening bracket in Text mode" : function() {
-        var session = new EditSession(["(()(", "())))"]);
-
-        assert.position(session.findMatchingBracket({row: 0, column: 3}), 0, 1);
-        assert.position(session.findMatchingBracket({row: 1, column: 2}), 1, 0);
-        assert.position(session.findMatchingBracket({row: 1, column: 3}), 0, 3);
-        assert.position(session.findMatchingBracket({row: 1, column: 4}), 0, 0);
-        assert.equal(session.findMatchingBracket({row: 1, column: 5}), null);
-    },
-
-    "test: find matching closing bracket in Text mode" : function() {
-        var session = new EditSession(["(()(", "())))"]);
-
-        assert.position(session.findMatchingBracket({row: 1, column: 1}), 1, 1);
-        assert.position(session.findMatchingBracket({row: 1, column: 1}), 1, 1);
-        assert.position(session.findMatchingBracket({row: 0, column: 4}), 1, 2);
-        assert.position(session.findMatchingBracket({row: 0, column: 2}), 0, 2);
-        assert.position(session.findMatchingBracket({row: 0, column: 1}), 1, 3);
-        assert.equal(session.findMatchingBracket({row: 0, column: 0}), null);
-    },
-
-    "test: find matching opening bracket in JavaScript mode" : function() {
-        var lines = [
-            "function foo() {",
-            "    var str = \"{ foo()\";",
-            "    if (debug) {",
-            "        // write str (a string) to the console",
-            "        console.log(str);",
-            "    }",
-            "    str += \" bar() }\";",
-            "}"
-        ];
-        var session = new EditSession(lines.join("\n"), new JavaScriptMode());
-
-        assert.position(session.findMatchingBracket({row: 0, column: 14}), 0, 12);
-        assert.position(session.findMatchingBracket({row: 7, column: 1}), 0, 15);
-        assert.position(session.findMatchingBracket({row: 6, column: 20}), 1, 15);
-        assert.position(session.findMatchingBracket({row: 1, column: 22}), 1, 20);
-        assert.position(session.findMatchingBracket({row: 3, column: 31}), 3, 21);
-        assert.position(session.findMatchingBracket({row: 4, column: 24}), 4, 19);
-        assert.equal(session.findMatchingBracket({row: 0, column: 1}), null);
-    },
-
-    "test: find matching closing bracket in JavaScript mode" : function() {
-        var lines = [
-            "function foo() {",
-            "    var str = \"{ foo()\";",
-            "    if (debug) {",
-            "        // write str (a string) to the console",
-            "        console.log(str);",
-            "    }",
-            "    str += \" bar() }\";",
-            "}"
-        ];
-        var session = new EditSession(lines.join("\n"), new JavaScriptMode());
-
-        assert.position(session.findMatchingBracket({row: 0, column: 13}), 0, 13);
-        assert.position(session.findMatchingBracket({row: 0, column: 16}), 7, 0);
-        assert.position(session.findMatchingBracket({row: 1, column: 16}), 6, 19);
-        assert.position(session.findMatchingBracket({row: 1, column: 21}), 1, 21);
-        assert.position(session.findMatchingBracket({row: 3, column: 22}), 3, 30);
-        assert.position(session.findMatchingBracket({row: 4, column: 20}), 4, 23);
-    },
-
-    "test: handle unbalanced brackets in JavaScript mode" : function() {
-        var lines = [
-            "function foo() {",
-            "    var str = \"{ foo()\";",
-            "    if (debug) {",
-            "        // write str a string) to the console",
-            "        console.log(str);",
-            "    ",
-            "    str += \" bar() \";",
-            "}"
-        ];
-        var session = new EditSession(lines.join("\n"), new JavaScriptMode());
-
-        assert.equal(session.findMatchingBracket({row: 0, column: 16}), null);
-        assert.equal(session.findMatchingBracket({row: 3, column: 30}), null);
-        assert.equal(session.findMatchingBracket({row: 1, column: 16}), null);
-    },
-
-    "test: match different bracket types" : function() {
-        var session = new EditSession(["({[", ")]}"]);
-
-        assert.position(session.findMatchingBracket({row: 0, column: 1}), 1, 0);
-        assert.position(session.findMatchingBracket({row: 0, column: 2}), 1, 2);
-        assert.position(session.findMatchingBracket({row: 0, column: 3}), 1, 1);
-
-        assert.position(session.findMatchingBracket({row: 1, column: 1}), 0, 0);
-        assert.position(session.findMatchingBracket({row: 1, column: 2}), 0, 2);
-        assert.position(session.findMatchingBracket({row: 1, column: 3}), 0, 1);
-    },
-
-    "test: move lines down" : function() {
-        var session = new EditSession(["a1", "a2", "a3", "a4"]);
-
-        session.moveLinesDown(0, 1);
-        assert.equal(session.getValue(), ["a3", "a1", "a2", "a4"].join("\n"));
-
-        session.moveLinesDown(1, 2);
-        assert.equal(session.getValue(), ["a3", "a4", "a1", "a2"].join("\n"));
-
-        session.moveLinesDown(2, 3);
-        assert.equal(session.getValue(), ["a3", "a4", "a1", "a2"].join("\n"));
-
-        session.moveLinesDown(2, 2);
-        assert.equal(session.getValue(), ["a3", "a4", "a2", "a1"].join("\n"));
-    },
-
-    "test: move lines up" : function() {
-        var session = new EditSession(["a1", "a2", "a3", "a4"]);
-
-        session.moveLinesUp(2, 3);
-        assert.equal(session.getValue(), ["a1", "a3", "a4", "a2"].join("\n"));
-
-        session.moveLinesUp(1, 2);
-        assert.equal(session.getValue(), ["a3", "a4", "a1", "a2"].join("\n"));
-
-        session.moveLinesUp(0, 1);
-        assert.equal(session.getValue(), ["a3", "a4", "a1", "a2"].join("\n"));
-
-        session.moveLinesUp(2, 2);
-        assert.equal(session.getValue(), ["a3", "a1", "a4", "a2"].join("\n"));
-    },
-
-    "test: duplicate lines" : function() {
-        var session = new EditSession(["1", "2", "3", "4"]);
-
-        session.duplicateLines(1, 2);
-        assert.equal(session.getValue(), ["1", "2", "3", "2", "3", "4"].join("\n"));
-    },
-
-    "test: duplicate last line" : function() {
-        var session = new EditSession(["1", "2", "3"]);
-
-        session.duplicateLines(2, 2);
-        assert.equal(session.getValue(), ["1", "2", "3", "3"].join("\n"));
-    },
-
-    "test: duplicate first line" : function() {
-        var session = new EditSession(["1", "2", "3"]);
-
-        session.duplicateLines(0, 0);
-        assert.equal(session.getValue(), ["1", "1", "2", "3"].join("\n"));
-    },
-
-    "test: getScreenLastRowColumn": function() {
-        var session = new EditSession([
-            "juhu",
-            "12\t\t34",
-            "ぁぁa"
-        ]);
-
-        assert.equal(session.getScreenLastRowColumn(0), 4);
-        assert.equal(session.getScreenLastRowColumn(1), 10);
-        assert.equal(session.getScreenLastRowColumn(2), 5);
-    },
-
-    "test: convert document to screen coordinates" : function() {
-        var session = new EditSession("01234\t567890\t1234");
-        session.setTabSize(4);
-
-        assert.equal(session.documentToScreenColumn(0, 0), 0);
-        assert.equal(session.documentToScreenColumn(0, 4), 4);
-        assert.equal(session.documentToScreenColumn(0, 5), 5);
-        assert.equal(session.documentToScreenColumn(0, 6), 8);
-        assert.equal(session.documentToScreenColumn(0, 12), 14);
-        assert.equal(session.documentToScreenColumn(0, 13), 16);
-
-        session.setTabSize(2);
-
-        assert.equal(session.documentToScreenColumn(0, 0), 0);
-        assert.equal(session.documentToScreenColumn(0, 4), 4);
-        assert.equal(session.documentToScreenColumn(0, 5), 5);
-        assert.equal(session.documentToScreenColumn(0, 6), 6);
-        assert.equal(session.documentToScreenColumn(0, 7), 7);
-        assert.equal(session.documentToScreenColumn(0, 12), 12);
-        assert.equal(session.documentToScreenColumn(0, 13), 14);
-    },
-
-    "test: convert document to screen coordinates with leading tabs": function() {
-        var session = new EditSession("\t\t123");
-        session.setTabSize(4);
-
-        assert.equal(session.documentToScreenColumn(0, 0), 0);
-        assert.equal(session.documentToScreenColumn(0, 1), 4);
-        assert.equal(session.documentToScreenColumn(0, 2), 8);
-        assert.equal(session.documentToScreenColumn(0, 3), 9);
-    },
-
-    "test: documentToScreen without soft wrap": function() {
-        var session = new EditSession([
-            "juhu",
-            "12\t\t34",
-            "ぁぁa"
-        ]);
-
-        assert.position(session.documentToScreenPosition(0, 3), 0, 3);
-        assert.position(session.documentToScreenPosition(1, 3), 1, 4);
-        assert.position(session.documentToScreenPosition(1, 4), 1, 8);
-        assert.position(session.documentToScreenPosition(2, 2), 2, 4);
-    },
-
-    "test: documentToScreen with soft wrap": function() {
-        var session = new EditSession(["foo bar foo bar"]);
-        session.setUseWrapMode(true);
-        session.setWrapLimitRange(12, 12);
-        session.adjustWrapLimit(80);
-
-        assert.position(session.documentToScreenPosition(0, 11), 0, 11);
-        assert.position(session.documentToScreenPosition(0, 12), 1, 0);
-    },
-
-    "test: documentToScreen with soft wrap and multibyte characters": function() {
-        var session = new EditSession(["ぁぁa"]);
-        session.setUseWrapMode(true);
-        session.setWrapLimitRange(2, 2);
-        session.adjustWrapLimit(80);
-
-        assert.position(session.documentToScreenPosition(0, 1), 1, 0);
-        assert.position(session.documentToScreenPosition(0, 2), 2, 0);
-        assert.position(session.documentToScreenPosition(0, 4), 2, 1);
-    },
-
-    "test: documentToScreen should clip position to the document boundaries": function() {
-        var session = new EditSession("foo bar\njuhu kinners");
-
-        assert.position(session.documentToScreenPosition(-1, 4), 0, 0);
-        assert.position(session.documentToScreenPosition(3, 0), 1, 12);
-    },
-
-    "test: convert screen to document coordinates" : function() {
-        var session = new EditSession("01234\t567890\t1234");
-        session.setTabSize(4);
-
-        assert.equal(session.screenToDocumentColumn(0, 0), 0);
-        assert.equal(session.screenToDocumentColumn(0, 4), 4);
-        assert.equal(session.screenToDocumentColumn(0, 5), 5);
-        assert.equal(session.screenToDocumentColumn(0, 6), 5);
-        assert.equal(session.screenToDocumentColumn(0, 7), 5);
-        assert.equal(session.screenToDocumentColumn(0, 8), 6);
-        assert.equal(session.screenToDocumentColumn(0, 9), 7);
-        assert.equal(session.screenToDocumentColumn(0, 15), 12);
-        assert.equal(session.screenToDocumentColumn(0, 19), 16);
-
-        session.setTabSize(2);
-
-        assert.equal(session.screenToDocumentColumn(0, 0), 0);
-        assert.equal(session.screenToDocumentColumn(0, 4), 4);
-        assert.equal(session.screenToDocumentColumn(0, 5), 5);
-        assert.equal(session.screenToDocumentColumn(0, 6), 6);
-        assert.equal(session.screenToDocumentColumn(0, 12), 12);
-        assert.equal(session.screenToDocumentColumn(0, 13), 12);
-        assert.equal(session.screenToDocumentColumn(0, 14), 13);
-    },
-
-    "test: screenToDocument with soft wrap": function() {
-        var session = new EditSession(["foo bar foo bar"]);
-        session.setUseWrapMode(true);
-        session.setWrapLimitRange(12, 12);
-        session.adjustWrapLimit(80);
-
-        assert.position(session.screenToDocumentPosition(1, 0), 0, 12);
-        assert.position(session.screenToDocumentPosition(0, 11), 0, 11);
-        // Check if the position is clamped the right way.
-        assert.position(session.screenToDocumentPosition(0, 12), 0, 11);
-        assert.position(session.screenToDocumentPosition(0, 20), 0, 11);
-    },
-
-    "test: screenToDocument with soft wrap and multi byte characters": function() {
-        var session = new EditSession(["ぁ a"]);
-        session.setUseWrapMode(true);
-        session.adjustWrapLimit(80);
-
-        assert.position(session.screenToDocumentPosition(0, 1), 0, 0);
-        assert.position(session.screenToDocumentPosition(0, 2), 0, 1);
-        assert.position(session.screenToDocumentPosition(0, 3), 0, 2);
-        assert.position(session.screenToDocumentPosition(0, 4), 0, 3);
-        assert.position(session.screenToDocumentPosition(0, 5), 0, 3);
-    },
-
-    "test: screenToDocument should clip position to the document boundaries": function() {
-        var session = new EditSession("foo bar\njuhu kinners");
-
-        assert.position(session.screenToDocumentPosition(-1, 4), 0, 0);
-        assert.position(session.screenToDocumentPosition(0, -1), 0, 0);
-        assert.position(session.screenToDocumentPosition(0, 30), 0, 7);
-        assert.position(session.screenToDocumentPosition(2, 4), 1, 12);
-        assert.position(session.screenToDocumentPosition(1, 30), 1, 12);
-        assert.position(session.screenToDocumentPosition(20, 50), 1, 12);
-        assert.position(session.screenToDocumentPosition(20, 5), 1, 12);
-
-        // and the same for folded rows
-        session.addFold("...", new Range(0,1,1,3));
-        assert.position(session.screenToDocumentPosition(1, 2), 1, 12);
-        // for wrapped rows
-        session.setUseWrapMode(true);
-        session.setWrapLimitRange(5,5);
-        assert.position(session.screenToDocumentPosition(4, 1), 1, 12);
-    },
-
-    "test: wrapLine split function" : function() {
-        function computeAndAssert(line, assertEqual, wrapLimit, tabSize) {
-            wrapLimit = wrapLimit || 12;
-            tabSize = tabSize || 4;
-            line = lang.stringTrimRight(line);
-            var tokens = EditSession.prototype.$getDisplayTokens(line);
-            var splits = EditSession.prototype.$computeWrapSplits(tokens, wrapLimit, tabSize);
-            // console.log("String:", line, "Result:", splits, "Expected:", assertEqual);
-            assert.ok(splits.length == assertEqual.length);
-            for (var i = 0; i < splits.length; i++) {
-                assert.ok(splits[i] == assertEqual[i]);
-            }
-        }
-        
-        EditSession.prototype.$wrapAsCode = true;
-        // Basic splitting.
-        computeAndAssert("foo bar foo bar", [ 12 ]);
-        computeAndAssert("foo bar f   bar", [ 12 ]);
-        computeAndAssert("foo bar f     r", [ 12 ]); // 14 if we enable 
-        computeAndAssert("foo bar foo bar foo bara foo", [12, 25]);
-
-        // Don't split if there is only whitespaces/tabs at the end of the line.
-        computeAndAssert("foo foo foo \t \t", [ ]);
-
-        // If there is no space to split, force split.
-        computeAndAssert("foooooooooooooo", [ 12 ]);
-        computeAndAssert("fooooooooooooooooooooooooooo", [12, 24]);
-        computeAndAssert("foo bar fooooooooooobooooooo", [8,  20]);
-
-        // Basic splitting + tabs.
-        computeAndAssert("foo \t\tbar", [ 6 ]);
-        computeAndAssert("foo \t \tbar", [ 7 ]);
-
-        // Ignore spaces/tabs at beginning of split.
-        computeAndAssert("foo \t \t   \t \t bar", [ 7 ]); // 14
-
-        // Test wrapping for asian characters.
-        computeAndAssert("ぁぁ", [1], 2);
-        computeAndAssert(" ぁぁ", [1, 2], 2);
-        computeAndAssert(" ぁ\tぁ", [1, 3], 2);
-        computeAndAssert(" ぁぁ\tぁ", [1, 4], 4);
-
-        // Test wrapping for punctuation.
-        computeAndAssert(" ab.c;ef++", [1, 3, 5, 7, 8], 2);
-        computeAndAssert(" a.b", [1, 2, 3], 1);
-        computeAndAssert("#>>", [1, 2], 1);
-        
-        // Test wrapping for punctuation in
-        EditSession.prototype.$wrapAsCode = false;
-        computeAndAssert("ab cde, Juhu kinners", [3, 8, 13, 19], 6);
-    },
-
-    "test get longest line" : function() {
-        var session = new EditSession(["12"]);
-        session.setTabSize(4);
-        assert.equal(session.getScreenWidth(), 2);
-
-        session.doc.insertNewLine({row: 0, column: Infinity});
-        session.doc.insertLines(1, ["123"]);
-        assert.equal(session.getScreenWidth(), 3);
-
-        session.doc.insertNewLine({row: 0, column: Infinity});
-        session.doc.insertLines(1, ["\t\t"]);
-
-        assert.equal(session.getScreenWidth(), 8);
-
-        session.setTabSize(2);
-        assert.equal(session.getScreenWidth(), 4);
-    },
-
-    "test getDisplayString": function() {
-        var session = new EditSession(["12"]);
-        session.setTabSize(4);
-
-        assert.equal(session.$getDisplayTokens("\t").length, 4);
-        assert.equal(session.$getDisplayTokens("abc").length, 3);
-        assert.equal(session.$getDisplayTokens("abc\t").length, 4);
-    },
-
-    "test issue 83": function() {
-        var session = new EditSession("");
-        var editor = new Editor(new MockRenderer(), session);
-        var document = session.getDocument();
-
-        session.setUseWrapMode(true);
-
-        document.insertLines(0, ["a", "b"]);
-        document.insertLines(2, ["c", "d"]);
-        document.removeLines(1, 2);
-    },
-
-    "test wrapMode init has to create wrapData array": function() {
-        var session = new EditSession("foo bar\nfoo bar");
-        var editor = new Editor(new MockRenderer(), session);
-        var document = session.getDocument();
-
-        session.setUseWrapMode(true);
-        session.setWrapLimitRange(3, 3);
-        session.adjustWrapLimit(80);
-
-        // Test if wrapData is there and was computed.
-        assert.equal(session.$wrapData.length, 2);
-        assert.equal(session.$wrapData[0].length, 1);
-        assert.equal(session.$wrapData[1].length, 1);
-    },
-
-    "test first line blank with wrap": function() {
-        var session = new EditSession("\nfoo");
-        session.setUseWrapMode(true);
-        assert.equal(session.doc.getValue(), ["", "foo"].join("\n"));
-    },
-
-    "test first line blank with wrap 2" : function() {
-        var session = new EditSession("");
-        session.setUseWrapMode(true);
-        session.setValue("\nfoo");
-
-        assert.equal(session.doc.getValue(), ["", "foo"].join("\n"));
-    },
-
-    "test fold getFoldDisplayLine": function() {
-        var session = createFoldTestSession();
-        function assertDisplayLine(foldLine, str) {
-            var line = session.getLine(foldLine.end.row);
-            var displayLine =
-                session.getFoldDisplayLine(foldLine, foldLine.end.row, line.length);
-            assert.equal(displayLine, str);
-        }
-
-        assertDisplayLine(session.$foldData[0], "function foo(args...) {")
-        assertDisplayLine(session.$foldData[1], "    for (vfoo...ert(items[bar...\"juhu\");");
-    },
-
-    "test foldLine idxToPosition": function() {
-        var session = createFoldTestSession();
-
-        function assertIdx2Pos(foldLineIdx, idx, row, column) {
-            var foldLine = session.$foldData[foldLineIdx];
-            assert.position(foldLine.idxToPosition(idx), row, column);
-        }
-
-//        "function foo(items) {",
-//        "    for (var i=0; i<items.length; i++) {",
-//        "        alert(items[i] + \"juhu\");",
-//        "    }    // Real Tab.",
-//        "}"
-
-        assertIdx2Pos(0, 12, 0, 12);
-        assertIdx2Pos(0, 13, 0, 13);
-        assertIdx2Pos(0, 14, 0, 13);
-        assertIdx2Pos(0, 19, 0, 13);
-        assertIdx2Pos(0, 20, 0, 18);
-
-        assertIdx2Pos(1, 10, 1, 10);
-        assertIdx2Pos(1, 11, 1, 10);
-        assertIdx2Pos(1, 15, 1, 10);
-        assertIdx2Pos(1, 16, 2, 10);
-        assertIdx2Pos(1, 26, 2, 20);
-        assertIdx2Pos(1, 27, 2, 20);
-        assertIdx2Pos(1, 32, 2, 25);
-    },
-
-    "test fold documentToScreen": function() {
-        var session = createFoldTestSession();
-        function assertDoc2Screen(docRow, docCol, screenRow, screenCol) {
-            assert.position(
-                session.documentToScreenPosition(docRow, docCol),
-                screenRow, screenCol
-            );
-        }
-
-        // One fold ending in the same row.
-        assertDoc2Screen(0,  0, 0, 0);
-        assertDoc2Screen(0, 13, 0, 13);
-        assertDoc2Screen(0, 14, 0, 13);
-        assertDoc2Screen(0, 17, 0, 13);
-        assertDoc2Screen(0, 18, 0, 20);
-
-        // Fold ending on some other row.
-        assertDoc2Screen(1,  0, 1, 0);
-        assertDoc2Screen(1, 10, 1, 10);
-        assertDoc2Screen(1, 11, 1, 10);
-        assertDoc2Screen(1, 99, 1, 10);
-
-        assertDoc2Screen(2,  0, 1, 10);
-        assertDoc2Screen(2,  9, 1, 10);
-        assertDoc2Screen(2, 10, 1, 16);
-        assertDoc2Screen(2, 11, 1, 17);
-
-        // Fold in the same row with fold over more then one row in the same row.
-        assertDoc2Screen(2, 19, 1, 25);
-        assertDoc2Screen(2, 20, 1, 26);
-        assertDoc2Screen(2, 21, 1, 26);
-
-        assertDoc2Screen(2, 24, 1, 26);
-        assertDoc2Screen(2, 25, 1, 32);
-        assertDoc2Screen(2, 26, 1, 33);
-        assertDoc2Screen(2, 99, 1, 40);
-
-        // Test one position after the folds. Should be all like normal.
-        assertDoc2Screen(3,  0, 2,  0);
-    },
-
-    "test fold screenToDocument": function() {
-        var session = createFoldTestSession();
-        function assertScreen2Doc(docRow, docCol, screenRow, screenCol) {
-            assert.position(
-                session.screenToDocumentPosition(screenRow, screenCol),
-                docRow, docCol
-            );
-        }
-
-        // One fold ending in the same row.
-        assertScreen2Doc(0,  0, 0, 0);
-        assertScreen2Doc(0, 13, 0, 13);
-        assertScreen2Doc(0, 13, 0, 14);
-        assertScreen2Doc(0, 18, 0, 20);
-        assertScreen2Doc(0, 19, 0, 21);
-
-        // Fold ending on some other row.
-        assertScreen2Doc(1,  0, 1, 0);
-        assertScreen2Doc(1, 10, 1, 10);
-        assertScreen2Doc(1, 10, 1, 11);
-
-        assertScreen2Doc(1, 10, 1, 15);
-        assertScreen2Doc(2, 10, 1, 16);
-        assertScreen2Doc(2, 11, 1, 17);
-
-        // Fold in the same row with fold over more then one row in the same row.
-        assertScreen2Doc(2, 19, 1, 25);
-        assertScreen2Doc(2, 20, 1, 26);
-        assertScreen2Doc(2, 20, 1, 27);
-
-        assertScreen2Doc(2, 20, 1, 31);
-        assertScreen2Doc(2, 25, 1, 32);
-        assertScreen2Doc(2, 26, 1, 33);
-        assertScreen2Doc(2, 33, 1, 99);
-
-        // Test one position after the folds. Should be all like normal.
-        assertScreen2Doc(3,  0, 2,  0);
-    },
-
-    "test getFoldsInRange()": function() {
-        var session = createFoldTestSession();
-        var foldLines = session.$foldData;
-        var folds = foldLines[0].folds.concat(foldLines[1].folds);
-
-        function test(startRow, startColumn, endColumn, endRow, folds) {
-            var r = new Range(startRow, startColumn, endColumn, endRow);
-            var retFolds = session.getFoldsInRange(r);
-
-            assert.ok(retFolds.length == folds.length);
-            for (var i = 0; i < retFolds.length; i++) {
-                assert.equal(retFolds[i].range + "", folds[i].range + "");
-            }
-        }
-
-        test(0, 0, 0, 13,  [ ]);
-        test(0, 0, 0, 14,  [ folds[0] ]);
-        test(0, 0, 0, 18,  [ folds[0] ]);
-        test(0, 0, 1, 10,  [ folds[0] ]);
-        test(0, 0, 1, 11,  [ folds[0], folds[1] ]);
-        test(0, 18, 1, 11, [ folds[1] ]);
-        test(2, 0,  2, 13, [ folds[1] ]);
-        test(2, 10, 2, 20, [ ]);
-        test(2, 10, 2, 11, [ ]);
-        test(2, 19, 2, 20, [ ]);
-    },
-
-    "test fold one-line text insert": function() {
-        // These are mostly test for the FoldLine.addRemoveChars function.
-        var session = createFoldTestSession();
-        var undoManager = session.getUndoManager();
-        var foldLines = session.$foldData;
-
-        function insert(row, column, text) {
-            session.insert({row: row, column: column}, text);
-
-            // Force the session to store all changes made to the document NOW
-            // on the undoManager's queue. Otherwise we can't undo in separate
-            // steps later.
-            session.$syncInformUndoManager();
-        }
-
-        var foldLine, fold, folds;
-        // First line.
-        foldLine = session.$foldData[0];
-        fold = foldLine.folds[0];
-
-        insert(0, 0, "0");
-        assert.range(foldLine.range, 0, 14, 0, 19);
-        assert.range(fold.range,     0, 14, 0, 19);
-        insert(0, 14, "1");
-        assert.range(foldLine.range, 0, 15, 0, 20);
-        assert.range(fold.range,     0, 15, 0, 20);
-        insert(0, 20, "2");
-        assert.range(foldLine.range, 0, 15, 0, 20);
-        assert.range(fold.range,     0, 15, 0, 20);
-
-        // Second line.
-        foldLine = session.$foldData[1];
-        folds = foldLine.folds;
-
-        insert(1, 0, "3");
-        assert.range(foldLine.range, 1, 11, 2, 25);
-        assert.range(folds[0].range, 1, 11, 2, 10);
-        assert.range(folds[1].range, 2, 20, 2, 25);
-
-        insert(1, 11, "4");
-        assert.range(foldLine.range, 1, 12, 2, 25);
-        assert.range(folds[0].range, 1, 12, 2, 10);
-        assert.range(folds[1].range, 2, 20, 2, 25);
-
-        insert(2, 10, "5");
-        assert.range(foldLine.range, 1, 12, 2, 26);
-        assert.range(folds[0].range, 1, 12, 2, 10);
-        assert.range(folds[1].range, 2, 21, 2, 26);
-
-        insert(2, 21, "6");
-        assert.range(foldLine.range, 1, 12, 2, 27);
-        assert.range(folds[0].range, 1, 12, 2, 10);
-        assert.range(folds[1].range, 2, 22, 2, 27);
-
-        insert(2, 27, "7");
-        assert.range(foldLine.range, 1, 12, 2, 27);
-        assert.range(folds[0].range, 1, 12, 2, 10);
-        assert.range(folds[1].range, 2, 22, 2, 27);
-
-        // UNDO = REMOVE
-        undoManager.undo(); // 6
-        assert.range(foldLine.range, 1, 12, 2, 27);
-        assert.range(folds[0].range, 1, 12, 2, 10);
-        assert.range(folds[1].range, 2, 22, 2, 27);
-
-        undoManager.undo(); // 5
-        assert.range(foldLine.range, 1, 12, 2, 26);
-        assert.range(folds[0].range, 1, 12, 2, 10);
-        assert.range(folds[1].range, 2, 21, 2, 26);
-
-        undoManager.undo(); // 4
-        assert.range(foldLine.range, 1, 12, 2, 25);
-        assert.range(folds[0].range, 1, 12, 2, 10);
-        assert.range(folds[1].range, 2, 20, 2, 25);
-
-        undoManager.undo(); // 3
-        assert.range(foldLine.range, 1, 11, 2, 25);
-        assert.range(folds[0].range, 1, 11, 2, 10);
-        assert.range(folds[1].range, 2, 20, 2, 25);
-
-        undoManager.undo(); // Beginning first line.
-        assert.equal(foldLines.length, 2);
-        assert.range(foldLines[0].range, 0, 15, 0, 20);
-        assert.range(foldLines[1].range, 1, 10, 2, 25);
-
-        foldLine = session.$foldData[0];
-        fold = foldLine.folds[0];
-
-        undoManager.undo(); // 2
-        assert.range(foldLine.range, 0, 15, 0, 20);
-        assert.range(fold.range,     0, 15, 0, 20);
-
-        undoManager.undo(); // 1
-        assert.range(foldLine.range, 0, 14, 0, 19);
-        assert.range(fold.range,     0, 14, 0, 19);
-
-        undoManager.undo(); // 0
-        assert.range(foldLine.range, 0, 13, 0, 18);
-        assert.range(fold.range,     0, 13, 0, 18);
-    },
-
-    "test fold multi-line insert/remove": function() {
-        var session = createFoldTestSession(),
-            undoManager = session.getUndoManager(),
-            foldLines = session.$foldData;
-        function insert(row, column, text) {
-            session.insert({row: row, column: column}, text);
-            // Force the session to store all changes made to the document NOW
-            // on the undoManager's queue. Otherwise we can't undo in separate
-            // steps later.
-            session.$syncInformUndoManager();
-        }
-
-        var foldLines = session.$foldData, foldLine, fold, folds;
-
-        insert(0, 0, "\nfo0");
-        assert.equal(foldLines.length, 2);
-        assert.range(foldLines[0].range, 1, 16, 1, 21);
-        assert.range(foldLines[1].range, 2, 10, 3, 25);
-
-        insert(2, 0, "\nba1");
-        assert.equal(foldLines.length, 2);
-        assert.range(foldLines[0].range, 1, 16, 1, 21);
-        assert.range(foldLines[1].range, 3, 13, 4, 25);
-
-        insert(3, 10, "\nfo2");
-        assert.equal(foldLines.length, 2);
-        assert.range(foldLines[0].range, 1, 16, 1, 21);
-        assert.range(foldLines[1].range, 4,  6, 5, 25);
-
-        insert(5, 10, "\nba3");
-        assert.equal(foldLines.length, 3);
-        assert.range(foldLines[0].range, 1, 16, 1, 21);
-        assert.range(foldLines[1].range, 4,  6, 5, 10);
-        assert.range(foldLines[2].range, 6, 13, 6, 18);
-
-        insert(6, 18, "\nfo4");
-        assert.equal(foldLines.length, 3);
-        assert.range(foldLines[0].range, 1, 16, 1, 21);
-        assert.range(foldLines[1].range, 4,  6, 5, 10);
-        assert.range(foldLines[2].range, 6, 13, 6, 18);
-
-        undoManager.undo(); // 3
-        assert.equal(foldLines.length, 3);
-        assert.range(foldLines[0].range, 1, 16, 1, 21);
-        assert.range(foldLines[1].range, 4,  6, 5, 10);
-        assert.range(foldLines[2].range, 6, 13, 6, 18);
-
-        undoManager.undo(); // 2
-        assert.equal(foldLines.length, 2);
-        assert.range(foldLines[0].range, 1, 16, 1, 21);
-        assert.range(foldLines[1].range, 4,  6, 5, 25);
-
-        undoManager.undo(); // 1
-        assert.equal(foldLines.length, 2);
-        assert.range(foldLines[0].range, 1, 16, 1, 21);
-        assert.range(foldLines[1].range, 3, 13, 4, 25);
-
-        undoManager.undo(); // 0
-        assert.equal(foldLines.length, 2);
-        assert.range(foldLines[0].range, 1, 16, 1, 21);
-        assert.range(foldLines[1].range, 2, 10, 3, 25);
-
-        undoManager.undo(); // Beginning
-        assert.equal(foldLines.length, 2);
-        assert.range(foldLines[0].range, 0, 13, 0, 18);
-        assert.range(foldLines[1].range, 1, 10, 2, 25);
-        // TODO: Add test for inseration inside of folds.
-    },
-
-    "test fold wrap data compution": function() {
-        function assertWrap(line0, line1, line2) {
-            line0 && assertArray(wrapData[0], line0);
-            line1 && assertArray(wrapData[1], line1);
-            line2 && assertArray(wrapData[2], line2);
-        }
-
-        function removeFoldAssertWrap(docRow, docColumn, line0, line1, line2) {
-            session.removeFold(session.getFoldAt(docRow, docColumn));
-            assertWrap(line0, line1, line2);
-        }
-
-        var lines = [
-            "foo bar foo bar",
-            "foo bar foo bar",
-            "foo bar foo bar"
-        ];
-
-        var session = new EditSession(lines.join("\n"));
-        session.setUseWrapMode(true);
-        session.$wrapLimit = 7;
-        session.$updateWrapData(0, 2);
-        var wrapData = session.$wrapData;
-
-        // Do a simple assertion without folds to check basic functionallity.
-        assertWrap([8], [8], [8]);
-
-        // --- Do in line folding ---
-
-        // Adding a fold. The split position is inside of the fold. As placeholder
-        // are not splitable, the split should be before the split.
-        session.addFold("woot", new Range(0, 4, 0, 15));
-        assertWrap([4], [8], [8]);
-
-        // Remove the fold again which should reset the wrapData.
-        removeFoldAssertWrap(0, 4, [8], [8], [8]);
-
-        session.addFold("woot", new Range(0, 6, 0, 9));
-        assertWrap([6, 13], [8], [8]);
-        removeFoldAssertWrap(0, 6, [8], [8], [8]);
-
-        // The fold fits into the wrap limit - no split expected.
-        session.addFold("woot", new Range(0, 3, 0, 15));
-        assertWrap([], [8], [8]);
-        removeFoldAssertWrap(0, 4, [8], [8], [8]);
-
-        // Fold after split position should be all fine.
-        session.addFold("woot", new Range(0, 8, 0, 15));
-        assertWrap([8], [8], [8]);
-        removeFoldAssertWrap(0, 8, [8], [8], [8]);
-
-        // Fold's placeholder is far too long for wrapSplit.
-        session.addFold("woot0123456789", new Range(0, 8, 0, 15));
-        assertWrap([8], [8], [8]);
-        removeFoldAssertWrap(0, 8, [8], [8], [8]);
-
-        // Fold's placeholder is far too long for wrapSplit
-        // + content at the end of the line
-        session.addFold("woot0123456789", new Range(0, 6, 0, 8));
-        assertWrap([6, 20], [8], [8]);
-        removeFoldAssertWrap(0, 8, [8], [8], [8]);
-
-        session.addFold("woot0123456789", new Range(0, 6, 0, 8));
-        session.addFold("woot0123456789", new Range(0, 8, 0, 10));
-        assertWrap([6, 20, 34], [8], [8]);
-        session.removeFold(session.getFoldAt(0, 7));
-        removeFoldAssertWrap(0, 8, [8], [8], [8]);
-
-        session.addFold("woot0123456789", new Range(0, 7, 0, 9));
-        session.addFold("woot0123456789", new Range(0, 13, 0, 15));
-        assertWrap([7, 21, 25], [8], [8]);
-        session.removeFold(session.getFoldAt(0, 7));
-        removeFoldAssertWrap(0, 14, [8], [8], [8]);
-
-        // --- Do some multiline folding ---
-
-        // Add a fold over two lines. Note, that the wrapData[1] stays the
-        // same. This is an implementation detail and expected behavior.
-        session.addFold("woot", new Range(0, 8, 1, 15));
-        assertWrap([8], [8 /* See comments */], [8]);
-        removeFoldAssertWrap(0, 8, [8], [8], [8]);
-
-        session.addFold("woot", new Range(0, 9, 1, 11));
-        assertWrap([8, 14], [8 /* See comments */], [8]);
-        removeFoldAssertWrap(0, 9, [8], [8], [8]);
-
-        session.addFold("woot", new Range(0, 9, 1, 15));
-        assertWrap([8], [8 /* See comments */], [8]);
-        removeFoldAssertWrap(0, 9, [8], [8], [8]);
-
-        return session;
-    },
-    
-    "test delete fold with wrap enabled": function() {
-        var session = new EditSession("");
-        session.setValue([
-            "This is some placeholder text that will be folded inline.",
-            "This is some placeholder text that will be folded inline.",
-            "More text.",
-            "<p>The cursor in this paragraph text will be offset by 1 row.<p>",
-            "<p>Everything after this will be offset as well due to the folds in the row before too.</p>"
-        ].join("\n"));
-        session.addFold('...', new Range(0, 8, 0, 42));
-        session.addFold('...', new Range(1, 8, 1, 42));
-        session.addFold('...', new Range(3, 7, 3, 51));
-        session.setOption("wrap", 40);
-        session.remove(new Range(0,0, 2, 5));
-        // needed because adjustWrapLimit is called async from renderer
-        session.adjustWrapLimit(80);
-        
-        assert.equal(session.$wrapData + "", [[], [], [40, 76]] + "");
-    },
-        
-    "test add fold": function() {
-        var session = createFoldTestSession();
-        var fold;
-
-        function tryAddFold(placeholder, range, shouldFail) {
-            var fail = false;
-            try {
-                fold = session.addFold(placeholder, range);
-            } catch (e) {
-                fail = true;
-            }
-            if (fail != shouldFail) {
-                throw new Error("Expected to get an exception");
-            }
-        }
-
-        tryAddFold("foo", new Range(0, 13, 0, 17), false);
-        tryAddFold("foo", new Range(0, 14, 0, 18), true);
-        tryAddFold("foo", new Range(0, 13, 0, 18), false);
-        assert.equal(session.$foldData[0].folds.length, 1);
-
-        tryAddFold("f", new Range(0, 13, 0, 18), false);
-        tryAddFold("foo", new Range(0, 18, 0, 21), false);
-        assert.equal(session.$foldData[0].folds.length, 2);
-        session.removeFold(fold);
-
-        tryAddFold("foo", new Range(0, 18, 0, 22), false);
-        tryAddFold("foo", new Range(0, 18, 0, 19), true);
-        tryAddFold("foo", new Range(0, 22, 1, 10), false);
-    },
-
-    "test add subfolds": function() {
-        var session = createFoldTestSession();
-        var fold, oldFold;
-        var foldData = session.$foldData;
-
-        oldFold = foldData[0].folds[0];
-
-        fold = session.addFold("fold0", new Range(0, 10, 0, 21));
-        assert.equal(foldData[0].folds.length, 1);
-        assert.equal(fold.subFolds.length, 1);
-        assert.equal(fold.subFolds[0], oldFold);
-
-        session.expandFold(fold);
-        assert.equal(foldData[0].folds.length, 1);
-        assert.equal(foldData[0].folds[0], oldFold);
-        assert.equal(fold.subFolds.length, 0);
-
-        fold = session.addFold("fold0", new Range(0, 13, 2, 10));
-        assert.equal(foldData.length, 1);
-        assert.equal(fold.subFolds.length, 2);
-        assert.equal(fold.subFolds[0], oldFold);
-
-        session.expandFold(fold);
-        assert.equal(foldData.length, 2);
-        assert.equal(foldData[0].folds.length, 1);
-        assert.equal(foldData[0].folds[0], oldFold);
-        assert.equal(fold.subFolds.length, 0);
-
-        session.unfold(null, true);
-        fold = session.addFold("fold0", new Range(0, 0, 0, 21));
-        session.addFold("fold0", new Range(0, 1, 0, 5));
-        session.addFold("fold0", new Range(0, 6, 0, 8));
-        assert.equal(fold.subFolds.length, 2);
-    },
-
-    "test row cache": function() {
-        var session = createFoldTestSession();
-
-        session.screenToDocumentPosition(2,3);
-        assertArray(session.$docRowCache, [1,3]);
-        assertArray(session.$screenRowCache, [1,2]);
-
-        session.screenToDocumentPosition(5,3);
-        assertArray(session.$docRowCache, [1,3,4]);
-        assertArray(session.$screenRowCache, [1,2,3]);
-
-        session.screenToDocumentPosition(0,3);
-        assertArray(session.$docRowCache, [1,3,4]);
-        assertArray(session.$screenRowCache, [1,2,3]);
-
-        var pos = session.screenToDocumentPosition(0,0);
-        assert.equal(pos.row, 0);
-        assertArray(session.$docRowCache, [1,3,4]);
-        assertArray(session.$screenRowCache, [1,2,3]);
-        
-        session.screenToDocumentPosition(1,0);
-        assertArray(session.$docRowCache, [1,3,4]);
-        assertArray(session.$screenRowCache, [1,2,3]);
-
-        session.$resetRowCache();
-        assertArray(session.$docRowCache, []);
-        assertArray(session.$screenRowCache, []);
-        
-        session.screenToDocumentPosition(1,3);
-        assertArray(session.$docRowCache, [1]);
-        assertArray(session.$screenRowCache, [1]);
-       
-        session.screenToDocumentPosition(5,3);
-        assertArray(session.$docRowCache, [1,3,4]);
-        assertArray(session.$screenRowCache, [1,2,3]);
-        
-        session = new EditSession(new Array(30).join("\n"));
-        session.documentToScreenPosition(2,0);
-        session.documentToScreenPosition(2,0);
-        assertArray(session.$docRowCache, [1,2]);
-        assertArray(session.$screenRowCache, [1,2]);
-    },
-
-    "test annotations": function() {
-        var session = new EditSession([]),
-            annotation = {row: 0, type: 'info', text: "This is a test."};
-
-        session.clearAnnotations();
-        assertArray(session.getAnnotations(), []);
-        session.setAnnotations([annotation]);
-        assertArray(session.getAnnotations(), [annotation]);
-    },
-    
-    "test: mode loading" : function(next) {
-        if (!require.undef) {
-            console.log("Skipping test: This test only runs in the browser");
-            next();
-            return;
-        }
-        var session = new EditSession([]);
-        session.setMode("ace/mode/javascript");
-        assert.equal(session.$modeid, "ace/mode/javascript");
-        session.on("changeMode", function() {
-            assert.equal(session.$modeid, "ace/mode/javascript");
-        });
-        session.setMode("ace/mode/sh", function(mode) {
-            assert.ok(!mode);
-        });
-        setTimeout(function() {
-            session.setMode("ace/mode/javascript", function(mode) {
-                session.setMode("ace/mode/javascript");
-                assert.equal(session.$modeid, "ace/mode/javascript");
-                next();
-            });
-        }, 0);
-    }
-};
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec();
-}

+ 0 - 2444
services/web/public/js/ace/editor.js

@@ -1,2444 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
-"use strict";
-
-require("./lib/fixoldbrowsers");
-
-var oop = require("./lib/oop");
-var dom = require("./lib/dom");
-var lang = require("./lib/lang");
-var useragent = require("./lib/useragent");
-var TextInput = require("./keyboard/textinput").TextInput;
-var MouseHandler = require("./mouse/mouse_handler").MouseHandler;
-var FoldHandler = require("./mouse/fold_handler").FoldHandler;
-var KeyBinding = require("./keyboard/keybinding").KeyBinding;
-var EditSession = require("./edit_session").EditSession;
-var Search = require("./search").Search;
-var Range = require("./range").Range;
-var EventEmitter = require("./lib/event_emitter").EventEmitter;
-var CommandManager = require("./commands/command_manager").CommandManager;
-var defaultCommands = require("./commands/default_commands").commands;
-var config = require("./config");
-
-/**
- * The main entry point into the Ace functionality.
- *
- * The `Editor` manages the [[EditSession]] (which manages [[Document]]s), as well as the [[VirtualRenderer]], which draws everything to the screen.
- *
- * Event sessions dealing with the mouse and keyboard are bubbled up from `Document` to the `Editor`, which decides what to do with them.
- * @class Editor
- **/
-
-/**
- * Creates a new `Editor` object.
- *
- * @param {VirtualRenderer} renderer Associated `VirtualRenderer` that draws everything
- * @param {EditSession} session The `EditSession` to refer to
- *
- *
- * @constructor
- **/
-var Editor = function(renderer, session) {
-    var container = renderer.getContainerElement();
-    this.container = container;
-    this.renderer = renderer;
-
-    this.commands = new CommandManager(useragent.isMac ? "mac" : "win", defaultCommands);
-    this.textInput  = new TextInput(renderer.getTextAreaContainer(), this);
-    this.renderer.textarea = this.textInput.getElement();
-    this.keyBinding = new KeyBinding(this);
-
-    // TODO detect touch event support
-    this.$mouseHandler = new MouseHandler(this);
-    new FoldHandler(this);
-
-    this.$blockScrolling = 0;
-    this.$search = new Search().set({
-        wrap: true
-    });
-
-    this.$historyTracker = this.$historyTracker.bind(this);
-    this.commands.on("exec", this.$historyTracker);
-
-    this.$initOperationListeners();
-    
-    this._$emitInputEvent = lang.delayedCall(function() {
-        this._signal("input", {});
-        this.session.bgTokenizer && this.session.bgTokenizer.scheduleStart();
-    }.bind(this));
-    
-    this.on("change", function(_, _self) {
-        _self._$emitInputEvent.schedule(31);
-    });
-
-    this.setSession(session || new EditSession(""));
-    config.resetOptions(this);
-    config._emit("editor", this);
-};
-
-(function(){
-
-    oop.implement(this, EventEmitter);
-
-    this.$initOperationListeners = function() {
-        function last(a) {return a[a.length - 1]}
-
-        this.selections = [];
-        this.commands.on("exec", function(e) {
-            this.startOperation(e);
-
-            var command = e.command;
-            if (command.aceCommandGroup == "fileJump") {
-                var prev = this.prevOp;
-                if (!prev || prev.command.aceCommandGroup != "fileJump") {
-                    this.lastFileJumpPos = last(this.selections);
-                }
-            } else {
-                this.lastFileJumpPos = null;
-            }
-        }.bind(this), true);
-
-        this.commands.on("afterExec", function(e) {
-            var command = e.command;
-
-            if (command.aceCommandGroup == "fileJump") {
-                if (this.lastFileJumpPos && !this.curOp.selectionChanged) {
-                    this.selection.fromJSON(this.lastFileJumpPos);
-                    return;
-                }
-            }
-            this.endOperation(e);
-        }.bind(this), true);
-
-        this.$opResetTimer = lang.delayedCall(this.endOperation.bind(this));
-
-        this.on("change", function() {
-            this.curOp || this.startOperation();
-            this.curOp.docChanged = true;
-        }.bind(this), true);
-
-        this.on("changeSelection", function() {
-            this.curOp || this.startOperation();
-            this.curOp.selectionChanged = true;
-        }.bind(this), true);
-    };
-
-    this.curOp = null;
-    this.prevOp = {};
-    this.startOperation = function(commadEvent) {
-        if (this.curOp) {
-            if (!commadEvent || this.curOp.command)
-                return;
-            this.prevOp = this.curOp;
-        }
-        if (!commadEvent) {
-            this.previousCommand = null;
-            commadEvent = {};
-        }
-
-        this.$opResetTimer.schedule();
-        this.curOp = {
-            command: commadEvent.command || {},
-            args: commadEvent.args
-        };
-        
-        var command = this.curOp.command;
-        if (command && command.scrollIntoView)
-            this.$blockScrolling++;
-
-        this.selections.push(this.selection.toJSON());
-    };
-
-    this.endOperation = function() {
-        if (this.curOp) {
-            var command = this.curOp.command;
-            if (command && command.scrollIntoView) {
-                this.$blockScrolling--;
-                switch (command.scrollIntoView) {
-                    case "center":
-                        this.renderer.scrollCursorIntoView(null, 0.5);
-                        break;
-                    case "cursor":
-                        this.renderer.scrollCursorIntoView();
-                        break;
-                    case "selectionPart":
-                        this.renderer.scrollCursorIntoView();
-                        break;
-                    default:
-                        break;
-                }
-            }
-            this.prevOp = this.curOp;
-            this.curOp = null;
-        }
-    };
-
-    this.$historyTracker = function(e) {
-        if (!this.$mergeUndoDeltas)
-            return;
-
-
-        var prev = this.prevOp;
-        var mergeableCommands = ["backspace", "del", "insertstring"];
-        // previous command was the same
-        var shouldMerge = prev.command && (e.command.name == prev.command.name);
-        if (e.command.name == "insertstring") {
-            var text = e.args;
-            if (this.mergeNextCommand === undefined)
-                this.mergeNextCommand = true;
-
-            shouldMerge = shouldMerge
-                && this.mergeNextCommand // previous command allows to coalesce with
-                && (!/\s/.test(text) || /\s/.test(prev.args)); // previous insertion was of same type
-
-            this.mergeNextCommand = true;
-        } else {
-            shouldMerge = shouldMerge
-                && mergeableCommands.indexOf(e.command.name) !== -1; // the command is mergeable
-        }
-
-        if (
-            this.$mergeUndoDeltas != "always"
-            && Date.now() - this.sequenceStartTime > 2000
-        ) {
-            shouldMerge = false; // the sequence is too long
-        }
-
-        if (shouldMerge)
-            this.session.mergeUndoDeltas = true;
-        else if (mergeableCommands.indexOf(e.command.name) !== -1)
-            this.sequenceStartTime = Date.now();
-    };
-
-    /**
-     * Sets a new key handler, such as "vim" or "windows".
-     * @param {String} keyboardHandler The new key handler
-     *
-     *
-     **/
-    this.setKeyboardHandler = function(keyboardHandler) {
-        if (!keyboardHandler) {
-            this.keyBinding.setKeyboardHandler(null);
-        } else if (typeof keyboardHandler == "string") {
-            this.$keybindingId = keyboardHandler;
-            var _self = this;
-            config.loadModule(["keybinding", keyboardHandler], function(module) {
-                if (_self.$keybindingId == keyboardHandler)
-                    _self.keyBinding.setKeyboardHandler(module && module.handler);
-            });
-        } else {
-            this.$keybindingId = null;
-            this.keyBinding.setKeyboardHandler(keyboardHandler);
-        }
-    };
-
-    /**
-     * Returns the keyboard handler, such as "vim" or "windows".
-     *
-     * @returns {String}
-     *
-     **/
-    this.getKeyboardHandler = function() {
-        return this.keyBinding.getKeyboardHandler();
-    };
-
-
-    /**
-     * Emitted whenever the [[EditSession]] changes.
-     * @event changeSession
-     * @param {Object} e An object with two properties, `oldSession` and `session`, that represent the old and new [[EditSession]]s.
-     *
-     *
-     **/
-    /**
-     * Sets a new editsession to use. This method also emits the `'changeSession'` event.
-     * @param {EditSession} session The new session to use
-     *
-     *
-     **/
-    this.setSession = function(session) {
-        if (this.session == session)
-            return;
-
-        if (this.session) {
-            var oldSession = this.session;
-            this.session.removeEventListener("change", this.$onDocumentChange);
-            this.session.removeEventListener("changeMode", this.$onChangeMode);
-            this.session.removeEventListener("tokenizerUpdate", this.$onTokenizerUpdate);
-            this.session.removeEventListener("changeTabSize", this.$onChangeTabSize);
-            this.session.removeEventListener("changeWrapLimit", this.$onChangeWrapLimit);
-            this.session.removeEventListener("changeWrapMode", this.$onChangeWrapMode);
-            this.session.removeEventListener("onChangeFold", this.$onChangeFold);
-            this.session.removeEventListener("changeFrontMarker", this.$onChangeFrontMarker);
-            this.session.removeEventListener("changeBackMarker", this.$onChangeBackMarker);
-            this.session.removeEventListener("changeBreakpoint", this.$onChangeBreakpoint);
-            this.session.removeEventListener("changeAnnotation", this.$onChangeAnnotation);
-            this.session.removeEventListener("changeOverwrite", this.$onCursorChange);
-            this.session.removeEventListener("changeScrollTop", this.$onScrollTopChange);
-            this.session.removeEventListener("changeScrollLeft", this.$onScrollLeftChange);
-
-            var selection = this.session.getSelection();
-            selection.removeEventListener("changeCursor", this.$onCursorChange);
-            selection.removeEventListener("changeSelection", this.$onSelectionChange);
-        }
-
-        this.session = session;
-
-        this.$onDocumentChange = this.onDocumentChange.bind(this);
-        session.addEventListener("change", this.$onDocumentChange);
-        this.renderer.setSession(session);
-
-        this.$onChangeMode = this.onChangeMode.bind(this);
-        session.addEventListener("changeMode", this.$onChangeMode);
-
-        this.$onTokenizerUpdate = this.onTokenizerUpdate.bind(this);
-        session.addEventListener("tokenizerUpdate", this.$onTokenizerUpdate);
-
-        this.$onChangeTabSize = this.renderer.onChangeTabSize.bind(this.renderer);
-        session.addEventListener("changeTabSize", this.$onChangeTabSize);
-
-        this.$onChangeWrapLimit = this.onChangeWrapLimit.bind(this);
-        session.addEventListener("changeWrapLimit", this.$onChangeWrapLimit);
-
-        this.$onChangeWrapMode = this.onChangeWrapMode.bind(this);
-        session.addEventListener("changeWrapMode", this.$onChangeWrapMode);
-
-        this.$onChangeFold = this.onChangeFold.bind(this);
-        session.addEventListener("changeFold", this.$onChangeFold);
-
-        this.$onChangeFrontMarker = this.onChangeFrontMarker.bind(this);
-        this.session.addEventListener("changeFrontMarker", this.$onChangeFrontMarker);
-
-        this.$onChangeBackMarker = this.onChangeBackMarker.bind(this);
-        this.session.addEventListener("changeBackMarker", this.$onChangeBackMarker);
-
-        this.$onChangeBreakpoint = this.onChangeBreakpoint.bind(this);
-        this.session.addEventListener("changeBreakpoint", this.$onChangeBreakpoint);
-
-        this.$onChangeAnnotation = this.onChangeAnnotation.bind(this);
-        this.session.addEventListener("changeAnnotation", this.$onChangeAnnotation);
-
-        this.$onCursorChange = this.onCursorChange.bind(this);
-        this.session.addEventListener("changeOverwrite", this.$onCursorChange);
-
-        this.$onScrollTopChange = this.onScrollTopChange.bind(this);
-        this.session.addEventListener("changeScrollTop", this.$onScrollTopChange);
-
-        this.$onScrollLeftChange = this.onScrollLeftChange.bind(this);
-        this.session.addEventListener("changeScrollLeft", this.$onScrollLeftChange);
-
-        this.selection = session.getSelection();
-        this.selection.addEventListener("changeCursor", this.$onCursorChange);
-
-        this.$onSelectionChange = this.onSelectionChange.bind(this);
-        this.selection.addEventListener("changeSelection", this.$onSelectionChange);
-
-        this.onChangeMode();
-
-        this.$blockScrolling += 1;
-        this.onCursorChange();
-        this.$blockScrolling -= 1;
-
-        this.onScrollTopChange();
-        this.onScrollLeftChange();
-        this.onSelectionChange();
-        this.onChangeFrontMarker();
-        this.onChangeBackMarker();
-        this.onChangeBreakpoint();
-        this.onChangeAnnotation();
-        this.session.getUseWrapMode() && this.renderer.adjustWrapLimit();
-        this.renderer.updateFull();
-
-        this._emit("changeSession", {
-            session: session,
-            oldSession: oldSession
-        });
-    };
-
-    /**
-     * Returns the current session being used.
-     * @returns {EditSession}
-     **/
-    this.getSession = function() {
-        return this.session;
-    };
-
-    /**
-     * Sets the current document to `val`.
-     * @param {String} val The new value to set for the document
-     * @param {Number} cursorPos Where to set the new value. `undefined` or 0 is selectAll, -1 is at the document start, and 1 is at the end
-     *
-     * @returns {String} The current document value
-     * @related Document.setValue
-     **/
-    this.setValue = function(val, cursorPos) {
-        this.session.doc.setValue(val);
-
-        if (!cursorPos)
-            this.selectAll();
-        else if (cursorPos == 1)
-            this.navigateFileEnd();
-        else if (cursorPos == -1)
-            this.navigateFileStart();
-
-        return val;
-    };
-
-    /**
-     * Returns the current session's content.
-     *
-     * @returns {String}
-     * @related EditSession.getValue
-     **/
-    this.getValue = function() {
-        return this.session.getValue();
-    };
-
-    /**
-     *
-     * Returns the currently highlighted selection.
-     * @returns {String} The highlighted selection
-     **/
-    this.getSelection = function() {
-        return this.selection;
-    };
-
-    /**
-     * {:VirtualRenderer.onResize}
-     * @param {Boolean} force If `true`, recomputes the size, even if the height and width haven't changed
-     *
-     *
-     * @related VirtualRenderer.onResize
-     **/
-    this.resize = function(force) {
-        this.renderer.onResize(force);
-    };
-
-    /**
-     * {:VirtualRenderer.setTheme}
-     * @param {String} theme The path to a theme
-     *
-     *
-     **/
-    this.setTheme = function(theme) {
-        this.renderer.setTheme(theme);
-    };
-
-    /**
-     * {:VirtualRenderer.getTheme}
-     *
-     * @returns {String} The set theme
-     * @related VirtualRenderer.getTheme
-     **/
-    this.getTheme = function() {
-        return this.renderer.getTheme();
-    };
-
-    /**
-     * {:VirtualRenderer.setStyle}
-     * @param {String} style A class name
-     *
-     *
-     * @related VirtualRenderer.setStyle
-     **/
-    this.setStyle = function(style) {
-        this.renderer.setStyle(style);
-    };
-
-    /**
-     * {:VirtualRenderer.unsetStyle}
-     * @related VirtualRenderer.unsetStyle
-     **/
-    this.unsetStyle = function(style) {
-        this.renderer.unsetStyle(style);
-    };
-
-    /**
-     * Gets the current font size of the editor text.
-     */
-    this.getFontSize = function () {
-        return this.getOption("fontSize") ||
-           dom.computedStyle(this.container, "fontSize");
-    };
-
-    /**
-     * Set a new font size (in pixels) for the editor text.
-     * @param {String} size A font size ( _e.g._ "12px")
-     *
-     *
-     **/
-    this.setFontSize = function(size) {
-        this.setOption("fontSize", size);
-    };
-
-    this.$highlightBrackets = function() {
-        if (this.session.$bracketHighlight) {
-            this.session.removeMarker(this.session.$bracketHighlight);
-            this.session.$bracketHighlight = null;
-        }
-
-        if (this.$highlightPending) {
-            return;
-        }
-
-        // perform highlight async to not block the browser during navigation
-        var self = this;
-        this.$highlightPending = true;
-        setTimeout(function() {
-            self.$highlightPending = false;
-
-            var pos = self.session.findMatchingBracket(self.getCursorPosition());
-            if (pos) {
-                var range = new Range(pos.row, pos.column, pos.row, pos.column+1);
-            } else if (self.session.$mode.getMatching) {
-                var range = self.session.$mode.getMatching(self.session);
-            }
-            if (range)
-                self.session.$bracketHighlight = self.session.addMarker(range, "ace_bracket", "text");
-        }, 50);
-    };
-
-    /**
-     *
-     * Brings the current `textInput` into focus.
-     **/
-    this.focus = function() {
-        // Safari needs the timeout
-        // iOS and Firefox need it called immediately
-        // to be on the save side we do both
-        var _self = this;
-        setTimeout(function() {
-            _self.textInput.focus();
-        });
-        this.textInput.focus();
-    };
-
-    /**
-     * Returns `true` if the current `textInput` is in focus.
-     * @return {Boolean}
-     **/
-    this.isFocused = function() {
-        return this.textInput.isFocused();
-    };
-
-    /**
-     *
-     * Blurs the current `textInput`.
-     **/
-    this.blur = function() {
-        this.textInput.blur();
-    };
-
-    /**
-     * Emitted once the editor comes into focus.
-     * @event focus
-     *
-     *
-     **/
-    this.onFocus = function() {
-        if (this.$isFocused)
-            return;
-        this.$isFocused = true;
-        this.renderer.showCursor();
-        this.renderer.visualizeFocus();
-        this._emit("focus");
-    };
-
-    /**
-     * Emitted once the editor has been blurred.
-     * @event blur
-     *
-     *
-     **/
-    this.onBlur = function() {
-        if (!this.$isFocused)
-            return;
-        this.$isFocused = false;
-        this.renderer.hideCursor();
-        this.renderer.visualizeBlur();
-        this._emit("blur");
-    };
-
-    this.$cursorChange = function() {
-        this.renderer.updateCursor();
-    };
-
-    /**
-     * Emitted whenever the document is changed.
-     * @event change
-     * @param {Object} e Contains a single property, `data`, which has the delta of changes
-     *
-     *
-     *
-     **/
-    this.onDocumentChange = function(e) {
-        var delta = e.data;
-        var range = delta.range;
-        var lastRow;
-
-        if (range.start.row == range.end.row && delta.action != "insertLines" && delta.action != "removeLines")
-            lastRow = range.end.row;
-        else
-            lastRow = Infinity;
-        this.renderer.updateLines(range.start.row, lastRow);
-
-        this._emit("change", e);
-
-        // update cursor because tab characters can influence the cursor position
-        this.$cursorChange();
-    };
-
-    this.onTokenizerUpdate = function(e) {
-        var rows = e.data;
-        this.renderer.updateLines(rows.first, rows.last);
-    };
-
-
-    this.onScrollTopChange = function() {
-        this.renderer.scrollToY(this.session.getScrollTop());
-    };
-
-    this.onScrollLeftChange = function() {
-        this.renderer.scrollToX(this.session.getScrollLeft());
-    };
-
-    /**
-     * Emitted when the selection changes.
-     *
-     **/
-    this.onCursorChange = function() {
-        this.$cursorChange();
-
-        if (!this.$blockScrolling) {
-            this.renderer.scrollCursorIntoView();
-        }
-
-        this.$highlightBrackets();
-        this.$updateHighlightActiveLine();
-        this._emit("changeSelection");
-    };
-
-    this.$updateHighlightActiveLine = function() {
-        var session = this.getSession();
-
-        var highlight;
-        if (this.$highlightActiveLine) {
-            if ((this.$selectionStyle != "line" || !this.selection.isMultiLine()))
-                highlight = this.getCursorPosition();
-            if (this.renderer.$maxLines && this.session.getLength() === 1)
-                highlight = false;
-        }
-
-        if (session.$highlightLineMarker && !highlight) {
-            session.removeMarker(session.$highlightLineMarker.id);
-            session.$highlightLineMarker = null;
-        } else if (!session.$highlightLineMarker && highlight) {
-            var range = new Range(highlight.row, highlight.column, highlight.row, Infinity);
-            range.id = session.addMarker(range, "ace_active-line", "screenLine");
-            session.$highlightLineMarker = range;
-        } else if (highlight) {
-            session.$highlightLineMarker.start.row = highlight.row;
-            session.$highlightLineMarker.end.row = highlight.row;
-            session.$highlightLineMarker.start.column = highlight.column;
-            session._emit("changeBackMarker");
-        }
-    };
-
-    this.onSelectionChange = function(e) {
-        var session = this.session;
-
-        if (session.$selectionMarker) {
-            session.removeMarker(session.$selectionMarker);
-        }
-        session.$selectionMarker = null;
-
-        if (!this.selection.isEmpty()) {
-            var range = this.selection.getRange();
-            var style = this.getSelectionStyle();
-            session.$selectionMarker = session.addMarker(range, "ace_selection", style);
-        } else {
-            this.$updateHighlightActiveLine();
-        }
-
-        var re = this.$highlightSelectedWord && this.$getSelectionHighLightRegexp();
-        this.session.highlight(re);
-
-        this._emit("changeSelection");
-    };
-
-    this.$getSelectionHighLightRegexp = function() {
-        var session = this.session;
-
-        var selection = this.getSelectionRange();
-        if (selection.isEmpty() || selection.isMultiLine())
-            return;
-
-        var startOuter = selection.start.column - 1;
-        var endOuter = selection.end.column + 1;
-        var line = session.getLine(selection.start.row);
-        var lineCols = line.length;
-        var needle = line.substring(Math.max(startOuter, 0),
-                                    Math.min(endOuter, lineCols));
-
-        // Make sure the outer characters are not part of the word.
-        if ((startOuter >= 0 && /^[\w\d]/.test(needle)) ||
-            (endOuter <= lineCols && /[\w\d]$/.test(needle)))
-            return;
-
-        needle = line.substring(selection.start.column, selection.end.column);
-        if (!/^[\w\d]+$/.test(needle))
-            return;
-
-        var re = this.$search.$assembleRegExp({
-            wholeWord: true,
-            caseSensitive: true,
-            needle: needle
-        });
-
-        return re;
-    };
-
-
-    this.onChangeFrontMarker = function() {
-        this.renderer.updateFrontMarkers();
-    };
-
-    this.onChangeBackMarker = function() {
-        this.renderer.updateBackMarkers();
-    };
-
-
-    this.onChangeBreakpoint = function() {
-        this.renderer.updateBreakpoints();
-    };
-
-    this.onChangeAnnotation = function() {
-        this.renderer.setAnnotations(this.session.getAnnotations());
-    };
-
-
-    this.onChangeMode = function(e) {
-        this.renderer.updateText();
-        this._emit("changeMode", e);
-    };
-
-
-    this.onChangeWrapLimit = function() {
-        this.renderer.updateFull();
-    };
-
-    this.onChangeWrapMode = function() {
-        this.renderer.onResize(true);
-    };
-
-
-    this.onChangeFold = function() {
-        // Update the active line marker as due to folding changes the current
-        // line range on the screen might have changed.
-        this.$updateHighlightActiveLine();
-        // TODO: This might be too much updating. Okay for now.
-        this.renderer.updateFull();
-    };
-
-    
-    /**
-     * Returns the string of text currently highlighted.
-     * @returns {String}
-     **/
-    this.getSelectedText = function() {
-        return this.session.getTextRange(this.getSelectionRange());
-    };
-    
-    /**
-     * Emitted when text is copied.
-     * @event copy
-     * @param {String} text The copied text
-     *
-     **/
-    /**
-     * Returns the string of text currently highlighted.
-     * @returns {String}
-     * @deprecated Use getSelectedText instead.
-     **/
-    this.getCopyText = function() {
-        var text = this.getSelectedText();
-        this._signal("copy", text);
-        return text;
-    };
-
-    /**
-     * Called whenever a text "copy" happens.
-     **/
-    this.onCopy = function() {
-        this.commands.exec("copy", this);
-    };
-
-    /**
-     * Called whenever a text "cut" happens.
-     **/
-    this.onCut = function() {
-        this.commands.exec("cut", this);
-    };
-
-    /**
-     * Emitted when text is pasted.
-     * @event paste
-     * @param {String} text The pasted text
-     *
-     *
-     **/
-    /**
-     * Called whenever a text "paste" happens.
-     * @param {String} text The pasted text
-     *
-     *
-     **/
-    this.onPaste = function(text) {
-        // todo this should change when paste becomes a command
-        if (this.$readOnly)
-            return;
-        this._emit("paste", text);
-        this.insert(text);
-    };
-
-
-    this.execCommand = function(command, args) {
-        this.commands.exec(command, this, args);
-    };
-
-    /**
-     * Inserts `text` into wherever the cursor is pointing.
-     * @param {String} text The new text to add
-     *
-     *
-     **/
-    this.insert = function(text) {
-        var session = this.session;
-        var mode = session.getMode();
-        var cursor = this.getCursorPosition();
-
-        if (this.getBehavioursEnabled()) {
-            // Get a transform if the current mode wants one.
-            var transform = mode.transformAction(session.getState(cursor.row), 'insertion', this, session, text);
-            if (transform) {
-                if (text !== transform.text) {
-                    this.session.mergeUndoDeltas = false;
-                    this.$mergeNextCommand = false;
-                }
-                text = transform.text;
-
-            }
-        }
-        
-        if (text == "\t")
-            text = this.session.getTabString();
-
-        // remove selected text
-        if (!this.selection.isEmpty()) {
-            var range = this.getSelectionRange();
-            cursor = this.session.remove(range);
-            this.clearSelection();
-        }
-        else if (this.session.getOverwrite()) {
-            var range = new Range.fromPoints(cursor, cursor);
-            range.end.column += text.length;
-            this.session.remove(range);
-        }
-
-        if (text == "\n" || text == "\r\n") {
-            var line = session.getLine(cursor.row)
-            if (cursor.column > line.search(/\S|$/)) {
-                var d = line.substr(cursor.column).search(/\S|$/);
-                session.doc.removeInLine(cursor.row, cursor.column, cursor.column + d);
-            }
-        }
-        this.clearSelection();
-
-        var start = cursor.column;
-        var lineState = session.getState(cursor.row);
-        var line = session.getLine(cursor.row);
-        var shouldOutdent = mode.checkOutdent(lineState, line, text);
-        var end = session.insert(cursor, text);
-
-        if (transform && transform.selection) {
-            if (transform.selection.length == 2) { // Transform relative to the current column
-                this.selection.setSelectionRange(
-                    new Range(cursor.row, start + transform.selection[0],
-                              cursor.row, start + transform.selection[1]));
-            } else { // Transform relative to the current row.
-                this.selection.setSelectionRange(
-                    new Range(cursor.row + transform.selection[0],
-                              transform.selection[1],
-                              cursor.row + transform.selection[2],
-                              transform.selection[3]));
-            }
-        }
-
-        if (session.getDocument().isNewLine(text)) {
-            var lineIndent = mode.getNextLineIndent(lineState, line.slice(0, cursor.column), session.getTabString());
-
-            session.insert({row: cursor.row+1, column: 0}, lineIndent);
-        }
-        if (shouldOutdent)
-            mode.autoOutdent(lineState, session, cursor.row);
-    };
-
-    this.onTextInput = function(text) {
-        this.keyBinding.onTextInput(text);
-    };
-
-    this.onCommandKey = function(e, hashId, keyCode) {
-        this.keyBinding.onCommandKey(e, hashId, keyCode);
-    };
-
-    /**
-     * Pass in `true` to enable overwrites in your session, or `false` to disable. If overwrites is enabled, any text you enter will type over any text after it. If the value of `overwrite` changes, this function also emites the `changeOverwrite` event.
-     * @param {Boolean} overwrite Defines wheter or not to set overwrites
-     *
-     *
-     * @related EditSession.setOverwrite
-     **/
-    this.setOverwrite = function(overwrite) {
-        this.session.setOverwrite(overwrite);
-    };
-
-    /**
-     * Returns `true` if overwrites are enabled; `false` otherwise.
-     * @returns {Boolean}
-     * @related EditSession.getOverwrite
-     **/
-    this.getOverwrite = function() {
-        return this.session.getOverwrite();
-    };
-
-    /**
-     * Sets the value of overwrite to the opposite of whatever it currently is.
-     * @related EditSession.toggleOverwrite
-     **/
-    this.toggleOverwrite = function() {
-        this.session.toggleOverwrite();
-    };
-
-    /**
-     * Sets how fast the mouse scrolling should do.
-     * @param {Number} speed A value indicating the new speed (in milliseconds)
-     **/
-    this.setScrollSpeed = function(speed) {
-        this.setOption("scrollSpeed", speed);
-    };
-
-    /**
-     * Returns the value indicating how fast the mouse scroll speed is (in milliseconds).
-     * @returns {Number}
-     **/
-    this.getScrollSpeed = function() {
-        return this.getOption("scrollSpeed");
-    };
-
-    /**
-     * Sets the delay (in milliseconds) of the mouse drag.
-     * @param {Number} dragDelay A value indicating the new delay
-     **/
-    this.setDragDelay = function(dragDelay) {
-        this.setOption("dragDelay", dragDelay);
-    };
-
-    /**
-     * Returns the current mouse drag delay.
-     * @returns {Number}
-     **/
-    this.getDragDelay = function() {
-        return this.getOption("dragDelay");
-    };
-
-    /**
-     * Emitted when the selection style changes, via [[Editor.setSelectionStyle]].
-     * @event changeSelectionStyle
-     * @param {Object} data Contains one property, `data`, which indicates the new selection style
-     **/
-    /**
-     * Draw selection markers spanning whole line, or only over selected text. Default value is "line"
-     * @param {String} style The new selection style "line"|"text"
-     *
-     **/
-    this.setSelectionStyle = function(val) {
-        this.setOption("selectionStyle", val);
-    };
-
-    /**
-     * Returns the current selection style.
-     * @returns {String}
-     **/
-    this.getSelectionStyle = function() {
-        return this.getOption("selectionStyle");
-    };
-
-    /**
-     * Determines whether or not the current line should be highlighted.
-     * @param {Boolean} shouldHighlight Set to `true` to highlight the current line
-     **/
-    this.setHighlightActiveLine = function(shouldHighlight) {
-        this.setOption("highlightActiveLine", shouldHighlight);
-    };
-    /**
-     * Returns `true` if current lines are always highlighted.
-     * @return {Boolean}
-     **/
-    this.getHighlightActiveLine = function() {
-        return this.getOption("highlightActiveLine");
-    };
-    this.setHighlightGutterLine = function(shouldHighlight) {
-        this.setOption("highlightGutterLine", shouldHighlight);
-    };
-
-    this.getHighlightGutterLine = function() {
-        return this.getOption("highlightGutterLine");
-    };
-    /**
-     * Determines if the currently selected word should be highlighted.
-     * @param {Boolean} shouldHighlight Set to `true` to highlight the currently selected word
-     *
-     **/
-    this.setHighlightSelectedWord = function(shouldHighlight) {
-        this.setOption("highlightSelectedWord", shouldHighlight);
-    };
-
-    /**
-     * Returns `true` if currently highlighted words are to be highlighted.
-     * @returns {Boolean}
-     **/
-    this.getHighlightSelectedWord = function() {
-        return this.$highlightSelectedWord;
-    };
-
-    this.setAnimatedScroll = function(shouldAnimate){
-        this.renderer.setAnimatedScroll(shouldAnimate);
-    };
-
-    this.getAnimatedScroll = function(){
-        return this.renderer.getAnimatedScroll();
-    };
-
-    /**
-     * If `showInvisibles` is set to `true`, invisible characters&mdash;like spaces or new lines&mdash;are show in the editor.
-     * @param {Boolean} showInvisibles Specifies whether or not to show invisible characters
-     *
-     **/
-    this.setShowInvisibles = function(showInvisibles) {
-        this.renderer.setShowInvisibles(showInvisibles);
-    };
-
-    /**
-     * Returns `true` if invisible characters are being shown.
-     * @returns {Boolean}
-     **/
-    this.getShowInvisibles = function() {
-        return this.renderer.getShowInvisibles();
-    };
-
-    this.setDisplayIndentGuides = function(display) {
-        this.renderer.setDisplayIndentGuides(display);
-    };
-
-    this.getDisplayIndentGuides = function() {
-        return this.renderer.getDisplayIndentGuides();
-    };
-
-    /**
-     * If `showPrintMargin` is set to `true`, the print margin is shown in the editor.
-     * @param {Boolean} showPrintMargin Specifies whether or not to show the print margin
-     *
-     **/
-    this.setShowPrintMargin = function(showPrintMargin) {
-        this.renderer.setShowPrintMargin(showPrintMargin);
-    };
-
-    /**
-     * Returns `true` if the print margin is being shown.
-     * @returns {Boolean}
-     **/
-    this.getShowPrintMargin = function() {
-        return this.renderer.getShowPrintMargin();
-    };
-
-    /**
-     * Sets the column defining where the print margin should be.
-     * @param {Number} showPrintMargin Specifies the new print margin
-     *
-     **/
-    this.setPrintMarginColumn = function(showPrintMargin) {
-        this.renderer.setPrintMarginColumn(showPrintMargin);
-    };
-
-    /**
-     * Returns the column number of where the print margin is.
-     * @returns {Number}
-     **/
-    this.getPrintMarginColumn = function() {
-        return this.renderer.getPrintMarginColumn();
-    };
-
-    /**
-     * If `readOnly` is true, then the editor is set to read-only mode, and none of the content can change.
-     * @param {Boolean} readOnly Specifies whether the editor can be modified or not
-     *
-     **/
-    this.setReadOnly = function(readOnly) {
-        this.setOption("readOnly", readOnly);
-    };
-
-    /**
-     * Returns `true` if the editor is set to read-only mode.
-     * @returns {Boolean}
-     **/
-    this.getReadOnly = function() {
-        return this.getOption("readOnly");
-    };
-
-    /**
-     * Specifies whether to use behaviors or not. ["Behaviors" in this case is the auto-pairing of special characters, like quotation marks, parenthesis, or brackets.]{: #BehaviorsDef}
-     * @param {Boolean} enabled Enables or disables behaviors
-     *
-     **/
-    this.setBehavioursEnabled = function (enabled) {
-        this.setOption("behavioursEnabled", enabled);
-    };
-
-    /**
-     * Returns `true` if the behaviors are currently enabled. {:BehaviorsDef}
-     *
-     * @returns {Boolean}
-     **/
-    this.getBehavioursEnabled = function () {
-        return this.getOption("behavioursEnabled");
-    };
-
-    /**
-     * Specifies whether to use wrapping behaviors or not, i.e. automatically wrapping the selection with characters such as brackets
-     * when such a character is typed in.
-     * @param {Boolean} enabled Enables or disables wrapping behaviors
-     *
-     **/
-    this.setWrapBehavioursEnabled = function (enabled) {
-        this.setOption("wrapBehavioursEnabled", enabled);
-    };
-
-    /**
-     * Returns `true` if the wrapping behaviors are currently enabled.
-     **/
-    this.getWrapBehavioursEnabled = function () {
-        return this.getOption("wrapBehavioursEnabled");
-    };
-
-    /**
-     * Indicates whether the fold widgets should be shown or not.
-     * @param {Boolean} show Specifies whether the fold widgets are shown
-     **/
-    this.setShowFoldWidgets = function(show) {
-        this.setOption("showFoldWidgets", show);
-
-    };
-    /**
-     * Returns `true` if the fold widgets are shown.
-     * @return {Boolean}
-     **/
-    this.getShowFoldWidgets = function() {
-        return this.getOption("showFoldWidgets");
-    };
-
-    this.setFadeFoldWidgets = function(fade) {
-        this.setOption("fadeFoldWidgets", fade);
-    };
-
-    this.getFadeFoldWidgets = function() {
-        return this.getOption("fadeFoldWidgets");
-    };
-
-    /**
-     * Removes words of text from the editor. A "word" is defined as a string of characters bookended by whitespace.
-     * @param {String} dir The direction of the deletion to occur, either "left" or "right"
-     *
-     **/
-    this.remove = function(dir) {
-        if (this.selection.isEmpty()){
-            if (dir == "left")
-                this.selection.selectLeft();
-            else
-                this.selection.selectRight();
-        }
-
-        var range = this.getSelectionRange();
-        if (this.getBehavioursEnabled()) {
-            var session = this.session;
-            var state = session.getState(range.start.row);
-            var new_range = session.getMode().transformAction(state, 'deletion', this, session, range);
-
-            if (range.end.column == 0) {
-                var text = session.getTextRange(range);
-                if (text[text.length - 1] == "\n") {
-                    var line = session.getLine(range.end.row);
-                    if (/^\s+$/.test(line)) {
-                        range.end.column = line.length;
-                    }
-                }
-            }
-            if (new_range)
-                range = new_range;
-        }
-
-        this.session.remove(range);
-        this.clearSelection();
-    };
-
-    /**
-     * Removes the word directly to the right of the current selection.
-     **/
-    this.removeWordRight = function() {
-        if (this.selection.isEmpty())
-            this.selection.selectWordRight();
-
-        this.session.remove(this.getSelectionRange());
-        this.clearSelection();
-    };
-
-    /**
-     * Removes the word directly to the left of the current selection.
-     **/
-    this.removeWordLeft = function() {
-        if (this.selection.isEmpty())
-            this.selection.selectWordLeft();
-
-        this.session.remove(this.getSelectionRange());
-        this.clearSelection();
-    };
-
-    /**
-     * Removes all the words to the left of the current selection, until the start of the line.
-     **/
-    this.removeToLineStart = function() {
-        if (this.selection.isEmpty())
-            this.selection.selectLineStart();
-
-        this.session.remove(this.getSelectionRange());
-        this.clearSelection();
-    };
-
-    /**
-     * Removes all the words to the right of the current selection, until the end of the line.
-     **/
-    this.removeToLineEnd = function() {
-        if (this.selection.isEmpty())
-            this.selection.selectLineEnd();
-
-        var range = this.getSelectionRange();
-        if (range.start.column == range.end.column && range.start.row == range.end.row) {
-            range.end.column = 0;
-            range.end.row++;
-        }
-
-        this.session.remove(range);
-        this.clearSelection();
-    };
-
-    /**
-     * Splits the line at the current selection (by inserting an `'\n'`).
-     **/
-    this.splitLine = function() {
-        if (!this.selection.isEmpty()) {
-            this.session.remove(this.getSelectionRange());
-            this.clearSelection();
-        }
-
-        var cursor = this.getCursorPosition();
-        this.insert("\n");
-        this.moveCursorToPosition(cursor);
-    };
-
-    /**
-     * Transposes current line.
-     **/
-    this.transposeLetters = function() {
-        if (!this.selection.isEmpty()) {
-            return;
-        }
-
-        var cursor = this.getCursorPosition();
-        var column = cursor.column;
-        if (column === 0)
-            return;
-
-        var line = this.session.getLine(cursor.row);
-        var swap, range;
-        if (column < line.length) {
-            swap = line.charAt(column) + line.charAt(column-1);
-            range = new Range(cursor.row, column-1, cursor.row, column+1);
-        }
-        else {
-            swap = line.charAt(column-1) + line.charAt(column-2);
-            range = new Range(cursor.row, column-2, cursor.row, column);
-        }
-        this.session.replace(range, swap);
-    };
-
-    /**
-     * Converts the current selection entirely into lowercase.
-     **/
-    this.toLowerCase = function() {
-        var originalRange = this.getSelectionRange();
-        if (this.selection.isEmpty()) {
-            this.selection.selectWord();
-        }
-
-        var range = this.getSelectionRange();
-        var text = this.session.getTextRange(range);
-        this.session.replace(range, text.toLowerCase());
-        this.selection.setSelectionRange(originalRange);
-    };
-
-    /**
-     * Converts the current selection entirely into uppercase.
-     **/
-    this.toUpperCase = function() {
-        var originalRange = this.getSelectionRange();
-        if (this.selection.isEmpty()) {
-            this.selection.selectWord();
-        }
-
-        var range = this.getSelectionRange();
-        var text = this.session.getTextRange(range);
-        this.session.replace(range, text.toUpperCase());
-        this.selection.setSelectionRange(originalRange);
-    };
-
-    /**
-     * Inserts an indentation into the current cursor position or indents the selected lines.
-     *
-     * @related EditSession.indentRows
-     **/
-    this.indent = function() {
-        var session = this.session;
-        var range = this.getSelectionRange();
-
-        if (range.start.row < range.end.row) {
-            var rows = this.$getSelectedRows();
-            session.indentRows(rows.first, rows.last, "\t");
-            return;
-        } else if (range.start.column < range.end.column) {
-            var text = session.getTextRange(range);
-            if (!/^\s+$/.test(text)) {
-                var rows = this.$getSelectedRows();
-                session.indentRows(rows.first, rows.last, "\t");
-                return;
-            }
-        }
-        
-        var line = session.getLine(range.start.row)
-        var position = range.start;
-        var size = session.getTabSize();
-        var column = session.documentToScreenColumn(position.row, position.column);
-
-        if (this.session.getUseSoftTabs()) {
-            var count = (size - column % size);
-            var indentString = lang.stringRepeat(" ", count);
-        } else {
-            var count = column % size;
-            while (line[range.start.column] == " " && count) {
-                range.start.column--;
-                count--;
-            }
-            this.selection.setSelectionRange(range);
-            indentString = "\t";
-        }
-        return this.insert(indentString);
-    };
-
-    /**
-     * Indents the current line.
-     * @related EditSession.indentRows
-     **/
-    this.blockIndent = function() {
-        var rows = this.$getSelectedRows();
-        this.session.indentRows(rows.first, rows.last, "\t");
-    };
-
-    /**
-     * Outdents the current line.
-     * @related EditSession.outdentRows
-     **/
-    this.blockOutdent = function() {
-        var selection = this.session.getSelection();
-        this.session.outdentRows(selection.getRange());
-    };
-
-    // TODO: move out of core when we have good mechanism for managing extensions
-    this.sortLines = function() {
-        var rows = this.$getSelectedRows();
-        var session = this.session;
-
-        var lines = [];
-        for (i = rows.first; i <= rows.last; i++)
-            lines.push(session.getLine(i));
-
-        lines.sort(function(a, b) {
-            if (a.toLowerCase() < b.toLowerCase()) return -1;
-            if (a.toLowerCase() > b.toLowerCase()) return 1;
-            return 0;
-        });
-
-        var deleteRange = new Range(0, 0, 0, 0);
-        for (var i = rows.first; i <= rows.last; i++) {
-            var line = session.getLine(i);
-            deleteRange.start.row = i;
-            deleteRange.end.row = i;
-            deleteRange.end.column = line.length;
-            session.replace(deleteRange, lines[i-rows.first]);
-        }
-    };
-
-    /**
-     * Given the currently selected range, this function either comments all the lines, or uncomments all of them.
-     **/
-    this.toggleCommentLines = function() {
-        var state = this.session.getState(this.getCursorPosition().row);
-        var rows = this.$getSelectedRows();
-        this.session.getMode().toggleCommentLines(state, this.session, rows.first, rows.last);
-    };
-
-    this.toggleBlockComment = function() {
-        var cursor = this.getCursorPosition();
-        var state = this.session.getState(cursor.row);
-        var range = this.getSelectionRange();
-        this.session.getMode().toggleBlockComment(state, this.session, range, cursor);
-    };
-
-    /**
-     * Works like [[EditSession.getTokenAt]], except it returns a number.
-     * @returns {Number}
-     **/
-    this.getNumberAt = function(row, column) {
-        var _numberRx = /[\-]?[0-9]+(?:\.[0-9]+)?/g;
-        _numberRx.lastIndex = 0;
-
-        var s = this.session.getLine(row);
-        while (_numberRx.lastIndex < column) {
-            var m = _numberRx.exec(s);
-            if(m.index <= column && m.index+m[0].length >= column){
-                var number = {
-                    value: m[0],
-                    start: m.index,
-                    end: m.index+m[0].length
-                };
-                return number;
-            }
-        }
-        return null;
-    };
-
-    /**
-     * If the character before the cursor is a number, this functions changes its value by `amount`.
-     * @param {Number} amount The value to change the numeral by (can be negative to decrease value)
-     *
-     **/
-    this.modifyNumber = function(amount) {
-        var row = this.selection.getCursor().row;
-        var column = this.selection.getCursor().column;
-
-        // get the char before the cursor
-        var charRange = new Range(row, column-1, row, column);
-
-        var c = this.session.getTextRange(charRange);
-        // if the char is a digit
-        if (!isNaN(parseFloat(c)) && isFinite(c)) {
-            // get the whole number the digit is part of
-            var nr = this.getNumberAt(row, column);
-            // if number found
-            if (nr) {
-                var fp = nr.value.indexOf(".") >= 0 ? nr.start + nr.value.indexOf(".") + 1 : nr.end;
-                var decimals = nr.start + nr.value.length - fp;
-
-                var t = parseFloat(nr.value);
-                t *= Math.pow(10, decimals);
-
-
-                if(fp !== nr.end && column < fp){
-                    amount *= Math.pow(10, nr.end - column - 1);
-                } else {
-                    amount *= Math.pow(10, nr.end - column);
-                }
-
-                t += amount;
-                t /= Math.pow(10, decimals);
-                var nnr = t.toFixed(decimals);
-
-                //update number
-                var replaceRange = new Range(row, nr.start, row, nr.end);
-                this.session.replace(replaceRange, nnr);
-
-                //reposition the cursor
-                this.moveCursorTo(row, Math.max(nr.start +1, column + nnr.length - nr.value.length));
-
-            }
-        }
-    };
-
-    /**
-     * Removes all the lines in the current selection
-     * @related EditSession.remove
-     **/
-    this.removeLines = function() {
-        var rows = this.$getSelectedRows();
-        var range;
-        if (rows.first === 0 || rows.last+1 < this.session.getLength())
-            range = new Range(rows.first, 0, rows.last+1, 0);
-        else
-            range = new Range(
-                rows.first-1, this.session.getLine(rows.first-1).length,
-                rows.last, this.session.getLine(rows.last).length
-            );
-        this.session.remove(range);
-        this.clearSelection();
-    };
-
-    this.duplicateSelection = function() {
-        var sel = this.selection;
-        var doc = this.session;
-        var range = sel.getRange();
-        var reverse = sel.isBackwards();
-        if (range.isEmpty()) {
-            var row = range.start.row;
-            doc.duplicateLines(row, row);
-        } else {
-            var point = reverse ? range.start : range.end;
-            var endPoint = doc.insert(point, doc.getTextRange(range), false);
-            range.start = point;
-            range.end = endPoint;
-
-            sel.setSelectionRange(range, reverse);
-        }
-    };
-
-    /**
-     * Shifts all the selected lines down one row.
-     *
-     * @returns {Number} On success, it returns -1.
-     * @related EditSession.moveLinesUp
-     **/
-    this.moveLinesDown = function() {
-        this.$moveLines(function(firstRow, lastRow) {
-            return this.session.moveLinesDown(firstRow, lastRow);
-        });
-    };
-
-    /**
-     * Shifts all the selected lines up one row.
-     * @returns {Number} On success, it returns -1.
-     * @related EditSession.moveLinesDown
-     **/
-    this.moveLinesUp = function() {
-        this.$moveLines(function(firstRow, lastRow) {
-            return this.session.moveLinesUp(firstRow, lastRow);
-        });
-    };
-
-    /**
-     * Moves a range of text from the given range to the given position. `toPosition` is an object that looks like this:
-     * ```json
-     *    { row: newRowLocation, column: newColumnLocation }
-     * ```
-     * @param {Range} fromRange The range of text you want moved within the document
-     * @param {Object} toPosition The location (row and column) where you want to move the text to
-     *
-     * @returns {Range} The new range where the text was moved to.
-     * @related EditSession.moveText
-     **/
-    this.moveText = function(range, toPosition, copy) {
-        return this.session.moveText(range, toPosition, copy);
-    };
-
-    /**
-     * Copies all the selected lines up one row.
-     * @returns {Number} On success, returns 0.
-     *
-     **/
-    this.copyLinesUp = function() {
-        this.$moveLines(function(firstRow, lastRow) {
-            this.session.duplicateLines(firstRow, lastRow);
-            return 0;
-        });
-    };
-
-    /**
-     * Copies all the selected lines down one row.
-     * @returns {Number} On success, returns the number of new rows added; in other words, `lastRow - firstRow + 1`.
-     * @related EditSession.duplicateLines
-     *
-     **/
-    this.copyLinesDown = function() {
-        this.$moveLines(function(firstRow, lastRow) {
-            return this.session.duplicateLines(firstRow, lastRow);
-        });
-    };
-
-    /**
-     * Executes a specific function, which can be anything that manipulates selected lines, such as copying them, duplicating them, or shifting them.
-     * @param {Function} mover A method to call on each selected row
-     *
-     *
-     **/
-    this.$moveLines = function(mover) {
-        var selection = this.selection;
-        if (!selection.inMultiSelectMode || this.inVirtualSelectionMode) {
-            var range = selection.toOrientedRange();
-            var rows = this.$getSelectedRows(range);
-            var linesMoved = mover.call(this, rows.first, rows.last);
-            range.moveBy(linesMoved, 0);
-            selection.fromOrientedRange(range);
-        } else {
-            var ranges = selection.rangeList.ranges;
-            selection.rangeList.detach(this.session);
-
-            for (var i = ranges.length; i--; ) {
-                var rangeIndex = i;
-                var rows = ranges[i].collapseRows();
-                var last = rows.end.row;
-                var first = rows.start.row;
-                while (i--) {
-                    var rows = ranges[i].collapseRows();
-                    if (first - rows.end.row <= 1)
-                        first = rows.end.row;
-                    else
-                        break;
-                }
-                i++;
-
-                var linesMoved = mover.call(this, first, last);
-                while (rangeIndex >= i) {
-                    ranges[rangeIndex].moveBy(linesMoved, 0);
-                    rangeIndex--;
-                }
-            }
-            selection.fromOrientedRange(selection.ranges[0]);
-            selection.rangeList.attach(this.session);
-        }
-    };
-
-    /**
-     * Returns an object indicating the currently selected rows. The object looks like this:
-     *
-     * ```json
-     * { first: range.start.row, last: range.end.row }
-     * ```
-     *
-     * @returns {Object}
-     **/
-    this.$getSelectedRows = function() {
-        var range = this.getSelectionRange().collapseRows();
-
-        return {
-            first: this.session.getRowFoldStart(range.start.row),
-            last: this.session.getRowFoldEnd(range.end.row)
-        };
-    };
-
-    this.onCompositionStart = function(text) {
-        this.renderer.showComposition(this.getCursorPosition());
-    };
-
-    this.onCompositionUpdate = function(text) {
-        this.renderer.setCompositionText(text);
-    };
-
-    this.onCompositionEnd = function() {
-        this.renderer.hideComposition();
-    };
-
-    /**
-     * {:VirtualRenderer.getFirstVisibleRow}
-     *
-     * @returns {Number}
-     * @related VirtualRenderer.getFirstVisibleRow
-     **/
-    this.getFirstVisibleRow = function() {
-        return this.renderer.getFirstVisibleRow();
-    };
-
-    /**
-     * {:VirtualRenderer.getLastVisibleRow}
-     *
-     * @returns {Number}
-     * @related VirtualRenderer.getLastVisibleRow
-     **/
-    this.getLastVisibleRow = function() {
-        return this.renderer.getLastVisibleRow();
-    };
-
-    /**
-     * Indicates if the row is currently visible on the screen.
-     * @param {Number} row The row to check
-     *
-     * @returns {Boolean}
-     **/
-    this.isRowVisible = function(row) {
-        return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow());
-    };
-
-    /**
-     * Indicates if the entire row is currently visible on the screen.
-     * @param {Number} row The row to check
-     *
-     *
-     * @returns {Boolean}
-     **/
-    this.isRowFullyVisible = function(row) {
-        return (row >= this.renderer.getFirstFullyVisibleRow() && row <= this.renderer.getLastFullyVisibleRow());
-    };
-
-    /**
-     * Returns the number of currently visibile rows.
-     * @returns {Number}
-     **/
-    this.$getVisibleRowCount = function() {
-        return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1;
-    };
-
-    this.$moveByPage = function(dir, select) {
-        var renderer = this.renderer;
-        var config = this.renderer.layerConfig;
-        var rows = dir * Math.floor(config.height / config.lineHeight);
-
-        this.$blockScrolling++;
-        if (select == true) {
-            this.selection.$moveSelection(function(){
-                this.moveCursorBy(rows, 0);
-            });
-        } else if (select == false) {
-            this.selection.moveCursorBy(rows, 0);
-            this.selection.clearSelection();
-        }
-        this.$blockScrolling--;
-
-        var scrollTop = renderer.scrollTop;
-
-        renderer.scrollBy(0, rows * config.lineHeight);
-        if (select != null)
-            renderer.scrollCursorIntoView(null, 0.5);
-
-        renderer.animateScrolling(scrollTop);
-    };
-
-    /**
-     * Selects the text from the current position of the document until where a "page down" finishes.
-     **/
-    this.selectPageDown = function() {
-        this.$moveByPage(1, true);
-    };
-
-    /**
-     * Selects the text from the current position of the document until where a "page up" finishes.
-     **/
-    this.selectPageUp = function() {
-        this.$moveByPage(-1, true);
-    };
-
-    /**
-     * Shifts the document to wherever "page down" is, as well as moving the cursor position.
-     **/
-    this.gotoPageDown = function() {
-       this.$moveByPage(1, false);
-    };
-
-    /**
-     * Shifts the document to wherever "page up" is, as well as moving the cursor position.
-     **/
-    this.gotoPageUp = function() {
-        this.$moveByPage(-1, false);
-    };
-
-    /**
-     * Scrolls the document to wherever "page down" is, without changing the cursor position.
-     **/
-    this.scrollPageDown = function() {
-        this.$moveByPage(1);
-    };
-
-    /**
-     * Scrolls the document to wherever "page up" is, without changing the cursor position.
-     **/
-    this.scrollPageUp = function() {
-        this.$moveByPage(-1);
-    };
-
-    /**
-     * Moves the editor to the specified row.
-     * @related VirtualRenderer.scrollToRow
-     **/
-    this.scrollToRow = function(row) {
-        this.renderer.scrollToRow(row);
-    };
-
-    /**
-     * Scrolls to a line. If `center` is `true`, it puts the line in middle of screen (or attempts to).
-     * @param {Number} line The line to scroll to
-     * @param {Boolean} center If `true`
-     * @param {Boolean} animate If `true` animates scrolling
-     * @param {Function} callback Function to be called when the animation has finished
-     *
-     *
-     * @related VirtualRenderer.scrollToLine
-     **/
-    this.scrollToLine = function(line, center, animate, callback) {
-        this.renderer.scrollToLine(line, center, animate, callback);
-    };
-
-    /**
-     * Attempts to center the current selection on the screen.
-     **/
-    this.centerSelection = function() {
-        var range = this.getSelectionRange();
-        var pos = {
-            row: Math.floor(range.start.row + (range.end.row - range.start.row) / 2),
-            column: Math.floor(range.start.column + (range.end.column - range.start.column) / 2)
-        };
-        this.renderer.alignCursor(pos, 0.5);
-    };
-
-    /**
-     * Gets the current position of the cursor.
-     * @returns {Object} An object that looks something like this:
-     *
-     * ```json
-     * { row: currRow, column: currCol }
-     * ```
-     *
-     * @related Selection.getCursor
-     **/
-    this.getCursorPosition = function() {
-        return this.selection.getCursor();
-    };
-
-    /**
-     * Returns the screen position of the cursor.
-     * @returns {Number}
-     * @related EditSession.documentToScreenPosition
-     **/
-    this.getCursorPositionScreen = function() {
-        return this.session.documentToScreenPosition(this.getCursorPosition());
-    };
-
-    /**
-     * {:Selection.getRange}
-     * @returns {Range}
-     * @related Selection.getRange
-     **/
-    this.getSelectionRange = function() {
-        return this.selection.getRange();
-    };
-
-
-    /**
-     * Selects all the text in editor.
-     * @related Selection.selectAll
-     **/
-    this.selectAll = function() {
-        this.$blockScrolling += 1;
-        this.selection.selectAll();
-        this.$blockScrolling -= 1;
-    };
-
-    /**
-     * {:Selection.clearSelection}
-     * @related Selection.clearSelection
-     **/
-    this.clearSelection = function() {
-        this.selection.clearSelection();
-    };
-
-    /**
-     * Moves the cursor to the specified row and column. Note that this does not de-select the current selection.
-     * @param {Number} row The new row number
-     * @param {Number} column The new column number
-     *
-     *
-     * @related Selection.moveCursorTo
-     **/
-    this.moveCursorTo = function(row, column) {
-        this.selection.moveCursorTo(row, column);
-    };
-
-    /**
-     * Moves the cursor to the position indicated by `pos.row` and `pos.column`.
-     * @param {Object} pos An object with two properties, row and column
-     *
-     *
-     * @related Selection.moveCursorToPosition
-     **/
-    this.moveCursorToPosition = function(pos) {
-        this.selection.moveCursorToPosition(pos);
-    };
-
-    /**
-     * Moves the cursor's row and column to the next matching bracket.
-     *
-     **/
-    this.jumpToMatching = function(select) {
-        var cursor = this.getCursorPosition();
-
-        var range = this.session.getBracketRange(cursor);
-        if (!range) {
-            range = this.find({
-                needle: /[{}()\[\]]/g,
-                preventScroll:true,
-                start: {row: cursor.row, column: cursor.column - 1}
-            });
-            if (!range)
-                return;
-            var pos = range.start;
-            if (pos.row == cursor.row && Math.abs(pos.column - cursor.column) < 2)
-                range = this.session.getBracketRange(pos);
-        }
-
-        pos = range && range.cursor || pos;
-        if (pos) {
-            if (select) {
-                if (range && range.isEqual(this.getSelectionRange()))
-                    this.clearSelection();
-                else
-                    this.selection.selectTo(pos.row, pos.column);
-            } else {
-                this.clearSelection();
-                this.moveCursorTo(pos.row, pos.column);
-            }
-        }
-    };
-
-    /**
-     * Moves the cursor to the specified line number, and also into the indiciated column.
-     * @param {Number} lineNumber The line number to go to
-     * @param {Number} column A column number to go to
-     * @param {Boolean} animate If `true` animates scolling
-     *
-     **/
-    this.gotoLine = function(lineNumber, column, animate) {
-        this.selection.clearSelection();
-        this.session.unfold({row: lineNumber - 1, column: column || 0});
-
-        this.$blockScrolling += 1;
-        // todo: find a way to automatically exit multiselect mode
-        this.exitMultiSelectMode && this.exitMultiSelectMode();
-        this.moveCursorTo(lineNumber - 1, column || 0);
-        this.$blockScrolling -= 1;
-
-        if (!this.isRowFullyVisible(lineNumber - 1))
-            this.scrollToLine(lineNumber - 1, true, animate);
-    };
-
-    /**
-     * Moves the cursor to the specified row and column. Note that this does de-select the current selection.
-     * @param {Number} row The new row number
-     * @param {Number} column The new column number
-     *
-     *
-     * @related Editor.moveCursorTo
-     **/
-    this.navigateTo = function(row, column) {
-        this.clearSelection();
-        this.moveCursorTo(row, column);
-    };
-
-    /**
-     * Moves the cursor up in the document the specified number of times. Note that this does de-select the current selection.
-     * @param {Number} times The number of times to change navigation
-     *
-     *
-     **/
-    this.navigateUp = function(times) {
-        if (this.selection.isMultiLine() && !this.selection.isBackwards()) {
-            var selectionStart = this.selection.anchor.getPosition();
-            return this.moveCursorToPosition(selectionStart);
-        }
-        this.selection.clearSelection();
-        times = times || 1;
-        this.selection.moveCursorBy(-times, 0);
-    };
-
-    /**
-     * Moves the cursor down in the document the specified number of times. Note that this does de-select the current selection.
-     * @param {Number} times The number of times to change navigation
-     *
-     *
-     **/
-    this.navigateDown = function(times) {
-        if (this.selection.isMultiLine() && this.selection.isBackwards()) {
-            var selectionEnd = this.selection.anchor.getPosition();
-            return this.moveCursorToPosition(selectionEnd);
-        }
-        this.selection.clearSelection();
-        times = times || 1;
-        this.selection.moveCursorBy(times, 0);
-    };
-
-    /**
-     * Moves the cursor left in the document the specified number of times. Note that this does de-select the current selection.
-     * @param {Number} times The number of times to change navigation
-     *
-     *
-     **/
-    this.navigateLeft = function(times) {
-        if (!this.selection.isEmpty()) {
-            var selectionStart = this.getSelectionRange().start;
-            this.moveCursorToPosition(selectionStart);
-        }
-        else {
-            times = times || 1;
-            while (times--) {
-                this.selection.moveCursorLeft();
-            }
-        }
-        this.clearSelection();
-    };
-
-    /**
-     * Moves the cursor right in the document the specified number of times. Note that this does de-select the current selection.
-     * @param {Number} times The number of times to change navigation
-     *
-     *
-     **/
-    this.navigateRight = function(times) {
-        if (!this.selection.isEmpty()) {
-            var selectionEnd = this.getSelectionRange().end;
-            this.moveCursorToPosition(selectionEnd);
-        }
-        else {
-            times = times || 1;
-            while (times--) {
-                this.selection.moveCursorRight();
-            }
-        }
-        this.clearSelection();
-    };
-
-    /**
-     *
-     * Moves the cursor to the start of the current line. Note that this does de-select the current selection.
-     **/
-    this.navigateLineStart = function() {
-        this.selection.moveCursorLineStart();
-        this.clearSelection();
-    };
-
-    /**
-     *
-     * Moves the cursor to the end of the current line. Note that this does de-select the current selection.
-     **/
-    this.navigateLineEnd = function() {
-        this.selection.moveCursorLineEnd();
-        this.clearSelection();
-    };
-
-    /**
-     *
-     * Moves the cursor to the end of the current file. Note that this does de-select the current selection.
-     **/
-    this.navigateFileEnd = function() {
-        var scrollTop = this.renderer.scrollTop;
-        this.selection.moveCursorFileEnd();
-        this.clearSelection();
-        this.renderer.animateScrolling(scrollTop);
-    };
-
-    /**
-     *
-     * Moves the cursor to the start of the current file. Note that this does de-select the current selection.
-     **/
-    this.navigateFileStart = function() {
-        var scrollTop = this.renderer.scrollTop;
-        this.selection.moveCursorFileStart();
-        this.clearSelection();
-        this.renderer.animateScrolling(scrollTop);
-    };
-
-    /**
-     *
-     * Moves the cursor to the word immediately to the right of the current position. Note that this does de-select the current selection.
-     **/
-    this.navigateWordRight = function() {
-        this.selection.moveCursorWordRight();
-        this.clearSelection();
-    };
-
-    /**
-     *
-     * Moves the cursor to the word immediately to the left of the current position. Note that this does de-select the current selection.
-     **/
-    this.navigateWordLeft = function() {
-        this.selection.moveCursorWordLeft();
-        this.clearSelection();
-    };
-
-    /**
-     * Replaces the first occurance of `options.needle` with the value in `replacement`.
-     * @param {String} replacement The text to replace with
-     * @param {Object} options The [[Search `Search`]] options to use
-     *
-     *
-     **/
-    this.replace = function(replacement, options) {
-        if (options)
-            this.$search.set(options);
-
-        var range = this.$search.find(this.session);
-        var replaced = 0;
-        if (!range)
-            return replaced;
-
-        if (this.$tryReplace(range, replacement)) {
-            replaced = 1;
-        }
-        if (range !== null) {
-            this.selection.setSelectionRange(range);
-            this.renderer.scrollSelectionIntoView(range.start, range.end);
-        }
-
-        return replaced;
-    };
-
-    /**
-     * Replaces all occurances of `options.needle` with the value in `replacement`.
-     * @param {String} replacement The text to replace with
-     * @param {Object} options The [[Search `Search`]] options to use
-     *
-     *
-     **/
-    this.replaceAll = function(replacement, options) {
-        if (options) {
-            this.$search.set(options);
-        }
-
-        var ranges = this.$search.findAll(this.session);
-        var replaced = 0;
-        if (!ranges.length)
-            return replaced;
-
-        this.$blockScrolling += 1;
-
-        var selection = this.getSelectionRange();
-        this.clearSelection();
-        this.selection.moveCursorTo(0, 0);
-
-        for (var i = ranges.length - 1; i >= 0; --i) {
-            if(this.$tryReplace(ranges[i], replacement)) {
-                replaced++;
-            }
-        }
-
-        this.selection.setSelectionRange(selection);
-        this.$blockScrolling -= 1;
-
-        return replaced;
-    };
-
-    this.$tryReplace = function(range, replacement) {
-        var input = this.session.getTextRange(range);
-        replacement = this.$search.replace(input, replacement);
-        if (replacement !== null) {
-            range.end = this.session.replace(range, replacement);
-            return range;
-        } else {
-            return null;
-        }
-    };
-
-    /**
-     * {:Search.getOptions} For more information on `options`, see [[Search `Search`]].
-     * @related Search.getOptions
-     * @returns {Object}
-     **/
-    this.getLastSearchOptions = function() {
-        return this.$search.getOptions();
-    };
-
-    /**
-     * Attempts to find `needle` within the document. For more information on `options`, see [[Search `Search`]].
-     * @param {String} needle The text to search for (optional)
-     * @param {Object} options An object defining various search properties
-     * @param {Boolean} animate If `true` animate scrolling
-     *
-     *
-     * @related Search.find
-     **/
-    this.find = function(needle, options, animate) {
-        if (!options)
-            options = {};
-
-        if (typeof needle == "string" || needle instanceof RegExp)
-            options.needle = needle;
-        else if (typeof needle == "object")
-            oop.mixin(options, needle);
-
-        var range = this.selection.getRange();
-        if (options.needle == null) {
-            needle = this.session.getTextRange(range)
-                || this.$search.$options.needle;
-            if (!needle) {
-                range = this.session.getWordRange(range.start.row, range.start.column);
-                needle = this.session.getTextRange(range);
-            }
-            this.$search.set({needle: needle});
-        }
-
-        this.$search.set(options);
-        if (!options.start)
-            this.$search.set({start: range});
-
-        var newRange = this.$search.find(this.session);
-        if (options.preventScroll)
-            return newRange;
-        if (newRange) {
-            this.revealRange(newRange, animate);
-            return newRange;
-        }
-        // clear selection if nothing is found
-        if (options.backwards)
-            range.start = range.end;
-        else
-            range.end = range.start;
-        this.selection.setRange(range);
-    };
-
-    /**
-     * Performs another search for `needle` in the document. For more information on `options`, see [[Search `Search`]].
-     * @param {Object} options search options
-     * @param {Boolean} animate If `true` animate scrolling
-     *
-     *
-     * @related Editor.find
-     **/
-    this.findNext = function(options, animate) {
-        this.find({skipCurrent: true, backwards: false}, options, animate);
-    };
-
-    /**
-     * Performs a search for `needle` backwards. For more information on `options`, see [[Search `Search`]].
-     * @param {Object} options search options
-     * @param {Boolean} animate If `true` animate scrolling
-     *
-     *
-     * @related Editor.find
-     **/
-    this.findPrevious = function(options, animate) {
-        this.find(options, {skipCurrent: true, backwards: true}, animate);
-    };
-
-    this.revealRange = function(range, animate) {
-        this.$blockScrolling += 1;
-        this.session.unfold(range);
-        this.selection.setSelectionRange(range);
-        this.$blockScrolling -= 1;
-
-        var scrollTop = this.renderer.scrollTop;
-        this.renderer.scrollSelectionIntoView(range.start, range.end, 0.5);
-        if (animate != false)
-            this.renderer.animateScrolling(scrollTop);
-    };
-
-    /**
-     * {:UndoManager.undo}
-     * @related UndoManager.undo
-     **/
-    this.undo = function() {
-        this.$blockScrolling++;
-        this.session.getUndoManager().undo();
-        this.$blockScrolling--;
-        this.renderer.scrollCursorIntoView(null, 0.5);
-    };
-
-    /**
-     * {:UndoManager.redo}
-     * @related UndoManager.redo
-     **/
-    this.redo = function() {
-        this.$blockScrolling++;
-        this.session.getUndoManager().redo();
-        this.$blockScrolling--;
-        this.renderer.scrollCursorIntoView(null, 0.5);
-    };
-
-    /**
-     *
-     * Cleans up the entire editor.
-     **/
-    this.destroy = function() {
-        this.renderer.destroy();
-        this._emit("destroy", this);
-    };
-
-    /**
-     * Enables automatic scrolling of the cursor into view when editor itself is inside scrollable element
-     * @param {Boolean} enable default true
-     **/
-    this.setAutoScrollEditorIntoView = function(enable) {
-        if (enable === false)
-            return;
-        var rect;
-        var self = this;
-        var shouldScroll = false;
-        if (!this.$scrollAnchor)
-            this.$scrollAnchor = document.createElement("div");
-        var scrollAnchor = this.$scrollAnchor;
-        scrollAnchor.style.cssText = "position:absolute";
-        this.container.insertBefore(scrollAnchor, this.container.firstChild);
-        var onChangeSelection = this.on("changeSelection", function() {
-            shouldScroll = true;
-        });
-        // needed to not trigger sync reflow
-        var onBeforeRender = this.renderer.on("beforeRender", function() {
-            if (shouldScroll)
-                rect = self.renderer.container.getBoundingClientRect();
-        });
-        var onAfterRender = this.renderer.on("afterRender", function() {
-            if (shouldScroll && rect && self.isFocused()) {
-                var renderer = self.renderer;
-                var pos = renderer.$cursorLayer.$pixelPos;
-                var config = renderer.layerConfig;
-                var top = pos.top - config.offset;
-                if (pos.top >= 0 && top + rect.top < 0) {
-                    shouldScroll = true;
-                } else if (pos.top < config.height &&
-                    pos.top + rect.top + config.lineHeight > window.innerHeight) {
-                    shouldScroll = false;
-                } else {
-                    shouldScroll = null;
-                }
-                if (shouldScroll != null) {
-                    scrollAnchor.style.top = top + "px";
-                    scrollAnchor.style.left = pos.left + "px";
-                    scrollAnchor.style.height = config.lineHeight + "px";
-                    scrollAnchor.scrollIntoView(shouldScroll);
-                }
-                shouldScroll = rect = null;
-            }
-        });
-        this.setAutoScrollEditorIntoView = function(enable) {
-            if (enable === true)
-                return;
-            delete this.setAutoScrollEditorIntoView;
-            this.removeEventListener("changeSelection", onChangeSelection);
-            this.renderer.removeEventListener("afterRender", onAfterRender);
-            this.renderer.removeEventListener("beforeRender", onBeforeRender);
-        };
-    };
-
-
-    this.$resetCursorStyle = function() {
-        var style = this.$cursorStyle || "ace";
-        var cursorLayer = this.renderer.$cursorLayer;
-        if (!cursorLayer)
-            return;
-        cursorLayer.setSmoothBlinking(style == "smooth");
-        cursorLayer.isBlinking = !this.$readOnly && style != "wide";
-    };
-
-}).call(Editor.prototype);
-
-
-
-config.defineOptions(Editor.prototype, "editor", {
-    selectionStyle: {
-        set: function(style) {
-            this.onSelectionChange();
-            this._emit("changeSelectionStyle", {data: style});
-        },
-        initialValue: "line"
-    },
-    highlightActiveLine: {
-        set: function() {this.$updateHighlightActiveLine();},
-        initialValue: true
-    },
-    highlightSelectedWord: {
-        set: function(shouldHighlight) {this.$onSelectionChange();},
-        initialValue: true
-    },
-    readOnly: {
-        set: function(readOnly) {
-            this.textInput.setReadOnly(readOnly); 
-            this.$resetCursorStyle(); 
-        },
-        initialValue: false
-    },
-    cursorStyle: {
-        set: function(val) { this.$resetCursorStyle(); },
-        values: ["ace", "slim", "smooth", "wide"],
-        initialValue: "ace"
-    },
-    mergeUndoDeltas: {
-        values: [false, true, "always"],
-        initialValue: true
-    },
-    behavioursEnabled: {initialValue: true},
-    wrapBehavioursEnabled: {initialValue: true},
-
-    hScrollBarAlwaysVisible: "renderer",
-    vScrollBarAlwaysVisible: "renderer",
-    highlightGutterLine: "renderer",
-    animatedScroll: "renderer",
-    showInvisibles: "renderer",
-    showPrintMargin: "renderer",
-    printMarginColumn: "renderer",
-    printMargin: "renderer",
-    fadeFoldWidgets: "renderer",
-    showFoldWidgets: "renderer",
-    showGutter: "renderer",
-    displayIndentGuides: "renderer",
-    fontSize: "renderer",
-    fontFamily: "renderer",
-    maxLines: "renderer",
-    minLines: "renderer",
-    scrollPastEnd: "renderer",
-    fixedWidthGutter: "renderer",
-
-    scrollSpeed: "$mouseHandler",
-    dragDelay: "$mouseHandler",
-    dragEnabled: "$mouseHandler",
-    focusTimout: "$mouseHandler",
-
-    firstLineNumber: "session",
-    overwrite: "session",
-    newLineMode: "session",
-    useWorker: "session",
-    useSoftTabs: "session",
-    tabSize: "session",
-    wrap: "session",
-    foldStyle: "session"
-});
-
-exports.Editor = Editor;
-});

+ 0 - 188
services/web/public/js/ace/editor_change_document_test.js

@@ -1,188 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-    require("./test/mockdom");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var EditSession = require("./edit_session").EditSession;
-var Editor = require("./editor").Editor;
-var Text = require("./mode/text").Mode;
-var JavaScriptMode = require("./mode/javascript").Mode;
-var CssMode = require("./mode/css").Mode;
-var HtmlMode = require("./mode/html").Mode;
-var MockRenderer = require("./test/mockrenderer").MockRenderer;
-var assert = require("./test/assertions");
-
-module.exports = {
-
-    setUp : function(next) {
-        this.session1 = new EditSession(["abc", "def"]);
-        this.session2 = new EditSession(["ghi", "jkl"]);
-        
-        
-        this.editor = new Editor(new MockRenderer());
-        next();
-    },
-
-    "test: change document" : function() {
-        this.editor.setSession(this.session1);
-        assert.equal(this.editor.getSession(), this.session1);
-
-        this.editor.setSession(this.session2);
-        assert.equal(this.editor.getSession(), this.session2);
-    },
-
-    "test: only changes to the new document should have effect" : function() {
-        var called = false;
-        this.editor.onDocumentChange = function() {
-            called = true;
-        };
-
-        this.editor.setSession(this.session1);
-        this.editor.setSession(this.session2);
-
-        this.session1.duplicateLines(0, 0);
-        assert.notOk(called);
-
-        this.session2.duplicateLines(0, 0);
-        assert.ok(called);
-    },
-
-    "test: should use cursor of new document" : function() {
-        this.session1.getSelection().moveCursorTo(0, 1);
-        this.session2.getSelection().moveCursorTo(1, 0);
-
-        this.editor.setSession(this.session1);
-        assert.position(this.editor.getCursorPosition(), 0, 1);
-
-        this.editor.setSession(this.session2);
-        assert.position(this.editor.getCursorPosition(), 1, 0);
-    },
-
-    "test: only changing the cursor of the new doc should not have an effect" : function() {
-        this.editor.onCursorChange = function() {
-            called = true;
-        };
-
-        this.editor.setSession(this.session1);
-        this.editor.setSession(this.session2);
-        assert.position(this.editor.getCursorPosition(), 0, 0);
-
-        var called = false;
-        this.session1.getSelection().moveCursorTo(0, 1);
-        assert.position(this.editor.getCursorPosition(), 0, 0);
-        assert.notOk(called);
-
-        this.session2.getSelection().moveCursorTo(1, 1);
-        assert.position(this.editor.getCursorPosition(), 1, 1);
-        assert.ok(called);
-    },
-
-    "test: should use selection of new document" : function() {
-        this.session1.getSelection().selectTo(0, 1);
-        this.session2.getSelection().selectTo(1, 0);
-
-        this.editor.setSession(this.session1);
-        assert.position(this.editor.getSelection().getSelectionLead(), 0, 1);
-
-        this.editor.setSession(this.session2);
-        assert.position(this.editor.getSelection().getSelectionLead(), 1, 0);
-    },
-
-    "test: only changing the selection of the new doc should not have an effect" : function() {
-        this.editor.onSelectionChange = function() {
-            called = true;
-        };
-
-        this.editor.setSession(this.session1);
-        this.editor.setSession(this.session2);
-        assert.position(this.editor.getSelection().getSelectionLead(), 0, 0);
-
-        var called = false;
-        this.session1.getSelection().selectTo(0, 1);
-        assert.position(this.editor.getSelection().getSelectionLead(), 0, 0);
-        assert.notOk(called);
-
-        this.session2.getSelection().selectTo(1, 1);
-        assert.position(this.editor.getSelection().getSelectionLead(), 1, 1);
-        assert.ok(called);
-    },
-
-    "test: should use mode of new document" : function() {
-        this.editor.onChangeMode = function() {
-            called = true;
-        };
-        this.editor.setSession(this.session1);
-        this.editor.setSession(this.session2);
-
-        var called = false;
-        this.session1.setMode(new Text());
-        assert.notOk(called);
-
-        this.session2.setMode(new JavaScriptMode());
-        assert.ok(called);
-    },
-    
-    "test: should use stop worker of old document" : function(next) {
-        var self = this;
-        
-        // 1. Open an editor and set the session to CssMode
-        self.editor.setSession(self.session1);
-        self.session1.setMode(new CssMode());
-        
-        // 2. Add a line or two of valid CSS.
-        self.session1.setValue("DIV { color: red; }");
-        
-        // 3. Clear the session value.
-        self.session1.setValue("");
-        
-        // 4. Set the session to HtmlMode
-        self.session1.setMode(new HtmlMode());
-
-        // 5. Try to type valid HTML
-        self.session1.insert({row: 0, column: 0}, "<html></html>");
-        
-        setTimeout(function() {
-            assert.equal(Object.keys(self.session1.getAnnotations()).length, 0);
-            next();
-        }, 600);
-    }
-};
-
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec()
-}

+ 0 - 223
services/web/public/js/ace/editor_highlight_selected_word_test.js

@@ -1,223 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-    require("./test/mockdom");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var EditSession = require("./edit_session").EditSession;
-var Editor = require("./editor").Editor;
-var MockRenderer = require("./test/mockrenderer").MockRenderer;
-var assert = require("./test/assertions");
-
-var lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
-             "Mauris at arcu mi, eu lobortis mauris. Quisque ut libero eget " +
-             "diam congue vehicula. Quisque ut odio ut mi aliquam tincidunt. " +
-             "Duis lacinia aliquam lorem eget eleifend. Morbi eget felis mi. " +
-             "Duis quam ligula, consequat vitae convallis volutpat, blandit " +
-             "nec neque. Nulla facilisi. Etiam suscipit lorem ac justo " +
-             "sollicitudin tristique. Phasellus ut posuere nunc. Aliquam " +
-             "scelerisque mollis felis non gravida. Vestibulum lacus sem, " +
-             "posuere non bibendum id, luctus non dolor. Aenean id metus " +
-             "lorem, vel dapibus est. Donec gravida feugiat augue nec " +
-             "accumsan.Lorem ipsum dolor sit amet, consectetur adipiscing " +
-             "elit. Nulla vulputate, velit vitae tincidunt congue, nunc " +
-             "augue accumsan velit, eu consequat turpis lectus ac orci. " +
-             "Pellentesque ornare dolor feugiat dui auctor eu varius nulla " +
-             "fermentum. Sed aliquam odio at velit lacinia vel fermentum " +
-             "felis sodales. In dignissim magna eget nunc lobortis non " +
-             "fringilla nibh ullamcorper. Donec facilisis malesuada elit " +
-             "at egestas. Etiam bibendum, diam vitae tempor aliquet, dui " +
-             "libero vehicula odio, eget bibendum mauris velit eu lorem.\n" +
-             "consectetur";
-
-function callHighlighterUpdate(session, firstRow, lastRow) {
-    var rangeCount = 0;
-    var  mockMarkerLayer = { drawSingleLineMarker: function() {rangeCount++;} }
-    session.$searchHighlight.update([], mockMarkerLayer, session, {
-        firstRow: firstRow,
-        lastRow: lastRow
-    });
-    return rangeCount;
-}
-
-module.exports = {
-    setUp: function(next) {
-        this.session = new EditSession(lipsum);
-        this.editor = new Editor(new MockRenderer(), this.session);
-        this.selection = this.session.getSelection();
-        this.search = this.editor.$search;
-        next();
-    },
-
-    "test: highlight selected words by default": function() {
-        assert.equal(this.editor.getHighlightSelectedWord(), true);
-    },
-
-    "test: highlight a word": function() {
-        this.editor.moveCursorTo(0, 9);
-        this.selection.selectWord();
-
-        var highlighter = this.editor.session.$searchHighlight;
-        assert.ok(highlighter != null);
-
-        var range = this.selection.getRange();
-        assert.equal(this.session.getTextRange(range), "ipsum");
-        assert.equal(highlighter.cache.length, 0);
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 2);
-    },
-
-    "test: highlight a word and clear highlight": function() {
-        this.editor.moveCursorTo(0, 8);
-        this.selection.selectWord();
-
-        var range = this.selection.getRange();
-        assert.equal(this.session.getTextRange(range), "ipsum");
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 2);
-
-        this.session.highlight("");
-        assert.equal(this.session.$searchHighlight.cache.length, 0);
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
-    },
-
-    "test: highlight another word": function() {
-        this.selection.moveCursorTo(0, 14);
-        this.selection.selectWord();
-
-        var range = this.selection.getRange();
-        assert.equal(this.session.getTextRange(range), "dolor");
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 4);
-    },
-
-    "test: no selection, no highlight": function() {
-        this.selection.clearSelection();
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
-    },
-
-    "test: select a word, no highlight": function() {
-        this.selection.moveCursorTo(0, 14);
-        this.selection.selectWord();
-
-        this.editor.setHighlightSelectedWord(false);
-
-        var range = this.selection.getRange();
-        assert.equal(this.session.getTextRange(range), "dolor");
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
-    },
-
-    "test: select a word with no matches": function() {
-        this.editor.setHighlightSelectedWord(true);
-
-        var currentOptions = this.search.getOptions();
-        var newOptions = {
-            wrap: true,
-            wholeWord: true,
-            caseSensitive: true,
-            needle: "Mauris"
-        };
-        this.search.set(newOptions);
-
-        var match = this.search.find(this.session);
-        assert.notEqual(match, null, "found a match for 'Mauris'");
-
-        this.search.set(currentOptions);
-
-        this.selection.setSelectionRange(match);
-
-        assert.equal(this.session.getTextRange(match), "Mauris");
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 1);
-    },
-
-    "test: partial word selection 1": function() {
-        this.selection.moveCursorTo(0, 14);
-        this.selection.selectWord();
-        this.selection.selectLeft();
-
-        var range = this.selection.getRange();
-        assert.equal(this.session.getTextRange(range), "dolo");
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
-    },
-
-    "test: partial word selection 2": function() {
-        this.selection.moveCursorTo(0, 13);
-        this.selection.selectWord();
-        this.selection.selectRight();
-
-        var range = this.selection.getRange();
-        assert.equal(this.session.getTextRange(range), "dolor ");
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
-    },
-
-    "test: partial word selection 3": function() {
-        this.selection.moveCursorTo(0, 14);
-        this.selection.selectWord();
-        this.selection.selectLeft();
-        this.selection.shiftSelection(1);
-
-        var range = this.selection.getRange();
-        assert.equal(this.session.getTextRange(range), "olor");
-        assert.equal(callHighlighterUpdate(this.session, 0, 0), 0);
-    },
-
-    "test: select last word": function() {
-        this.selection.moveCursorTo(0, 1);
-
-        var currentOptions = this.search.getOptions();
-        var newOptions = {
-            wrap: true,
-            wholeWord: true,
-            caseSensitive: true,
-            backwards: true,
-            needle: "consectetur"
-        };
-        this.search.set(newOptions);
-
-        var match = this.search.find(this.session);
-        assert.notEqual(match, null, "found a match for 'consectetur'");
-        assert.position(match.start, 1, 0);
-
-        this.search.set(currentOptions);
-
-        this.selection.setSelectionRange(match);
-
-        assert.equal(this.session.getTextRange(match), "consectetur");
-        assert.equal(callHighlighterUpdate(this.session, 0, 1), 3);
-    }
-};
-
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec();
-}

+ 0 - 164
services/web/public/js/ace/editor_navigation_test.js

@@ -1,164 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-    require("./test/mockdom");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var EditSession = require("./edit_session").EditSession;
-var Editor = require("./editor").Editor;
-var MockRenderer = require("./test/mockrenderer").MockRenderer;
-var assert = require("./test/assertions");
-
-module.exports = {
-    createEditSession : function(rows, cols) {
-        var line = new Array(cols + 1).join("a");
-        var text = new Array(rows).join(line + "\n") + line;
-        return new EditSession(text);
-    },
-
-    "test: navigate to end of file should scroll the last line into view" : function() {
-        var doc = this.createEditSession(200, 10);
-        var editor = new Editor(new MockRenderer(), doc);
-
-        editor.navigateFileEnd();
-        var cursor = editor.getCursorPosition();
-
-        assert.ok(editor.getFirstVisibleRow() <= cursor.row);
-        assert.ok(editor.getLastVisibleRow() >= cursor.row);
-    },
-
-    "test: navigate to start of file should scroll the first row into view" : function() {
-        var doc = this.createEditSession(200, 10);
-        var editor = new Editor(new MockRenderer(), doc);
-
-        editor.moveCursorTo(editor.getLastVisibleRow() + 20);
-        editor.navigateFileStart();
-
-        assert.equal(editor.getFirstVisibleRow(), 0);
-    },
-
-    "test: goto hidden line should scroll the line into the middle of the viewport" : function() {
-        var editor = new Editor(new MockRenderer(), this.createEditSession(200, 5));
-
-        editor.navigateTo(0, 0);
-        editor.gotoLine(101);
-        assert.position(editor.getCursorPosition(), 100, 0);
-        assert.equal(editor.getFirstVisibleRow(), 89);
-
-        editor.navigateTo(100, 0);
-        editor.gotoLine(11);
-        assert.position(editor.getCursorPosition(), 10, 0);
-        assert.equal(editor.getFirstVisibleRow(), 0);
-
-        editor.navigateTo(100, 0);
-        editor.gotoLine(6);
-        assert.position(editor.getCursorPosition(), 5, 0);
-        assert.equal(0, editor.getFirstVisibleRow(), 0);
-
-        editor.navigateTo(100, 0);
-        editor.gotoLine(1);
-        assert.position(editor.getCursorPosition(), 0, 0);
-        assert.equal(editor.getFirstVisibleRow(), 0);
-
-        editor.navigateTo(0, 0);
-        editor.gotoLine(191);
-        assert.position(editor.getCursorPosition(), 190, 0);
-        assert.equal(editor.getFirstVisibleRow(), 179);
-
-        editor.navigateTo(0, 0);
-        editor.gotoLine(196);
-        assert.position(editor.getCursorPosition(), 195, 0);
-        assert.equal(editor.getFirstVisibleRow(), 180);
-    },
-
-    "test: goto visible line should only move the cursor and not scroll": function() {
-        var editor = new Editor(new MockRenderer(), this.createEditSession(200, 5));
-
-        editor.navigateTo(0, 0);
-        editor.gotoLine(12);
-        assert.position(editor.getCursorPosition(), 11, 0);
-        assert.equal(editor.getFirstVisibleRow(), 0);
-
-        editor.navigateTo(30, 0);
-        editor.gotoLine(33);
-        assert.position(editor.getCursorPosition(), 32, 0);
-        assert.equal(editor.getFirstVisibleRow(), 30);
-    },
-
-    "test: navigate from the end of a long line down to a short line and back should maintain the curser column": function() {
-        var editor = new Editor(new MockRenderer(), new EditSession(["123456", "1"]));
-
-        editor.navigateTo(0, 6);
-        assert.position(editor.getCursorPosition(), 0, 6);
-
-        editor.navigateDown();
-        assert.position(editor.getCursorPosition(), 1, 1);
-
-        editor.navigateUp();
-        assert.position(editor.getCursorPosition(), 0, 6);
-    },
-
-    "test: reset desired column on navigate left or right": function() {
-        var editor = new Editor(new MockRenderer(), new EditSession(["123456", "12"]));
-
-        editor.navigateTo(0, 6);
-        assert.position(editor.getCursorPosition(), 0, 6);
-
-        editor.navigateDown();
-        assert.position(editor.getCursorPosition(), 1, 2);
-
-        editor.navigateLeft();
-        assert.position(editor.getCursorPosition(), 1, 1);
-
-        editor.navigateUp();
-        assert.position(editor.getCursorPosition(), 0, 1);
-    },
-    
-    "test: typing text should update the desired column": function() {
-        var editor = new Editor(new MockRenderer(), new EditSession(["1234", "1234567890"]));
-
-        editor.navigateTo(0, 3);
-        editor.insert("juhu");
-        
-        editor.navigateDown();
-        assert.position(editor.getCursorPosition(), 1, 7);
-    }
-};
-
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec()
-}

+ 0 - 557
services/web/public/js/ace/editor_text_edit_test.js

@@ -1,557 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-if (typeof process !== "undefined") {
-    require("amd-loader");
-    require("./test/mockdom");
-}
-
-define(function(require, exports, module) {
-"use strict";
-
-var EditSession = require("./edit_session").EditSession;
-var Editor = require("./editor").Editor;
-var JavaScriptMode = require("./mode/javascript").Mode;
-var UndoManager = require("./undomanager").UndoManager;
-var MockRenderer = require("./test/mockrenderer").MockRenderer;
-var assert = require("./test/assertions");
-var whitespace = require("./ext/whitespace");
-
-module.exports = {
-    "test: delete line from the middle" : function() {
-        var session = new EditSession(["a", "b", "c", "d"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(1, 1);
-        editor.removeLines();
-
-        assert.equal(session.toString(), "a\nc\nd");
-        assert.position(editor.getCursorPosition(), 1, 0);
-
-        editor.removeLines();
-
-        assert.equal(session.toString(), "a\nd");
-        assert.position(editor.getCursorPosition(), 1, 0);
-
-        editor.removeLines();
-
-        assert.equal(session.toString(), "a");
-        assert.position(editor.getCursorPosition(), 0, 1);
-
-        editor.removeLines();
-
-        assert.equal(session.toString(), "");
-        assert.position(editor.getCursorPosition(), 0, 0);
-    },
-
-    "test: delete multiple selected lines" : function() {
-        var session = new EditSession(["a", "b", "c", "d"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(1, 1);
-        editor.getSelection().selectDown();
-
-        editor.removeLines();
-        assert.equal(session.toString(), "a\nd");
-        assert.position(editor.getCursorPosition(), 1, 0);
-    },
-
-    "test: delete first line" : function() {
-        var session = new EditSession(["a", "b", "c"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.removeLines();
-
-        assert.equal(session.toString(), "b\nc");
-        assert.position(editor.getCursorPosition(), 0, 0);
-    },
-
-    "test: delete last should also delete the new line of the previous line" : function() {
-        var session = new EditSession(["a", "b", "c", ""].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(3, 0);
-
-        editor.removeLines();
-        assert.equal(session.toString(), "a\nb\nc");
-        assert.position(editor.getCursorPosition(), 2, 1);
-
-        editor.removeLines();
-        assert.equal(session.toString(), "a\nb");
-        assert.position(editor.getCursorPosition(), 1, 1);
-    },
-
-    "test: indent block" : function() {
-        var session = new EditSession(["a12345", "b12345", "c12345"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(1, 3);
-        editor.getSelection().selectDown();
-
-        editor.indent();
-
-        assert.equal(["a12345", "    b12345", "    c12345"].join("\n"), session.toString());
-
-        assert.position(editor.getCursorPosition(), 2, 7);
-
-        var range = editor.getSelectionRange();
-        assert.position(range.start, 1, 7);
-        assert.position(range.end, 2, 7);
-    },
-
-    "test: indent selected lines" : function() {
-        var session = new EditSession(["a12345", "b12345", "c12345"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(1, 0);
-        editor.getSelection().selectDown();
-
-        editor.indent();
-        assert.equal(["a12345", "    b12345", "c12345"].join("\n"), session.toString());
-    },
-
-    "test: no auto indent if cursor is before the {" : function() {
-        var session = new EditSession("{", new JavaScriptMode());
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(0, 0);
-        editor.onTextInput("\n");
-        assert.equal(["", "{"].join("\n"), session.toString());
-    },
-    
-    "test: outdent block" : function() {
-        var session = new EditSession(["        a12345", "    b12345", "        c12345"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(0, 5);
-        editor.getSelection().selectDown();
-        editor.getSelection().selectDown();
-
-        editor.blockOutdent();
-        assert.equal(session.toString(), ["    a12345", "b12345", "    c12345"].join("\n"));
-
-        assert.position(editor.getCursorPosition(), 2, 1);
-
-        var range = editor.getSelectionRange();
-        assert.position(range.start, 0, 1);
-        assert.position(range.end, 2, 1);
-
-        editor.blockOutdent();
-        assert.equal(session.toString(), ["a12345", "b12345", "c12345"].join("\n"));
-
-        var range = editor.getSelectionRange();
-        assert.position(range.start, 0, 0);
-        assert.position(range.end, 2, 0);
-    },
-
-    "test: outent without a selection should update cursor" : function() {
-        var session = new EditSession("        12");
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(0, 3);
-        editor.blockOutdent("  ");
-
-        assert.equal(session.toString(), "    12");
-        assert.position(editor.getCursorPosition(), 0, 0);
-    },
-
-    "test: comment lines should perserve selection" : function() {
-        var session = new EditSession(["  abc", "cde"].join("\n"), new JavaScriptMode());
-        var editor = new Editor(new MockRenderer(), session);
-        whitespace.detectIndentation(session);
-        
-        editor.moveCursorTo(0, 2);
-        editor.getSelection().selectDown();
-        editor.toggleCommentLines();
-
-        assert.equal(["//   abc", "// cde"].join("\n"), session.toString());
-
-        var selection = editor.getSelectionRange();
-        assert.position(selection.start, 0, 5);
-        assert.position(selection.end, 1, 5);
-    },
-
-    "test: uncomment lines should perserve selection" : function() {
-        var session = new EditSession(["//   abc", "//cde"].join("\n"), new JavaScriptMode());
-        var editor = new Editor(new MockRenderer(), session);
-        session.setTabSize(2);
-
-        editor.moveCursorTo(0, 1);
-        editor.getSelection().selectDown();
-        editor.getSelection().selectRight();
-        editor.getSelection().selectRight();
-
-        editor.toggleCommentLines();
-
-        assert.equal(["  abc", "cde"].join("\n"), session.toString());
-        assert.range(editor.getSelectionRange(), 0, 0, 1, 1);
-    },
-
-    "test: toggle comment lines twice should return the original text" : function() {
-        var session = new EditSession(["  abc", "cde", "fg"], new JavaScriptMode());
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(0, 0);
-        editor.getSelection().selectDown();
-        editor.getSelection().selectDown();
-
-        editor.toggleCommentLines();
-        editor.toggleCommentLines();
-
-        assert.equal(["  abc", "cde", "fg"].join("\n"), session.toString());
-    },
-
-
-    "test: comment lines - if the selection end is at the line start it should stay there": function() {
-        //select down
-        var session = new EditSession(["abc", "cde"].join("\n"), new JavaScriptMode());
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(0, 0);
-        editor.getSelection().selectDown();
-
-        editor.toggleCommentLines();
-        assert.range(editor.getSelectionRange(), 0, 3, 1, 0);
-
-        // select up
-        var session = new EditSession(["abc", "cde"].join("\n"), new JavaScriptMode());
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(1, 0);
-        editor.getSelection().selectUp();
-
-        editor.toggleCommentLines();
-        assert.range(editor.getSelectionRange(), 0, 3, 1, 0);
-    },
-
-    "test: move lines down should keep selection on moved lines" : function() {
-        var session = new EditSession(["11", "22", "33", "44"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(0, 1);
-        editor.getSelection().selectDown();
-
-        editor.moveLinesDown();
-        assert.equal(["33", "11", "22", "44"].join("\n"), session.toString());
-        assert.position(editor.getCursorPosition(), 2, 1);
-        assert.position(editor.getSelection().getSelectionAnchor(), 1, 1);
-        assert.position(editor.getSelection().getSelectionLead(), 2, 1);
-
-        editor.moveLinesDown();
-        assert.equal(["33", "44", "11", "22"].join("\n"), session.toString());
-        assert.position(editor.getCursorPosition(), 3, 1);
-        assert.position(editor.getSelection().getSelectionAnchor(), 2, 1);
-        assert.position(editor.getSelection().getSelectionLead(), 3, 1);
-
-        // moving again should have no effect
-        editor.moveLinesDown();
-        assert.equal(["33", "44", "11", "22"].join("\n"), session.toString());
-        assert.position(editor.getCursorPosition(), 3, 1);
-        assert.position(editor.getSelection().getSelectionAnchor(), 2, 1);
-        assert.position(editor.getSelection().getSelectionLead(), 3, 1);
-    },
-
-    "test: move lines up should keep selection on moved lines" : function() {
-        var session = new EditSession(["11", "22", "33", "44"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(2, 1);
-        editor.getSelection().selectDown();
-
-        editor.moveLinesUp();
-        assert.equal(session.toString(), ["11", "33", "44", "22"].join("\n"));
-        assert.position(editor.getCursorPosition(), 2, 1);
-        assert.position(editor.getSelection().getSelectionAnchor(), 1, 1);
-        assert.position(editor.getSelection().getSelectionLead(), 2, 1);
-
-        editor.moveLinesUp();
-        assert.equal(session.toString(), ["33", "44", "11", "22"].join("\n"));
-        assert.position(editor.getCursorPosition(), 1, 1);
-        assert.position(editor.getSelection().getSelectionAnchor(), 0, 1);
-        assert.position(editor.getSelection().getSelectionLead(), 1, 1);
-    },
-
-    "test: move line without active selection should not move cursor relative to the moved line" : function() {
-        var session = new EditSession(["11", "22", "33", "44"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(1, 1);
-        editor.clearSelection();
-
-        editor.moveLinesDown();
-        assert.equal(["11", "33", "22", "44"].join("\n"), session.toString());
-        assert.position(editor.getCursorPosition(), 2, 1);
-
-        editor.clearSelection();
-
-        editor.moveLinesUp();
-        assert.equal(["11", "22", "33", "44"].join("\n"), session.toString());
-        assert.position(editor.getCursorPosition(), 1, 1);
-    },
-
-    "test: copy lines down should keep selection" : function() {
-        var session = new EditSession(["11", "22", "33", "44"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(1, 1);
-        editor.getSelection().selectDown();
-
-        editor.copyLinesDown();
-        assert.equal(["11", "22", "33", "22", "33", "44"].join("\n"), session.toString());
-
-        assert.position(editor.getCursorPosition(), 4, 1);
-        assert.position(editor.getSelection().getSelectionAnchor(), 3, 1);
-        assert.position(editor.getSelection().getSelectionLead(), 4, 1);
-    },
-
-    "test: copy lines up should keep selection" : function() {
-        var session = new EditSession(["11", "22", "33", "44"].join("\n"));
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.moveCursorTo(1, 1);
-        editor.getSelection().selectDown();
-
-        editor.copyLinesUp();
-        assert.equal(["11", "22", "33", "22", "33", "44"].join("\n"), session.toString());
-
-        assert.position(editor.getCursorPosition(), 2, 1);
-        assert.position(editor.getSelection().getSelectionAnchor(), 1, 1);
-        assert.position(editor.getSelection().getSelectionLead(), 2, 1);
-    },
-
-    "test: input a tab with soft tab should convert it to spaces" : function() {
-        var session = new EditSession("");
-        var editor = new Editor(new MockRenderer(), session);
-
-        session.setTabSize(2);
-        session.setUseSoftTabs(true);
-
-        editor.onTextInput("\t");
-        assert.equal(session.toString(), "  ");
-
-        session.setTabSize(5);
-        editor.onTextInput("\t");
-        assert.equal(session.toString(), "       ");
-    },
-
-    "test: input tab without soft tabs should keep the tab character" : function() {
-        var session = new EditSession("");
-        var editor = new Editor(new MockRenderer(), session);
-
-        session.setUseSoftTabs(false);
-
-        editor.onTextInput("\t");
-        assert.equal(session.toString(), "\t");
-    },
-
-    "test: undo/redo for delete line" : function() {
-        var session = new EditSession(["111", "222", "333"]);
-        var undoManager = new UndoManager();
-        session.setUndoManager(undoManager);
-
-        var initialText = session.toString();
-        var editor = new Editor(new MockRenderer(), session);
-
-        editor.removeLines();
-        var step1 = session.toString();
-        assert.equal(step1, "222\n333");
-        session.$syncInformUndoManager();
-
-        editor.removeLines();
-        var step2 = session.toString();
-        assert.equal(step2, "333");
-        session.$syncInformUndoManager();
-
-        editor.removeLines();
-        var step3 = session.toString();
-        assert.equal(step3, "");
-        session.$syncInformUndoManager();
-
-        undoManager.undo();
-        session.$syncInformUndoManager();
-        assert.equal(session.toString(), step2);
-
-        undoManager.undo();
-        session.$syncInformUndoManager();
-        assert.equal(session.toString(), step1);
-
-        undoManager.undo();
-        session.$syncInformUndoManager();
-        assert.equal(session.toString(), initialText);
-
-        undoManager.undo();
-        session.$syncInformUndoManager();
-        assert.equal(session.toString(), initialText);
-    },
-
-    "test: remove left should remove character left of the cursor" : function() {
-        var session = new EditSession(["123", "456"]);
-
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 1);
-        editor.remove("left");
-        assert.equal(session.toString(), "123\n56");
-    },
-
-    "test: remove left should remove line break if cursor is at line start" : function() {
-        var session = new EditSession(["123", "456"]);
-
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 0);
-        editor.remove("left");
-        assert.equal(session.toString(), "123456");
-    },
-
-    "test: remove left should remove tabsize spaces if cursor is on a tab stop and preceeded by spaces" : function() {
-        var session = new EditSession(["123", "        456"]);
-        session.setUseSoftTabs(true);
-        session.setTabSize(4);
-
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 8);
-        editor.remove("left");
-        assert.equal(session.toString(), "123\n    456");
-    },
-    
-    "test: transpose at line start should be a noop": function() {
-        var session = new EditSession(["123", "4567", "89"]);
-        
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 0);
-        editor.transposeLetters();
-        
-        assert.equal(session.getValue(), ["123", "4567", "89"].join("\n"));
-    },
-    
-    "test: transpose in line should swap the charaters before and after the cursor": function() {
-        var session = new EditSession(["123", "4567", "89"]);
-        
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 2);
-        editor.transposeLetters();
-        
-        assert.equal(session.getValue(), ["123", "4657", "89"].join("\n"));
-    },
-    
-    "test: transpose at line end should swap the last two characters": function() {
-        var session = new EditSession(["123", "4567", "89"]);
-        
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 4);
-        editor.transposeLetters();
-        
-        assert.equal(session.getValue(), ["123", "4576", "89"].join("\n"));
-    },
-    
-    "test: transpose with non empty selection should be a noop": function() {
-        var session = new EditSession(["123", "4567", "89"]);
-        
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 1);
-        editor.getSelection().selectRight();
-        editor.transposeLetters();
-        
-        assert.equal(session.getValue(), ["123", "4567", "89"].join("\n"));
-    },
-    
-    "test: transpose should move the cursor behind the last swapped character": function() {
-        var session = new EditSession(["123", "4567", "89"]);
-        
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 2);
-        editor.transposeLetters();
-        assert.position(editor.getCursorPosition(), 1, 3);
-    },
-    
-    "test: remove to line end": function() {
-        var session = new EditSession(["123", "4567", "89"]);
-        
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 2);
-        editor.removeToLineEnd();
-        assert.equal(session.getValue(), ["123", "45", "89"].join("\n"));
-    },
-    
-    "test: remove to line end at line end should remove the new line": function() {
-        var session = new EditSession(["123", "4567", "89"]);
-        
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 4);
-        editor.removeToLineEnd();
-        assert.position(editor.getCursorPosition(), 1, 4);
-        assert.equal(session.getValue(), ["123", "456789"].join("\n"));
-    },
-
-    "test: transform selection to uppercase": function() {
-        var session = new EditSession(["ajax", "dot", "org"]);
-
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 0);
-        editor.getSelection().selectLineEnd();
-        editor.toUpperCase()
-        assert.equal(session.getValue(), ["ajax", "DOT", "org"].join("\n"));
-    },
-
-    "test: transform word to uppercase": function() {
-        var session = new EditSession(["ajax", "dot", "org"]);
-
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 0);
-        editor.toUpperCase()
-        assert.equal(session.getValue(), ["ajax", "DOT", "org"].join("\n"));
-        assert.position(editor.getCursorPosition(), 1, 0);
-    },
-
-    "test: transform selection to lowercase": function() {
-        var session = new EditSession(["AJAX", "DOT", "ORG"]);
-
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 0);
-        editor.getSelection().selectLineEnd();
-        editor.toLowerCase()
-        assert.equal(session.getValue(), ["AJAX", "dot", "ORG"].join("\n"));
-    },
-
-    "test: transform word to lowercase": function() {
-        var session = new EditSession(["AJAX", "DOT", "ORG"]);
-
-        var editor = new Editor(new MockRenderer(), session);
-        editor.moveCursorTo(1, 0);
-        editor.toLowerCase()
-        assert.equal(session.getValue(), ["AJAX", "dot", "ORG"].join("\n"));
-        assert.position(editor.getCursorPosition(), 1, 0);
-    }
-};
-
-});
-
-if (typeof module !== "undefined" && module === require.main) {
-    require("asyncjs").test.testcase(module.exports).exec()
-}

+ 335 - 0
services/web/public/js/ace/ext-beautify.js

@@ -0,0 +1,335 @@
+define("ace/ext/beautify/php_rules",["require","exports","module","ace/token_iterator"], function(require, exports, module) {
+"use strict";
+var TokenIterator = require("ace/token_iterator").TokenIterator;
+exports.newLines = [{
+    type: 'support.php_tag',
+    value: '<?php'
+}, {
+    type: 'support.php_tag',
+    value: '<?'
+}, {
+    type: 'support.php_tag',
+    value: '?>'
+}, {
+    type: 'paren.lparen',
+    value: '{',
+    indent: true
+}, {
+    type: 'paren.rparen',
+    breakBefore: true,
+    value: '}',
+    indent: false
+}, {
+    type: 'paren.rparen',
+    breakBefore: true,
+    value: '})',
+    indent: false,
+    dontBreak: true
+}, {
+    type: 'comment'
+}, {
+    type: 'text',
+    value: ';'
+}, {
+    type: 'text',
+    value: ':',
+    context: 'php'
+}, {
+    type: 'keyword',
+    value: 'case',
+    indent: true,
+    dontBreak: true
+}, {
+    type: 'keyword',
+    value: 'default',
+    indent: true,
+    dontBreak: true
+}, {
+    type: 'keyword',
+    value: 'break',
+    indent: false,
+    dontBreak: true
+}, {
+    type: 'punctuation.doctype.end',
+    value: '>'
+}, {
+    type: 'meta.tag.punctuation.end',
+    value: '>'
+}, {
+    type: 'meta.tag.punctuation.begin',
+    value: '<',
+    blockTag: true,
+    indent: true,
+    dontBreak: true
+}, {
+    type: 'meta.tag.punctuation.begin',
+    value: '</',
+    indent: false,
+    breakBefore: true,
+    dontBreak: true
+}, {
+    type: 'punctuation.operator',
+    value: ';'
+}];
+
+exports.spaces = [{
+    type: 'xml-pe',
+    prepend: true
+},{
+    type: 'entity.other.attribute-name',
+    prepend: true
+}, {
+    type: 'storage.type',
+    value: 'var',
+    append: true
+}, {
+    type: 'storage.type',
+    value: 'function',
+    append: true
+}, {
+    type: 'keyword.operator',
+    value: '='
+}, {
+    type: 'keyword',
+    value: 'as',
+    prepend: true,
+    append: true
+}, {
+    type: 'keyword',
+    value: 'function',
+    append: true
+}, {
+    type: 'support.function',
+    next: /[^\(]/,
+    append: true
+}, {
+    type: 'keyword',
+    value: 'or',
+    append: true,
+    prepend: true
+}, {
+    type: 'keyword',
+    value: 'and',
+    append: true,
+    prepend: true
+}, {
+    type: 'keyword',
+    value: 'case',
+    append: true
+}, {
+    type: 'keyword.operator',
+    value: '||',
+    append: true,
+    prepend: true
+}, {
+    type: 'keyword.operator',
+    value: '&&',
+    append: true,
+    prepend: true
+}];
+exports.singleTags = ['!doctype','area','base','br','hr','input','img','link','meta'];
+
+exports.transform = function(iterator, maxPos, context) {
+    var token = iterator.getCurrentToken();
+    
+    var newLines = exports.newLines;
+    var spaces = exports.spaces;
+    var singleTags = exports.singleTags;
+
+    var code = '';
+    
+    var indentation = 0;
+    var dontBreak = false;
+    var tag;
+    var lastTag;
+    var lastToken = {};
+    var nextTag;
+    var nextToken = {};
+    var breakAdded = false;
+    var value = '';
+
+    while (token!==null) {
+        console.log(token);
+
+        if( !token ){
+            token = iterator.stepForward();
+            continue;
+        }
+        if( token.type == 'support.php_tag' && token.value != '?>' ){
+            context = 'php';
+        }
+        else if( token.type == 'support.php_tag' && token.value == '?>' ){
+            context = 'html';
+        }
+        else if( token.type == 'meta.tag.name.style' && context != 'css' ){
+            context = 'css';
+        }
+        else if( token.type == 'meta.tag.name.style' && context == 'css' ){
+            context = 'html';
+        }
+        else if( token.type == 'meta.tag.name.script' && context != 'js' ){
+            context = 'js';
+        }
+        else if( token.type == 'meta.tag.name.script' && context == 'js' ){
+            context = 'html';
+        }
+
+        nextToken = iterator.stepForward();
+        if (nextToken && nextToken.type.indexOf('meta.tag.name') == 0) {
+            nextTag = nextToken.value;
+        }
+        if ( lastToken.type == 'support.php_tag' && lastToken.value == '<?=') {
+            dontBreak = true;
+        }
+        if (token.type == 'meta.tag.name') {
+            token.value = token.value.toLowerCase();
+        }
+        if (token.type == 'text') {
+            token.value = token.value.trim();
+        }
+        if (!token.value) {
+            token = nextToken;
+            continue;
+        }
+        value = token.value;
+        for (var i in spaces) {
+            if (
+                token.type == spaces[i].type &&
+                (!spaces[i].value || token.value == spaces[i].value) &&
+                (
+                    nextToken &&
+                    (!spaces[i].next || spaces[i].next.test(nextToken.value))
+                )
+            ) {
+                if (spaces[i].prepend) {
+                    value = ' ' + token.value;
+                }
+
+                if (spaces[i].append) {
+                    value += ' ';
+                }
+            }
+        }
+        if (token.type.indexOf('meta.tag.name') == 0) {
+            tag = token.value;
+        }
+        breakAdded = false;
+        for (i in newLines) {
+            if (
+                token.type == newLines[i].type &&
+                (
+                    !newLines[i].value ||
+                    token.value == newLines[i].value
+                ) &&
+                (
+                    !newLines[i].blockTag ||
+                    singleTags.indexOf(nextTag) === -1
+                ) &&
+                (
+                    !newLines[i].context ||
+                    newLines[i].context === context
+                )
+            ) {
+                if (newLines[i].indent === false) {
+                    indentation--;
+                }
+
+                if (
+                    newLines[i].breakBefore &&
+                    ( !newLines[i].prev || newLines[i].prev.test(lastToken.value) )
+                ) {
+                    code += "\n";
+                    breakAdded = true;
+                    for (i = 0; i < indentation; i++) {
+                        code += "\t";
+                    }
+                }
+
+                break;
+            }
+        }
+
+        if (dontBreak===false) {
+            for (i in newLines) {
+                if (
+                    lastToken.type == newLines[i].type &&
+                    (
+                        !newLines[i].value || lastToken.value == newLines[i].value
+                    ) &&
+                    (
+                        !newLines[i].blockTag ||
+                        singleTags.indexOf(tag) === -1
+                    ) &&
+                    (
+                        !newLines[i].context ||
+                        newLines[i].context === context
+                    )
+                ) {
+                    if (newLines[i].indent === true) {
+                        indentation++;
+                    }
+
+                    if (!newLines[i].dontBreak  && !breakAdded) {
+                        code += "\n";
+                        for (i = 0; i < indentation; i++) {
+                            code += "\t";
+                        }
+                    }
+
+                    break;
+                }
+            }
+        }
+
+        code += value;
+        if ( lastToken.type == 'support.php_tag' && lastToken.value == '?>' ) {
+            dontBreak = false;
+        }
+        lastTag = tag;
+
+        lastToken = token;
+
+        token = nextToken;
+
+        if (token===null) {
+            break;
+        }
+    }
+    
+    return code;
+};
+
+
+
+});
+
+define("ace/ext/beautify",["require","exports","module","ace/token_iterator","ace/ext/beautify/php_rules"], function(require, exports, module) {
+"use strict";
+var TokenIterator = require("ace/token_iterator").TokenIterator;
+
+var phpTransform = require("./beautify/php_rules").transform;
+
+exports.beautify = function(session) {
+    var iterator = new TokenIterator(session, 0, 0);
+    var token = iterator.getCurrentToken();
+
+    var context = session.$modeId.split("/").pop();
+
+    var code = phpTransform(iterator, context);
+    session.doc.setValue(code);
+};
+
+exports.commands = [{
+    name: "beautify",
+    exec: function(editor) {
+        exports.beautify(editor.session);
+    },
+    bindKey: "Ctrl-Shift-B"
+}]
+
+});
+;
+                (function() {
+                    window.require(["ace/ext/beautify"], function() {});
+                })();
+            

+ 6 - 444
services/web/public/js/ace/ext/chromevox.js → services/web/public/js/ace/ext-chromevox.js

@@ -1,125 +1,46 @@
-define(function(require, exports, module) {
-
-/* ChromeVox Ace namespace. */
+define("ace/ext/chromevox",["require","exports","module","ace/editor","ace/config"], function(require, exports, module) {
 var cvoxAce = {};
-
-/* Typedefs for Closure compiler. */
-/**
- * @typedef {{
-    rate: number,
-    pitch: number,
-    volume: number,
-    relativePitch: number,
-    punctuationEcho: string
-   }}
- */
-/* TODO(peterxiao): Export this typedef through cvox.Api. */
 cvoxAce.SpeechProperty;
-
-/**
- * @typedef {{
- *   row: number,
- *   column: number
- * }}
- */
 cvoxAce.Cursor;
-
-/**
- * @typedef {{
-    type: string,
-    value: string
-   }}
- }
- */
 cvoxAce.Token;
-
-/**
- * These are errors and information that Ace will display in the gutter.
- * @typedef {{
-    row: number,
-    column: number,
-    value: string
-   }}
- }
- */
 cvoxAce.Annotation;
-
-/* Speech Properties. */
-/**
- * Speech property for speaking constant tokens.
- * @type {cvoxAce.SpeechProperty}
- */
 var CONSTANT_PROP = {
   'rate': 0.8,
   'pitch': 0.4,
   'volume': 0.9
 };
-
-/**
- * Default speech property for speaking tokens.
- * @type {cvoxAce.SpeechProperty}
- */
 var DEFAULT_PROP = {
   'rate': 1,
   'pitch': 0.5,
   'volume': 0.9
 };
-
-/**
- * Speech property for speaking entity tokens.
- * @type {cvoxAce.SpeechProperty}
- */
 var ENTITY_PROP = {
   'rate': 0.8,
   'pitch': 0.8,
   'volume': 0.9
 };
-
-/**
- * Speech property for speaking keywords.
- * @type {cvoxAce.SpeechProperty}
- */
 var KEYWORD_PROP = {
   'rate': 0.8,
   'pitch': 0.3,
   'volume': 0.9
 };
-
-/**
- * Speech property for speaking storage tokens.
- * @type {cvoxAce.SpeechProperty}
- */
 var STORAGE_PROP = {
   'rate': 0.8,
   'pitch': 0.7,
   'volume': 0.9
 };
-
-/**
- * Speech property for speaking variable tokens.
- * @type {cvoxAce.SpeechProperty}
- */
 var VARIABLE_PROP = {
   'rate': 0.8,
   'pitch': 0.8,
   'volume': 0.9
 };
-
-/**
- * Speech property for speaking deleted text.
- * @type {cvoxAce.SpeechProperty}
- */
 var DELETED_PROP = {
   'punctuationEcho': 'none',
   'relativePitch': -0.6
 };
-
-/* Constants for Earcons. */
 var ERROR_EARCON = 'ALERT_NONMODAL';
 var MODE_SWITCH_EARCON = 'ALERT_MODAL';
 var NO_MATCH_EARCON = 'INVALID_KEYPRESS';
-
-/* Constants for vim state. */
 var INSERT_MODE_STATE = 'insertMode';
 var COMMAND_MODE_STATE = 'start';
 
@@ -133,10 +54,6 @@ var REPLACE_LIST = [
     newSubstr: ' colon '
   }
 ];
-
-/**
- * Context menu commands.
- */
 var Command = {
   SPEAK_ANNOT: 'annots',
   SPEAK_ALL_ANNOTS: 'all_annots',
@@ -146,104 +63,30 @@ var Command = {
   TOGGLE_DISPLACEMENT: 'toggle_displacement',
   FOCUS_TEXT: 'focus_text'
 };
-
-/**
- * Key prefix for each shortcut.
- */
 var KEY_PREFIX = 'CONTROL + SHIFT ';
-
-/* Globals. */
 cvoxAce.editor = null;
-/**
- * Last cursor position.
- * @type {cvoxAce.Cursor}
- */
 var lastCursor = null;
-
-/**
- * Table of annotations.
- * @typedef {!Object.<number, Object<number, cvoxAce.Annotation>>}
- */
 var annotTable = {};
-
-/**
- * Whether to speak character, word, and then line. This allows blind users
- * to know the location of the cursor when they change lines.
- * @typedef {boolean}
- */
 var shouldSpeakRowLocation = false;
-
-/**
- * Whether to speak displacement.
- * @typedef {boolean}
- */
 var shouldSpeakDisplacement = false;
-
-/**
- * Whether text was changed to cause a cursor change event.
- * @typedef {boolean}
- */
 var changed = false;
-
-/**
- * Current state vim is in.
- */
 var vimState = null;
-
-/**
- * Mapping from key code to shortcut.
- */
 var keyCodeToShortcutMap = {};
-
-/**
- * Mapping from command to shortcut.
- */
 var cmdToShortcutMap = {};
-
-/**
- * Get shortcut string from keyCode.
- * @param {number} keyCode Key code of shortcut.
- * @return {string} String representation of shortcut.
- */
 var getKeyShortcutString = function(keyCode) {
   return KEY_PREFIX + String.fromCharCode(keyCode);
 };
-
-/**
- * Return if in vim mode.
- * @return {boolean} True if in Vim mode.
- */
 var isVimMode = function() {
   var keyboardHandler = cvoxAce.editor.keyBinding.getKeyboardHandler();
   return keyboardHandler.$id === 'ace/keyboard/vim';
 };
-
-/**
- * Gets the current token.
- * @param {!cvoxAce.Cursor} cursor Current position of the cursor.
- * @return {!cvoxAce.Token} Token at the current position.
- */
 var getCurrentToken = function(cursor) {
   return cvoxAce.editor.getSession().getTokenAt(cursor.row, cursor.column + 1);
 };
-
-/**
- * Gets the current line the cursor is under.
- * @param {!cvoxAce.Cursor} cursor Current cursor position.
- */
 var getCurrentLine = function(cursor) {
   return cvoxAce.editor.getSession().getLine(cursor.row);
 };
-
-/**
- * Event handler for row changes. When the user changes rows we want to speak
- * the line so the user can work on this line. If shouldSpeakRowLocation is on
- * then we speak the character, then the row, then the line so the user knows
- * where the cursor is.
- * @param {!cvoxAce.Cursor} currCursor Current cursor position.
- */
 var onRowChange = function(currCursor) {
-  /* Notify that this line has an annotation. */
   if (annotTable[currCursor.row]) {
     cvox.Api.playEarcon(ERROR_EARCON);
   }
@@ -256,28 +99,16 @@ var onRowChange = function(currCursor) {
     speakLine(currCursor.row, 0);
   }
 };
-
-/**
- * Returns whether the cursor is at the beginning of a word. A word is
- * a grouping of alphanumeric characters including underscores.
- * @param {!cvoxAce.Cursor} cursor Current cursor position.
- * @return {boolean} Whether there is word.
- */
 var isWord = function(cursor) {
   var line = getCurrentLine(cursor);
   var lineSuffix = line.substr(cursor.column - 1);
   if (cursor.column === 0) {
     lineSuffix = ' ' + line;
   }
-  /* Use regex to tell if the suffix is at the start of a new word. */
   var firstWordRegExp = /^\W(\w+)/;
   var words = firstWordRegExp.exec(lineSuffix);
   return words !== null;
 };
-
-/**
- * A mapping of syntax type to speech properties / expanding rules.
- */
 var rules = {
   'constant': {
     prop: CONSTANT_PROP
@@ -316,20 +147,9 @@ var rules = {
     ]
   }
 };
-
-/**
- * Default rule to be used.
- */
 var DEFAULT_RULE = {
   prop: DEFAULT_RULE
 };
-
-/**
- * Expands substrings to how they are read based on the given rules.
- * @param {string} value Text to be expanded.
- * @param {Array.<Object>} replaceRules Rules to determine expansion.
- * @return {string} New expanded value.
- */
 var expand = function(value, replaceRules) {
   var newValue = value;
   for (var i = 0; i < replaceRules.length; i++) {
@@ -339,16 +159,7 @@ var expand = function(value, replaceRules) {
   }
   return newValue;
 };
-
-/**
- * Merges tokens from start inclusive to end exclusive.
- * @param {Array.<cvoxAce.Token>} Tokens to be merged.
- * @param {number} start Start index inclusive.
- * @param {number} end End index exclusive.
- * @return {cvoxAce.Token} Merged token.
- */
 var mergeTokens = function(tokens, start, end) {
-  /* Different type of token found! Merge all previous like tokens. */
   var newToken = {};
   newToken.value = '';
   newToken.type = tokens[start].type;
@@ -357,12 +168,6 @@ var mergeTokens = function(tokens, start, end) {
   }
   return newToken;
 };
-
-/**
- * Merges tokens that use the same speech properties.
- * @param {Array.<cvoxAce.Token>} tokens Tokens to be merged.
- * @return {Array.<cvoxAce.Token>} Merged tokens.
- */
 var mergeLikeTokens = function(tokens) {
   if (tokens.length <= 1) {
     return tokens;
@@ -380,23 +185,11 @@ var mergeLikeTokens = function(tokens) {
   newTokens.push(mergeTokens(tokens, lastLikeIndex, tokens.length));
   return newTokens;
 };
-
-/**
- * Returns if given row is a whitespace row.
- * @param {number} row Row.
- * @return {boolean} True if row is whitespaces.
- */
 var isRowWhiteSpace = function(row) {
   var line = cvoxAce.editor.getSession().getLine(row);
   var whiteSpaceRegexp = /^\s*$/;
   return whiteSpaceRegexp.exec(line) !== null;
 };
-
-/**
- * Speak the line with syntax properties.
- * @param {number} row Row to speak.
- * @param {number} queue Queue mode to speak.
- */
 var speakLine = function(row, queue) {
   var tokens = cvoxAce.editor.getSession().getTokens(row);
   if (tokens.length === 0 || isRowWhiteSpace(row)) {
@@ -405,41 +198,19 @@ var speakLine = function(row, queue) {
   }
   tokens = mergeLikeTokens(tokens);
   var firstToken = tokens[0];
-  /* Filter out first token. */
   tokens = tokens.filter(function(token) {
     return token !== firstToken;
   });
-  /* Speak first token separately to flush if queue. */
   speakToken_(firstToken, queue);
-  /* Speak rest of tokens. */
   tokens.forEach(speakTokenQueue);
 };
-
-/**
- * Speak the token based on the syntax of the token, flushing.
- * @param {!cvoxAce.Token} token Token to speak.
- * @param {number} queue Queue mode.
- */
 var speakTokenFlush = function(token) {
   speakToken_(token, 0);
 };
-
-/**
- * Speak the token based on the syntax of the token, queueing.
- * @param {!cvoxAce.Token} token Token to speak.
- * @param {number} queue Queue mode.
- */
 var speakTokenQueue = function(token) {
   speakToken_(token, 1);
 };
-
-/**
- * @param {!cvoxAce.Token} token Token to speak.
- * Get the token speech property.
- */
 var getTokenRule = function(token) {
-  /* Types are period delimited. In this case, we only syntax speak the outer
-   * most type of token. */
   if (!token || !token.type) {
     return;
   }
@@ -454,13 +225,6 @@ var getTokenRule = function(token) {
   }
   return rule;
 };
-
-/**
- * Speak the token based on the syntax of the token.
- * @private
- * @param {!cvoxAce.Token} token Token to speak.
- * @param {number} queue Queue mode.
- */
 var speakToken_ = function(token, queue) {
   var rule = getTokenRule(token);
   var value = expand(token.value, REPLACE_LIST);
@@ -469,45 +233,19 @@ var speakToken_ = function(token, queue) {
   }
   cvox.Api.speak(value, queue, rule.prop);
 };
-
-/**
- * Speaks the character under the cursor. This is queued.
- * @param {!cvoxAce.Cursor} cursor Current cursor position.
- * @return {string} Character.
- */
 var speakChar = function(cursor) {
   var line = getCurrentLine(cursor);
   cvox.Api.speak(line[cursor.column], 1);
 };
-
-/**
- * Speaks the jump from lastCursor to currCursor. This function assumes the
- * jump takes place on the current line.
- * @param {!cvoxAce.Cursor} lastCursor Previous cursor position.
- * @param {!cvoxAce.Cursor} currCursor Current cursor position.
- */
 var speakDisplacement = function(lastCursor, currCursor) {
   var line = getCurrentLine(currCursor);
-
-  /* Get the text that we jumped past. */
   var displace = line.substring(lastCursor.column, currCursor.column);
-
-  /* Speak out loud spaces. */
   displace = displace.replace(/ /g, ' space ');
   cvox.Api.speak(displace);
 };
-
-/**
- * Speaks the word if the cursor jumped to a new word or to the beginning
- * of the line. Otherwise speak the charactor.
- * @param {!cvoxAce.Cursor} lastCursor Previous cursor position.
- * @param {!cvoxAce.Cursor} currCursor Current cursor position.
- */
 var speakCharOrWordOrLine = function(lastCursor, currCursor) {
-  /* Say word only if jump. */
   if (Math.abs(lastCursor.column - currCursor.column) !== 1) {
     var currLineLength = getCurrentLine(currCursor).length;
-    /* Speak line if jumping to beginning or end of line. */
     if (currCursor.column === 0 || currCursor.column === currLineLength) {
       speakLine(currCursor.row, 0);
       return;
@@ -520,15 +258,6 @@ var speakCharOrWordOrLine = function(lastCursor, currCursor) {
   }
   speakChar(currCursor);
 };
-
-/**
- * Event handler for column changes. If shouldSpeakDisplacement is on, then
- * we just speak displacements in row changes. Otherwise, we either speak
- * the character for single character movements, the word when jumping to the
- * next word, or the entire line if jumping to beginning or end of the line.
- * @param {!cvoxAce.Cursor} lastCursor Previous cursor position.
- * @param {!cvoxAce.Cursor} currCursor Current cursor position.
- */
 var onColumnChange = function(lastCursor, currCursor) {
   if (!cvoxAce.editor.selection.isEmpty()) {
     speakDisplacement(lastCursor, currCursor);
@@ -540,15 +269,7 @@ var onColumnChange = function(lastCursor, currCursor) {
     speakCharOrWordOrLine(lastCursor, currCursor);
   }
 };
-
-/**
- * Event handler for cursor changes. Classify cursor changes as either row or
- * column changes, then delegate accordingly.
- * @param {!Event} evt The event.
- */
 var onCursorChange = function(evt) {
-  /* Do not speak if cursor change was a result of text insertion. We want to
-   * speak the text that was inserted and not where the cursor lands. */
   if (changed) {
     changed = false;
     return;
@@ -561,54 +282,29 @@ var onCursorChange = function(evt) {
   }
   lastCursor = currCursor;
 };
-
-/**
- * Event handler for selection changes.
- * @param {!Event} evt The event.
- */
 var onSelectionChange = function(evt) {
-  /* Assumes that when selection changes to empty, the user has unselected. */
   if (cvoxAce.editor.selection.isEmpty()) {
     cvox.Api.speak('unselected');
   }
 };
-
-/**
- * Event handler for source changes. We want auditory feedback for inserting
- * and deleting text.
- * @param {!Event} evt The event.
- */
 var onChange = function(evt) {
   var data = evt.data;
   switch (data.action) {
   case 'removeText':
     cvox.Api.speak(data.text, 0, DELETED_PROP);
-    /* Let the future cursor change event know it's from text change. */
     changed = true;
     break;
   case 'insertText':
     cvox.Api.speak(data.text, 0);
-    /* Let the future cursor change event know it's from text change. */
     changed = true;
     break;
   }
 };
-
-/**
- * Returns whether or not the annotation is new.
- * @param {!cvoxAce.Annotation} annot Annotation in question.
- * @return {boolean} Whether annot is new.
- */
 var isNewAnnotation = function(annot) {
   var row = annot.row;
   var col = annot.column;
   return !annotTable[row] || !annotTable[row][col];
 };
-
-/**
- * Populates the annotation table.
- * @param {!Array.<cvoxAce.Annotation>} annotations Array of annotations.
- */
 var populateAnnotations = function(annotations) {
   annotTable = {};
   for (var i = 0; i < annotations.length; i++) {
@@ -621,12 +317,6 @@ var populateAnnotations = function(annotations) {
     annotTable[row][col] = annotation;
   }
 };
-
-/**
- * Event handler for annotation changes. We want to notify the user when an
- * a new annotation appears.
- * @param {!Event} evt Event.
- */
 var onAnnotationChange = function(evt) {
   var annotations = cvoxAce.editor.getSession().getAnnotations();
   var newAnnotations = annotations.filter(isNewAnnotation);
@@ -635,58 +325,29 @@ var onAnnotationChange = function(evt) {
   }
   populateAnnotations(annotations);
 };
-
-/**
- * Speak annotation.
- * @param {!cvoxAce.Annotation} annot Annotation to speak.
- */
 var speakAnnot = function(annot) {
   var annotText = annot.type + ' ' + annot.text + ' on ' +
       rowColToString(annot.row, annot.column);
   annotText = annotText.replace(';', 'semicolon');
   cvox.Api.speak(annotText, 1);
 };
-
-/**
- * Speak annotations in a row.
- * @param {number} row Row of annotations to speak.
- */
 var speakAnnotsByRow = function(row) {
   var annots = annotTable[row];
   for (var col in annots) {
     speakAnnot(annots[col]);
   }
 };
-
-/**
- * Get a string representation of a row and column.
- * @param {boolean} row Zero indexed row.
- * @param {boolean} col Zero indexed column.
- * @return {string} Row and column to be spoken.
- */
 var rowColToString = function(row, col) {
   return 'row ' + (row + 1) + ' column ' + (col + 1);
 };
-
-/**
- * Speaks the row and column.
- */
 var speakCurrRowAndCol = function() {
   cvox.Api.speak(rowColToString(lastCursor.row, lastCursor.column));
 };
-
-/**
- * Speaks all annotations.
- */
 var speakAllAnnots = function() {
   for (var row in annotTable) {
     speakAnnotsByRow(row);
   }
 };
-
-/**
- * Speak the vim mode. If no vim mode, this function does nothing.
- */
 var speakMode = function() {
   if (!isVimMode()) {
     return;
@@ -700,38 +361,22 @@ var speakMode = function() {
     break;
   }
 };
-
-/**
- * Toggle speak location.
- */
 var toggleSpeakRowLocation = function() {
   shouldSpeakRowLocation = !shouldSpeakRowLocation;
-  /* Auditory feedback of the change. */
   if (shouldSpeakRowLocation) {
     cvox.Api.speak('Speak location on row change enabled.');
   } else {
     cvox.Api.speak('Speak location on row change disabled.');
   }
 };
-
-/**
- * Toggle speak displacement.
- */
 var toggleSpeakDisplacement = function() {
   shouldSpeakDisplacement = !shouldSpeakDisplacement;
-  /* Auditory feedback of the change. */
   if (shouldSpeakDisplacement) {
     cvox.Api.speak('Speak displacement on column changes.');
   } else {
     cvox.Api.speak('Speak current character or word on column changes.');
   }
 };
-
-/**
- * Event handler for key down events. Gets the right shortcut from the map,
- * and calls the associated function.
- * @param {!Event} evt Keyboard event.
- */
 var onKeyDown = function(evt) {
   if (evt.ctrlKey && evt.shiftKey) {
     var shortcut = keyCodeToShortcutMap[evt.keyCode];
@@ -740,59 +385,34 @@ var onKeyDown = function(evt) {
     }
   }
 };
-
-/**
- * Event handler for status change events. Auditory feedback of changing
- * between vim states.
- * @param {!Event} evt Change status event.
- * @param {!Object} editor Editor state.
- */
 var onChangeStatus = function(evt, editor) {
   if (!isVimMode()) {
     return;
   }
   var state = editor.keyBinding.$data.state;
   if (state === vimState) {
-    /* State hasn't changed, do nothing. */
     return;
   }
   switch (state) {
   case INSERT_MODE_STATE:
     cvox.Api.playEarcon(MODE_SWITCH_EARCON);
-    /* When in insert mode, we want to speak out keys as feedback. */
     cvox.Api.setKeyEcho(true);
     break;
   case COMMAND_MODE_STATE:
     cvox.Api.playEarcon(MODE_SWITCH_EARCON);
-    /* When in command mode, we want don't speak out keys because those keys
-    * are not being inserted in the document. */
     cvox.Api.setKeyEcho(false);
     break;
   }
   vimState = state;
 };
-
-/**
- * Handles context menu events. This is a ChromeVox feature where hitting
- * the shortcut ChromeVox + comma will open up a search bar where you can
- * type in various commands. All keyboard shortcuts are also commands that
- * can be invoked. This handles the event that ChromeVox sends to the page.
- * @param {Event} evt Event received.
- */
 var contextMenuHandler = function(evt) {
   var cmd = evt.detail['customCommand'];
   var shortcut = cmdToShortcutMap[cmd];
   if (shortcut) {
     shortcut.func();
-    /* ChromeVox will bring focus to an element near the cursor instead of the
-     * text input. */
     cvoxAce.editor.focus();
   }
 };
-
-/**
- * Initialize the ChromeVox context menu.
- */
 var initContextMenu = function() {
   var ACTIONS = SHORTCUTS.map(function(shortcut) {
     return {
@@ -800,44 +420,22 @@ var initContextMenu = function() {
       cmd: shortcut.cmd
     };
   });
-
-  /* Attach ContextMenuActions. */
   var body = document.querySelector('body');
   body.setAttribute('contextMenuActions', JSON.stringify(ACTIONS));
-
-  /* Listen for ContextMenu events. */
   body.addEventListener('ATCustomEvent', contextMenuHandler, true);
 };
-
-/**
- * Event handler for find events. When there is a match, we want to speak the
- * line we are now at. Otherwise, we want to notify the user there was no
- * match
- * @param {!Event} evt The event.
- */
 var onFindSearchbox = function(evt) {
   if (evt.match) {
-    /* There is still a match! Speak the line. */
     speakLine(lastCursor.row, 0);
   } else {
-    /* No match, give auditory feedback! */
     cvox.Api.playEarcon(NO_MATCH_EARCON);
   }
 };
-
-/**
- * Focus to text input.
- */
 var focus = function() {
   cvoxAce.editor.focus();
 };
-
-/**
- * Shortcut definitions.
- */
 var SHORTCUTS = [
   {
-    /* 1 key. */
     keyCode: 49,
     func: function() {
       speakAnnotsByRow(lastCursor.row);
@@ -846,56 +444,44 @@ var SHORTCUTS = [
     desc: 'Speak annotations on line'
   },
   {
-    /* 2 key. */
     keyCode: 50,
     func: speakAllAnnots,
     cmd: Command.SPEAK_ALL_ANNOTS,
     desc: 'Speak all annotations'
   },
   {
-    /* 3 key. */
     keyCode: 51,
     func: speakMode,
     cmd: Command.SPEAK_MODE,
     desc: 'Speak Vim mode'
   },
   {
-    /* 4 key. */
     keyCode: 52,
     func: toggleSpeakRowLocation,
     cmd: Command.TOGGLE_LOCATION,
     desc: 'Toggle speak row location'
   },
   {
-    /* 5 key. */
     keyCode: 53,
     func: speakCurrRowAndCol,
     cmd: Command.SPEAK_ROW_COL,
     desc: 'Speak row and column'
   },
   {
-    /* 6 key. */
     keyCode: 54,
     func: toggleSpeakDisplacement,
     cmd: Command.TOGGLE_DISPLACEMENT,
     desc: 'Toggle speak displacement'
   },
   {
-    /* 7 key. */
     keyCode: 55,
     func: focus,
     cmd: Command.FOCUS_TEXT,
     desc: 'Focus text'
   }
 ];
-
-/**
- * Event handler for focus events.
- */
 var onFocus = function() {
   cvoxAce.editor = editor;
-
-  /* Set up listeners. */
   editor.getSession().selection.on('changeCursor', onCursorChange);
   editor.getSession().selection.on('changeSelection', onSelectionChange);
   editor.getSession().on('change', onChange);
@@ -906,53 +492,24 @@ var onFocus = function() {
 
   lastCursor = editor.selection.getCursor();
 };
-
-/**
- * Initialize the theme.
- * @param {Object} editor Editor to use.
- */
 var init = function(editor) {
   onFocus();
-
-  /* Construct maps. */
   SHORTCUTS.forEach(function(shortcut) {
     keyCodeToShortcutMap[shortcut.keyCode] = shortcut;
     cmdToShortcutMap[shortcut.cmd] = shortcut;
   });
 
   editor.on('focus', onFocus);
-
-  /* Assume we start in command mode if vim. */
   if (isVimMode()) {
     cvox.Api.setKeyEcho(false);
   }
   initContextMenu();
 };
-
-/**
- * Returns if cvox exists, and the api exists.
- * @return {boolean} Whether not Cvox Api exists.
- */
 function cvoxApiExists() {
   return (typeof(cvox) !== 'undefined') && cvox && cvox.Api;
 }
-
-/**
- * Number of tries for Cvox loading.
- * @type {number}
- */
 var tries = 0;
-
-/**
- * Max number of tries to watch for Cvox loading.
- * @type {number}
- */
 var MAX_TRIES = 15;
-
-/**
- * Check for ChromeVox load.
- * @param {Object} editor Editor to use.
- */
 function watchForCvoxLoad(editor) {
   if (cvoxApiExists()) {
     init(editor);
@@ -978,3 +535,8 @@ require('../config').defineOptions(Editor.prototype, 'editor', {
 });
 
 });
+;
+                (function() {
+                    window.require(["ace/ext/chromevox"], function() {});
+                })();
+            

+ 7 - 50
services/web/public/js/ace/ext/elastic_tabstops_lite.js → services/web/public/js/ace/ext-elastic_tabstops_lite.js

@@ -1,34 +1,4 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2012, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
+define("ace/ext/elastic_tabstops_lite",["require","exports","module","ace/editor","ace/config"], function(require, exports, module) {
 "use strict";
 
 var ElasticTabstopsLite = function(editor) {
@@ -82,8 +52,6 @@ var ElasticTabstopsLite = function(editor) {
 
     this.$findCellWidthsForBlock = function(row) {
         var cellWidths = [], widths;
-
-        // starting row and backward
         var rowIter = row;
         while (rowIter >= 0) {
             widths = this.$cellWidthsForRow(rowIter);
@@ -94,8 +62,6 @@ var ElasticTabstopsLite = function(editor) {
             rowIter--;
         }
         var firstRow = rowIter + 1;
-
-        // forward (not including starting row)
         rowIter = row;
         var numRows = this.$editor.session.getLength();
 
@@ -114,7 +80,6 @@ var ElasticTabstopsLite = function(editor) {
 
     this.$cellWidthsForRow = function(row) {
         var selectionColumns = this.$selectionColumnsForRow(row);
-        // todo: support multicursor
 
         var tabs = [-1].concat(this.$tabsForRow(row));
         var widths = tabs.map(function(el) { return 0; } ).slice(1);
@@ -135,7 +100,6 @@ var ElasticTabstopsLite = function(editor) {
     this.$selectionColumnsForRow = function(row) {
         var selections = [], cursor = this.$editor.getCursorPosition();
         if (this.$editor.session.getSelection().isEmpty()) {
-            // todo: support multicursor
             if (row == cursor.row)
                 selections.push(cursor.column);
         }
@@ -153,8 +117,6 @@ var ElasticTabstopsLite = function(editor) {
                 console.error(column);
                 continue;
             }
-            // add an extra None to the end so that the end of the column automatically
-            // finishes a block
             column.push(NaN);
 
             for (var r = 0, s = column.length; r < s; r++) {
@@ -165,7 +127,6 @@ var ElasticTabstopsLite = function(editor) {
                     startingNewBlock = false;
                 }
                 if (isNaN(width)) {
-                    // block ended
                     blockEndRow = r;
 
                     for (var j = blockStartRow; j < blockEndRow; j++) {
@@ -216,8 +177,6 @@ var ElasticTabstopsLite = function(editor) {
             return;
 
         var bias = 0, location = -1;
-
-        // this always only contains two elements, so we're safe in the loop below
         var expandedSet = this.$izip(widths, rowTabs);
 
         for (var i = 0, l = expandedSet.length; i < l; i++) {
@@ -234,8 +193,6 @@ var ElasticTabstopsLite = function(editor) {
             var ispaces = partialLine.length - strippedPartialLine.length;
 
             if (difference > 0) {
-                // put the spaces after the tab and then delete the tab, so any insertion
-                // points behave as expected
                 this.$editor.session.getDocument().insertInLine({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t");
                 this.$editor.session.getDocument().removeInLine(row, it, it + 1);
 
@@ -248,8 +205,6 @@ var ElasticTabstopsLite = function(editor) {
             }
         }
     };
-
-    // the is a (naive) Python port--but works for these purposes
     this.$izip_longest = function(iterables) {
         if (!iterables[0])
             return [];
@@ -279,10 +234,7 @@ var ElasticTabstopsLite = function(editor) {
 
         return expandedSet;
     };
-
-    // an even more (naive) Python port
     this.$izip = function(widths, tabs) {
-        // grab the shorter size
         var size = widths.length >= tabs.length ? tabs.length : widths.length;
 
         var expandedSet = [];
@@ -316,4 +268,9 @@ require("../config").defineOptions(Editor.prototype, "editor", {
     }
 });
 
-});
+});
+;
+                (function() {
+                    window.require(["ace/ext/elastic_tabstops_lite"], function() {});
+                })();
+            

+ 417 - 93
services/web/public/js/ace/snippets.js → services/web/public/js/ace/ext-emmet.js

@@ -1,37 +1,10 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
+define("ace/snippets",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/lib/lang","ace/range","ace/anchor","ace/keyboard/hash_handler","ace/tokenizer","ace/lib/dom","ace/editor"], function(require, exports, module) {
 "use strict";
-var lang = require("./lib/lang")
-var Range = require("./range").Range
+var oop = require("./lib/oop");
+var EventEmitter = require("./lib/event_emitter").EventEmitter;
+var lang = require("./lib/lang");
+var Range = require("./range").Range;
+var Anchor = require("./anchor").Anchor;
 var HashHandler = require("./keyboard/hash_handler").HashHandler;
 var Tokenizer = require("./tokenizer").Tokenizer;
 var comparePoints = Range.comparePoints;
@@ -42,12 +15,14 @@ var SnippetManager = function() {
 };
 
 (function() {
+    oop.implement(this, EventEmitter);
+    
     this.getTokenizer = function() {
         function TabstopToken(str, _, stack) {
             str = str.substr(1);
             if (/^\d+$/.test(str) && !stack.inFormatString)
                 return [{tabstopId: parseInt(str, 10)}];
-            return [{text: str}]
+            return [{text: str}];
         }
         function escape(ch) {
             return "(?:[^\\\\" + ch + "]|\\\\.)";
@@ -125,7 +100,7 @@ var SnippetManager = function() {
         });
         SnippetManager.prototype.getTokenizer = function() {
             return SnippetManager.$tokenizer;
-        }
+        };
         return SnippetManager.$tokenizer;
     };
 
@@ -166,10 +141,9 @@ var SnippetManager = function() {
                 return s.getUseSoftTabs() ? "YES" : "NO";
             case "TAB_SIZE":
                 return s.getTabSize();
-            // defult but can't fill :(
             case "FILENAME":
             case "FILEPATH":
-                return "ace.ajax.org";
+                return "";
             case "FULLNAME":
                 return "Ace";
         }
@@ -180,8 +154,6 @@ var SnippetManager = function() {
             return this.variables[varName](editor, varName) || "";
         return this.$getDefaultValue(editor, varName) || "";
     };
-
-    // returns string formatted according to http://manual.macromates.com/en/regular_expressions#replacement_string_syntax_format_strings
     this.tmStrFormat = function(str, ch, editor) {
         var flag = ch.flag || "";
         var re = ch.guard;
@@ -262,15 +234,17 @@ var SnippetManager = function() {
         return result;
     };
 
-    this.insertSnippet = function(editor, snippetText) {
+    this.insertSnippetForSelection = function(editor, snippetText) {
         var cursor = editor.getCursorPosition();
         var line = editor.session.getLine(cursor.row);
-        var indentString = line.match(/^\s*/)[0];
         var tabString = editor.session.getTabString();
+        var indentString = line.match(/^\s*/)[0];
+        
+        if (cursor.column < indentString.length)
+            indentString = indentString.slice(0, cursor.column);
 
         var tokens = this.tokenizeTmSnippet(snippetText);
         tokens = this.resolveVariables(tokens, editor);
-        // indent
         tokens = tokens.map(function(x) {
             if (x == "\n")
                 return x + indentString;
@@ -278,7 +252,6 @@ var SnippetManager = function() {
                 return x.replace(/\t/g, tabString);
             return x;
         });
-        // tabstop values
         var tabstops = [];
         tokens.forEach(function(p, i) {
             if (typeof p != "object")
@@ -305,12 +278,10 @@ var SnippetManager = function() {
                 ts.value = value.join("");
             }
         });
-
-        // expand tabstop values
         tabstops.forEach(function(ts) {ts.length = 0});
         var expanding = {};
         function copyValue(val) {
-            var copy = []
+            var copy = [];
             for (var i = 0; i < val.length; i++) {
                 var p = val[i];
                 if (typeof p == "object") {
@@ -329,8 +300,9 @@ var SnippetManager = function() {
                 continue;
             var id = p.tabstopId;
             var i1 = tokens.indexOf(p, i + 1);
-            if (expanding[id] == p) { 
-                expanding[id] = null;
+            if (expanding[id]) {
+                if (expanding[id] === p)
+                    expanding[id] = null;
                 continue;
             }
             
@@ -343,9 +315,7 @@ var SnippetManager = function() {
 
             if (ts.indexOf(p) === -1)
                 ts.push(p);
-        };
-        
-        // convert to plain text
+        }
         var row = 0, column = 0;
         var text = "";
         tokens.forEach(function(t) {
@@ -367,18 +337,30 @@ var SnippetManager = function() {
         var end = editor.session.replace(range, text);
 
         var tabstopManager = new TabstopManager(editor);
-        tabstopManager.addTabstops(tabstops, range.start, end);
-        tabstopManager.tabNext();
+        var selectionId = editor.inVirtualSelectionMode && editor.selection.index;
+        tabstopManager.addTabstops(tabstops, range.start, end, selectionId);
+    };
+    
+    this.insertSnippet = function(editor, snippetText) {
+        var self = this;
+        if (editor.inVirtualSelectionMode)
+            return self.insertSnippetForSelection(editor, snippetText);
+        
+        editor.forEachSelection(function() {
+            self.insertSnippetForSelection(editor, snippetText);
+        }, null, {keepOrder: true});
+        
+        if (editor.tabstopManager)
+            editor.tabstopManager.tabNext();
     };
 
     this.$getScope = function(editor) {
         var scope = editor.session.$mode.$id || "";
         scope = scope.split("/").pop();
         if (scope === "html" || scope === "php") {
-            // PHP is actually HTML
-            if (scope === "php") 
+            if (scope === "php" && !editor.session.$mode.inlinePhp) 
                 scope = "html";
-            var c = editor.getCursorPosition()
+            var c = editor.getCursorPosition();
             var state = editor.session.getState(c.row);
             if (typeof state === "object") {
                 state = state[0];
@@ -396,16 +378,36 @@ var SnippetManager = function() {
         return scope;
     };
 
-    this.expandWithTab = function(editor) {
+    this.getActiveScopes = function(editor) {
+        var scope = this.$getScope(editor);
+        var scopes = [scope];
+        var snippetMap = this.snippetMap;
+        if (snippetMap[scope] && snippetMap[scope].includeScopes) {
+            scopes.push.apply(scopes, snippetMap[scope].includeScopes);
+        }
+        scopes.push("_");
+        return scopes;
+    };
+
+    this.expandWithTab = function(editor, options) {
+        var self = this;
+        var result = editor.forEachSelection(function() {
+            return self.expandSnippetForSelection(editor, options);
+        }, null, {keepOrder: true});
+        if (result && editor.tabstopManager)
+            editor.tabstopManager.tabNext();
+        return result;
+    };
+    
+    this.expandSnippetForSelection = function(editor, options) {
         var cursor = editor.getCursorPosition();
         var line = editor.session.getLine(cursor.row);
         var before = line.substring(0, cursor.column);
         var after = line.substr(cursor.column);
 
-        var scope = this.$getScope(editor);
         var snippetMap = this.snippetMap;
         var snippet;
-        [scope, "_"].some(function(scope) {
+        this.getActiveScopes(editor).some(function(scope) {
             var snippets = snippetMap[scope];
             if (snippets)
                 snippet = this.findMatchingSnippet(snippets, before, after);
@@ -413,7 +415,8 @@ var SnippetManager = function() {
         }, this);
         if (!snippet)
             return false;
-
+        if (options && options.dryRun)
+            return true;
         editor.session.doc.removeInLine(cursor.row,
             cursor.column - snippet.replaceBefore.length,
             cursor.column + snippet.replaceAfter.length
@@ -421,7 +424,7 @@ var SnippetManager = function() {
 
         this.variables.M__ = snippet.matchBefore;
         this.variables.T__ = snippet.matchAfter;
-        this.insertSnippet(editor, snippet.content);
+        this.insertSnippetForSelection(editor, snippet.content);
 
         this.variables.M__ = this.variables.T__ = null;
         return true;
@@ -453,7 +456,7 @@ var SnippetManager = function() {
         var self = this;
         function wrapRegexp(src) {
             if (src && !/^\^?\(.*\)\$?$|^\\b$/.test(src))
-                src = "(?:" + src + ")"
+                src = "(?:" + src + ")";
 
             return src || "";
         }
@@ -475,7 +478,7 @@ var SnippetManager = function() {
         function addSnippet(s) {
             if (!s.scope)
                 s.scope = scope || "_";
-            scope = s.scope
+            scope = s.scope;
             if (!snippetMap[scope]) {
                 snippetMap[scope] = [];
                 snippetNameMap[scope] = {};
@@ -501,12 +504,14 @@ var SnippetManager = function() {
 
             s.endRe = guardedRegexp(s.endTrigger, s.endGuard, true);
             s.endTriggerRe = new RegExp(s.endTrigger, "", true);
-        };
+        }
 
         if (snippets.content)
             addSnippet(snippets);
         else if (Array.isArray(snippets))
             snippets.forEach(addSnippet);
+        
+        this._signal("registerSnippets", {scope: scope});
     };
     this.unregister = function(snippets, scope) {
         var snippetMap = this.snippetMap;
@@ -535,7 +540,7 @@ var SnippetManager = function() {
         while (m = re.exec(str)) {
             if (m[1]) {
                 try {
-                    snippet = JSON.parse(m[1])
+                    snippet = JSON.parse(m[1]);
                     list.push(snippet);
                 } catch (e) {}
             } if (m[4]) {
@@ -562,10 +567,9 @@ var SnippetManager = function() {
         return list;
     };
     this.getSnippetByName = function(name, editor) {
-        var scope = editor && this.$getScope(editor);
         var snippetMap = this.snippetNameMap;
         var snippet;
-        [scope, "_"].some(function(scope) {
+        this.getActiveScopes(editor).some(function(scope) {
             var snippets = snippetMap[scope];
             if (snippets)
                 snippet = snippets[name];
@@ -589,9 +593,10 @@ var TabstopManager = function(editor) {
 };
 (function() {
     this.attach = function(editor) {
-        this.index = -1;
+        this.index = 0;
         this.ranges = [];
         this.tabstops = [];
+        this.$openTabstops = null;
         this.selectedTabstop = null;
 
         this.editor = editor;
@@ -631,7 +636,7 @@ var TabstopManager = function(editor) {
         }
         if (!this.$inChange && isRemove) {
             var ts = this.selectedTabstop;
-            var changedOutside = !ts.some(function(r) {
+            var changedOutside = ts && !ts.some(function(r) {
                 return comparePoints(r.start, start) <= 0 && comparePoints(r.end, end) >= 0;
             });
             if (changedOutside)
@@ -643,7 +648,7 @@ var TabstopManager = function(editor) {
             if (r.end.row < start.row)
                 continue;
 
-            if (comparePoints(start, r.start) < 0 && comparePoints(end, r.end) > 0) {
+            if (isRemove && comparePoints(start, r.start) < 0 && comparePoints(end, r.end) > 0) {
                 this.removeRange(r);
                 i--;
                 continue;
@@ -666,7 +671,7 @@ var TabstopManager = function(editor) {
     };
     this.updateLinkedFields = function() {
         var ts = this.selectedTabstop;
-        if (!ts.hasLinkedRanges)
+        if (!ts || !ts.hasLinkedRanges)
             return;
         this.$inChange = true;
         var session = this.editor.session;
@@ -675,7 +680,7 @@ var TabstopManager = function(editor) {
             var range = ts[i];
             if (!range.linked)
                 continue;
-            var fmt = exports.snippetManager.tmStrFormat(text, range.original)
+            var fmt = exports.snippetManager.tmStrFormat(text, range.original);
             session.replace(range, fmt);
         }
         this.$inChange = false;
@@ -686,7 +691,7 @@ var TabstopManager = function(editor) {
     };
     this.onChangeSelection = function() {
         if (!this.editor)
-            return
+            return;
         var lead = this.editor.selection.lead;
         var anchor = this.editor.selection.anchor;
         var isEmpty = this.editor.selection.isEmpty();
@@ -704,14 +709,17 @@ var TabstopManager = function(editor) {
         this.detach();
     };
     this.tabNext = function(dir) {
-        var max = this.tabstops.length - 1;
+        var max = this.tabstops.length;
         var index = this.index + (dir || 1);
-        index = Math.min(Math.max(index, 0), max);
-        this.selectTabstop(index);
+        index = Math.min(Math.max(index, 1), max);
         if (index == max)
+            index = 0;
+        this.selectTabstop(index);
+        if (index === 0)
             this.detach();
     };
     this.selectTabstop = function(index) {
+        this.$openTabstops = null;
         var ts = this.tabstops[this.index];
         if (ts)
             this.addTabstopMarkers(ts);
@@ -729,6 +737,8 @@ var TabstopManager = function(editor) {
                     continue;
                 sel.addRange(ts[i].clone(), true);
             }
+            if (sel.ranges[0])
+                sel.addRange(sel.ranges[0].clone());
         } else {
             this.editor.selection.setRange(ts.firstNonLinked);
         }
@@ -736,7 +746,8 @@ var TabstopManager = function(editor) {
         this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
     };
     this.addTabstops = function(tabstops, start, end) {
-        // add final tabstop if missing
+        if (!this.$openTabstops)
+            this.$openTabstops = [];
         if (!tabstops[0]) {
             var p = Range.fromPoints(end, end);
             moveRelative(p.start, start);
@@ -746,33 +757,43 @@ var TabstopManager = function(editor) {
         }
 
         var i = this.index;
-        var arg = [i, 0];
+        var arg = [i + 1, 0];
         var ranges = this.ranges;
-        var editor = this.editor;
-        tabstops.forEach(function(ts) {
+        tabstops.forEach(function(ts, index) {
+            var dest = this.$openTabstops[index] || ts;
+                
             for (var i = ts.length; i--;) {
                 var p = ts[i];
                 var range = Range.fromPoints(p.start, p.end || p.start);
                 movePoint(range.start, start);
                 movePoint(range.end, start);
                 range.original = p;
-                range.tabstop = ts;
+                range.tabstop = dest;
                 ranges.push(range);
-                ts[i] = range;
+                if (dest != ts)
+                    dest.unshift(range);
+                else
+                    dest[i] = range;
                 if (p.fmtString) {
                     range.linked = true;
-                    ts.hasLinkedRanges = true;
-                } else if (!ts.firstNonLinked)
-                    ts.firstNonLinked = range;
+                    dest.hasLinkedRanges = true;
+                } else if (!dest.firstNonLinked)
+                    dest.firstNonLinked = range;
             }
-            if (!ts.firstNonLinked)
-                ts.hasLinkedRanges = false;
-            arg.push(ts);
-            this.addTabstopMarkers(ts);
+            if (!dest.firstNonLinked)
+                dest.hasLinkedRanges = false;
+            if (dest === ts) {
+                arg.push(dest);
+                this.$openTabstops[index] = dest;
+            }
+            this.addTabstopMarkers(dest);
         }, this);
-        // tabstop 0 is the last one
-        arg.push(arg.splice(2, 1)[0]);
-        this.tabstops.splice.apply(this.tabstops, arg);
+        
+        if (arg.length > 2) {
+            if (this.tabstops.length)
+                arg.push(arg.splice(2, 1)[0]);
+            this.tabstops.splice.apply(this.tabstops, arg);
+        }
     };
 
     this.addTabstopMarkers = function(ts) {
@@ -795,11 +816,22 @@ var TabstopManager = function(editor) {
         i = this.ranges.indexOf(range);
         this.ranges.splice(i, 1);
         this.editor.session.removeMarker(range.markerId);
+        if (!range.tabstop.length) {
+            i = this.tabstops.indexOf(range.tabstop);
+            if (i != -1)
+                this.tabstops.splice(i, 1);
+            if (!this.tabstops.length)
+                this.detach();
+        }
     };
 
     this.keyboardHandler = new HashHandler();
     this.keyboardHandler.bindKeys({
         "Tab": function(ed) {
+            if (exports.snippetManager && exports.snippetManager.expandWithTab(ed)) {
+                return;
+            }
+
             ed.tabstopManager.tabNext(1);
         },
         "Shift-Tab": function(ed) {
@@ -809,13 +841,25 @@ var TabstopManager = function(editor) {
             ed.tabstopManager.detach();
         },
         "Return": function(ed) {
-            //ed.tabstopManager.tabNext(1);
             return false;
         }
     });
 }).call(TabstopManager.prototype);
 
 
+
+var changeTracker = {};
+changeTracker.onChange = Anchor.prototype.onChange;
+changeTracker.setPosition = function(row, column) {
+    this.pos.row = row;
+    this.pos.column = column;
+};
+changeTracker.update = function(pos, delta, $insertRight) {
+    this.$insertRight = $insertRight;
+    this.pos = pos; 
+    this.onChange(delta);
+};
+
 var movePoint = function(point, diff) {
     if (point.row == 0)
         point.column += diff.column;
@@ -841,4 +885,284 @@ require("./lib/dom").importCssString("\
 exports.snippetManager = new SnippetManager();
 
 
+var Editor = require("./editor").Editor;
+(function() {
+    this.insertSnippet = function(content, options) {
+        return exports.snippetManager.insertSnippet(this, content, options);
+    };
+    this.expandSnippet = function(options) {
+        return exports.snippetManager.expandWithTab(this, options);
+    };
+}).call(Editor.prototype);
+
+});
+
+define("ace/ext/emmet",["require","exports","module","ace/keyboard/hash_handler","ace/editor","ace/snippets","ace/range","resources","resources","range","tabStops","resources","utils","actions","ace/config"], function(require, exports, module) {
+"use strict";
+var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
+var Editor = require("ace/editor").Editor;
+var snippetManager = require("ace/snippets").snippetManager;
+var Range = require("ace/range").Range;
+var emmet;
+
+Editor.prototype.indexToPosition = function(index) {
+    return this.session.doc.indexToPosition(index);
+};
+
+Editor.prototype.positionToIndex = function(pos) {
+    return this.session.doc.positionToIndex(pos);
+};
+function AceEmmetEditor() {}
+
+AceEmmetEditor.prototype = {
+    setupContext: function(editor) {
+        this.ace = editor;
+        this.indentation = editor.session.getTabString();
+        if (!emmet)
+            emmet = window.emmet;
+        emmet.require("resources").setVariable("indentation", this.indentation);
+        this.$syntax = null;
+        this.$syntax = this.getSyntax();
+    },
+    getSelectionRange: function() {
+        var range = this.ace.getSelectionRange();
+        return {
+            start: this.ace.positionToIndex(range.start),
+            end: this.ace.positionToIndex(range.end)
+        };
+    },
+    createSelection: function(start, end) {
+        this.ace.selection.setRange({
+            start: this.ace.indexToPosition(start),
+            end: this.ace.indexToPosition(end)
+        });
+    },
+    getCurrentLineRange: function() {
+        var row = this.ace.getCursorPosition().row;
+        var lineLength = this.ace.session.getLine(row).length;
+        var index = this.ace.positionToIndex({row: row, column: 0});
+        return {
+            start: index,
+            end: index + lineLength
+        };
+    },
+    getCaretPos: function(){
+        var pos = this.ace.getCursorPosition();
+        return this.ace.positionToIndex(pos);
+    },
+    setCaretPos: function(index){
+        var pos = this.ace.indexToPosition(index);
+        this.ace.selection.moveToPosition(pos);
+    },
+    getCurrentLine: function() {
+        var row = this.ace.getCursorPosition().row;
+        return this.ace.session.getLine(row);
+    },
+    replaceContent: function(value, start, end, noIndent) {
+        if (end == null)
+            end = start == null ? this.getContent().length : start;
+        if (start == null)
+            start = 0;        
+        
+        var editor = this.ace;
+        var range = Range.fromPoints(editor.indexToPosition(start), editor.indexToPosition(end));
+        editor.session.remove(range);
+        
+        range.end = range.start;
+        
+        value = this.$updateTabstops(value);
+        snippetManager.insertSnippet(editor, value)
+    },
+    getContent: function(){
+        return this.ace.getValue();
+    },
+    getSyntax: function() {
+        if (this.$syntax)
+            return this.$syntax;
+        var syntax = this.ace.session.$modeId.split("/").pop();
+        if (syntax == "html" || syntax == "php") {
+            var cursor = this.ace.getCursorPosition();
+            var state = this.ace.session.getState(cursor.row);
+            if (typeof state != "string")
+                state = state[0];
+            if (state) {
+                state = state.split("-");
+                if (state.length > 1)
+                    syntax = state[0];
+                else if (syntax == "php")
+                    syntax = "html";
+            }
+        }
+        return syntax;
+    },
+    getProfileName: function() {
+        switch(this.getSyntax()) {
+          case "css": return "css";
+          case "xml":
+          case "xsl":
+            return "xml";
+          case "html":
+            var profile = emmet.require("resources").getVariable("profile");
+            if (!profile)
+                profile = this.ace.session.getLines(0,2).join("").search(/<!DOCTYPE[^>]+XHTML/i) != -1 ? "xhtml": "html";
+            return profile;
+        }
+        return "xhtml";
+    },
+    prompt: function(title) {
+        return prompt(title);
+    },
+    getSelection: function() {
+        return this.ace.session.getTextRange();
+    },
+    getFilePath: function() {
+        return "";
+    },
+    $updateTabstops: function(value) {
+        var base = 1000;
+        var zeroBase = 0;
+        var lastZero = null;
+        var range = emmet.require('range');
+        var ts = emmet.require('tabStops');
+        var settings = emmet.require('resources').getVocabulary("user");
+        var tabstopOptions = {
+            tabstop: function(data) {
+                var group = parseInt(data.group, 10);
+                var isZero = group === 0;
+                if (isZero)
+                    group = ++zeroBase;
+                else
+                    group += base;
+
+                var placeholder = data.placeholder;
+                if (placeholder) {
+                    placeholder = ts.processText(placeholder, tabstopOptions);
+                }
+
+                var result = '${' + group + (placeholder ? ':' + placeholder : '') + '}';
+
+                if (isZero) {
+                    lastZero = range.create(data.start, result);
+                }
+
+                return result
+            },
+            escape: function(ch) {
+                if (ch == '$') return '\\$';
+                if (ch == '\\') return '\\\\';
+                return ch;
+            }
+        };
+
+        value = ts.processText(value, tabstopOptions);
+
+        if (settings.variables['insert_final_tabstop'] && !/\$\{0\}$/.test(value)) {
+            value += '${0}';
+        } else if (lastZero) {
+            value = emmet.require('utils').replaceSubstring(value, '${0}', lastZero);
+        }
+        
+        return value;
+    }
+};
+
+
+var keymap = {
+    expand_abbreviation: {"mac": "ctrl+alt+e", "win": "alt+e"},
+    match_pair_outward: {"mac": "ctrl+d", "win": "ctrl+,"},
+    match_pair_inward: {"mac": "ctrl+j", "win": "ctrl+shift+0"},
+    matching_pair: {"mac": "ctrl+alt+j", "win": "alt+j"},
+    next_edit_point: "alt+right",
+    prev_edit_point: "alt+left",
+    toggle_comment: {"mac": "command+/", "win": "ctrl+/"},
+    split_join_tag: {"mac": "shift+command+'", "win": "shift+ctrl+`"},
+    remove_tag: {"mac": "command+'", "win": "shift+ctrl+;"},
+    evaluate_math_expression: {"mac": "shift+command+y", "win": "shift+ctrl+y"},
+    increment_number_by_1: "ctrl+up",
+    decrement_number_by_1: "ctrl+down",
+    increment_number_by_01: "alt+up",
+    decrement_number_by_01: "alt+down",
+    increment_number_by_10: {"mac": "alt+command+up", "win": "shift+alt+up"},
+    decrement_number_by_10: {"mac": "alt+command+down", "win": "shift+alt+down"},
+    select_next_item: {"mac": "shift+command+.", "win": "shift+ctrl+."},
+    select_previous_item: {"mac": "shift+command+,", "win": "shift+ctrl+,"},
+    reflect_css_value: {"mac": "shift+command+r", "win": "shift+ctrl+r"},
+
+    encode_decode_data_url: {"mac": "shift+ctrl+d", "win": "ctrl+'"},
+    expand_abbreviation_with_tab: "Tab",
+    wrap_with_abbreviation: {"mac": "shift+ctrl+a", "win": "shift+ctrl+a"}
+};
+
+var editorProxy = new AceEmmetEditor();
+exports.commands = new HashHandler();
+exports.runEmmetCommand = function(editor) {
+    editorProxy.setupContext(editor);
+    if (editorProxy.getSyntax() == "php")
+        return false;
+    var actions = emmet.require("actions");
+
+    if (this.action == "expand_abbreviation_with_tab") {
+        if (!editor.selection.isEmpty())
+            return false;
+    }
+    
+    if (this.action == "wrap_with_abbreviation") {
+        return setTimeout(function() {
+            actions.run("wrap_with_abbreviation", editorProxy);
+        }, 0);
+    }
+    
+    try {
+        var result = actions.run(this.action, editorProxy);
+    } catch(e) {
+        editor._signal("changeStatus", typeof e == "string" ? e : e.message);
+        console.log(e);
+        result = false
+    }
+    return result;
+};
+
+for (var command in keymap) {
+    exports.commands.addCommand({
+        name: "emmet:" + command,
+        action: command,
+        bindKey: keymap[command],
+        exec: exports.runEmmetCommand,
+        multiSelectAction: "forEach"
+    });
+}
+
+var onChangeMode = function(e, target) {
+    var editor = target;
+    if (!editor)
+        return;
+    var modeId = editor.session.$modeId;
+    var enabled = modeId && /css|less|scss|sass|stylus|html|php/.test(modeId);
+    if (e.enableEmmet === false)
+        enabled = false;
+    if (enabled)
+        editor.keyBinding.addKeyboardHandler(exports.commands);
+    else
+        editor.keyBinding.removeKeyboardHandler(exports.commands);
+};
+
+
+exports.AceEmmetEditor = AceEmmetEditor;
+require("ace/config").defineOptions(Editor.prototype, "editor", {
+    enableEmmet: {
+        set: function(val) {
+            this[val ? "on" : "removeListener"]("changeMode", onChangeMode);
+            onChangeMode({enableEmmet: !!val}, this);
+        },
+        value: true
+    }
+});
+
+
+exports.setCore = function(e) {emmet = e;};
 });
+;
+                (function() {
+                    window.require(["ace/ext/emmet"], function() {});
+                })();
+            

+ 6 - 0
services/web/public/js/ace/ext-error_marker.js

@@ -0,0 +1,6 @@
+
+;
+                (function() {
+                    window.require(["ace/ext/error_marker"], function() {});
+                })();
+            

+ 180 - 0
services/web/public/js/ace/ext-keybinding_menu.js

@@ -0,0 +1,180 @@
+define("ace/ext/menu_tools/overlay_page",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
+'use strict';
+var dom = require("../../lib/dom");
+var cssText = "#ace_settingsmenu, #kbshortcutmenu {\
+background-color: #F7F7F7;\
+color: black;\
+box-shadow: -5px 4px 5px rgba(126, 126, 126, 0.55);\
+padding: 1em 0.5em 2em 1em;\
+overflow: auto;\
+position: absolute;\
+margin: 0;\
+bottom: 0;\
+right: 0;\
+top: 0;\
+z-index: 9991;\
+cursor: default;\
+}\
+.ace_dark #ace_settingsmenu, .ace_dark #kbshortcutmenu {\
+box-shadow: -20px 10px 25px rgba(126, 126, 126, 0.25);\
+background-color: rgba(255, 255, 255, 0.6);\
+color: black;\
+}\
+.ace_optionsMenuEntry:hover {\
+background-color: rgba(100, 100, 100, 0.1);\
+-webkit-transition: all 0.5s;\
+transition: all 0.3s\
+}\
+.ace_closeButton {\
+background: rgba(245, 146, 146, 0.5);\
+border: 1px solid #F48A8A;\
+border-radius: 50%;\
+padding: 7px;\
+position: absolute;\
+right: -8px;\
+top: -8px;\
+z-index: 1000;\
+}\
+.ace_closeButton{\
+background: rgba(245, 146, 146, 0.9);\
+}\
+.ace_optionsMenuKey {\
+color: darkslateblue;\
+font-weight: bold;\
+}\
+.ace_optionsMenuCommand {\
+color: darkcyan;\
+font-weight: normal;\
+}";
+dom.importCssString(cssText);
+module.exports.overlayPage = function overlayPage(editor, contentElement, top, right, bottom, left) {
+    top = top ? 'top: ' + top + ';' : '';
+    bottom = bottom ? 'bottom: ' + bottom + ';' : '';
+    right = right ? 'right: ' + right + ';' : '';
+    left = left ? 'left: ' + left + ';' : '';
+
+    var closer = document.createElement('div');
+    var contentContainer = document.createElement('div');
+
+    function documentEscListener(e) {
+        if (e.keyCode === 27) {
+            closer.click();
+        }
+    }
+
+    closer.style.cssText = 'margin: 0; padding: 0; ' +
+        'position: fixed; top:0; bottom:0; left:0; right:0;' +
+        'z-index: 9990; ' +
+        'background-color: rgba(0, 0, 0, 0.3);';
+    closer.addEventListener('click', function() {
+        document.removeEventListener('keydown', documentEscListener);
+        closer.parentNode.removeChild(closer);
+        editor.focus();
+        closer = null;
+    });
+    document.addEventListener('keydown', documentEscListener);
+
+    contentContainer.style.cssText = top + right + bottom + left;
+    contentContainer.addEventListener('click', function(e) {
+        e.stopPropagation();
+    });
+
+    var wrapper = dom.createElement("div");
+    wrapper.style.position = "relative";
+    
+    var closeButton = dom.createElement("div");
+    closeButton.className = "ace_closeButton";
+    closeButton.addEventListener('click', function() {
+        closer.click();
+    });
+    
+    wrapper.appendChild(closeButton);
+    contentContainer.appendChild(wrapper);
+    
+    contentContainer.appendChild(contentElement);
+    closer.appendChild(contentContainer);
+    document.body.appendChild(closer);
+    editor.blur();
+};
+
+});
+
+define("ace/ext/menu_tools/get_editor_keyboard_shortcuts",["require","exports","module","ace/lib/keys"], function(require, exports, module) {
+"use strict";
+var keys = require("../../lib/keys");
+module.exports.getEditorKeybordShortcuts = function(editor) {
+    var KEY_MODS = keys.KEY_MODS;
+    var keybindings = [];
+    var commandMap = {};
+    editor.keyBinding.$handlers.forEach(function(handler) {
+        var ckb = handler.commandKeyBinding;
+        for (var i in ckb) {
+            var modifier = parseInt(i);
+            if (modifier == -1) {
+                modifier = "";
+            } else if(isNaN(modifier)) {
+                modifier = i;
+            } else {
+                modifier = "" +
+                    (modifier & KEY_MODS.command ? "Cmd-"   : "") +
+                    (modifier & KEY_MODS.ctrl    ? "Ctrl-"  : "") +
+                    (modifier & KEY_MODS.alt     ? "Alt-"   : "") +
+                    (modifier & KEY_MODS.shift   ? "Shift-" : "");
+            }
+            for (var key in ckb[i]) {
+                var command = ckb[i][key]
+                if (typeof command != "string")
+                    command  = command.name
+                if (commandMap[command]) {
+                    commandMap[command].key += "|" + modifier + key;
+                } else {
+                    commandMap[command] = {key: modifier+key, command: command};
+                    keybindings.push(commandMap[command]);
+                }
+            }
+        }
+    });
+    return keybindings;
+};
+
+});
+
+define("ace/ext/keybinding_menu",["require","exports","module","ace/editor","ace/ext/menu_tools/overlay_page","ace/ext/menu_tools/get_editor_keyboard_shortcuts"], function(require, exports, module) {
+    "use strict";
+    var Editor = require("ace/editor").Editor;
+    function showKeyboardShortcuts (editor) {
+        if(!document.getElementById('kbshortcutmenu')) {
+            var overlayPage = require('./menu_tools/overlay_page').overlayPage;
+            var getEditorKeybordShortcuts = require('./menu_tools/get_editor_keyboard_shortcuts').getEditorKeybordShortcuts;
+            var kb = getEditorKeybordShortcuts(editor);
+            var el = document.createElement('div');
+            var commands = kb.reduce(function(previous, current) {
+                return previous + '<div class="ace_optionsMenuEntry"><span class="ace_optionsMenuCommand">' 
+                    + current.command + '</span> : '
+                    + '<span class="ace_optionsMenuKey">' + current.key + '</span></div>';
+            }, '');
+
+            el.id = 'kbshortcutmenu';
+            el.innerHTML = '<h1>Keyboard Shortcuts</h1>' + commands + '</div>';
+            overlayPage(editor, el, '0', '0', '0', null);
+        }
+    };
+    module.exports.init = function(editor) {
+        Editor.prototype.showKeyboardShortcuts = function() {
+            showKeyboardShortcuts(this);
+        };
+        editor.commands.addCommands([{
+            name: "showKeyboardShortcuts",
+            bindKey: {win: "Ctrl-Alt-h", mac: "Command-Alt-h"},
+            exec: function(editor, line) {
+                editor.showKeyboardShortcuts();
+            }
+        }]);
+    };
+
+});
+;
+                (function() {
+                    window.require(["ace/ext/keybinding_menu"], function() {});
+                })();
+            

+ 1813 - 0
services/web/public/js/ace/ext-language_tools.js

@@ -0,0 +1,1813 @@
+define("ace/snippets",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/lib/lang","ace/range","ace/anchor","ace/keyboard/hash_handler","ace/tokenizer","ace/lib/dom","ace/editor"], function(require, exports, module) {
+"use strict";
+var oop = require("./lib/oop");
+var EventEmitter = require("./lib/event_emitter").EventEmitter;
+var lang = require("./lib/lang");
+var Range = require("./range").Range;
+var Anchor = require("./anchor").Anchor;
+var HashHandler = require("./keyboard/hash_handler").HashHandler;
+var Tokenizer = require("./tokenizer").Tokenizer;
+var comparePoints = Range.comparePoints;
+
+var SnippetManager = function() {
+    this.snippetMap = {};
+    this.snippetNameMap = {};
+};
+
+(function() {
+    oop.implement(this, EventEmitter);
+    
+    this.getTokenizer = function() {
+        function TabstopToken(str, _, stack) {
+            str = str.substr(1);
+            if (/^\d+$/.test(str) && !stack.inFormatString)
+                return [{tabstopId: parseInt(str, 10)}];
+            return [{text: str}];
+        }
+        function escape(ch) {
+            return "(?:[^\\\\" + ch + "]|\\\\.)";
+        }
+        SnippetManager.$tokenizer = new Tokenizer({
+            start: [
+                {regex: /:/, onMatch: function(val, state, stack) {
+                    if (stack.length && stack[0].expectIf) {
+                        stack[0].expectIf = false;
+                        stack[0].elseBranch = stack[0];
+                        return [stack[0]];
+                    }
+                    return ":";
+                }},
+                {regex: /\\./, onMatch: function(val, state, stack) {
+                    var ch = val[1];
+                    if (ch == "}" && stack.length) {
+                        val = ch;
+                    }else if ("`$\\".indexOf(ch) != -1) {
+                        val = ch;
+                    } else if (stack.inFormatString) {
+                        if (ch == "n")
+                            val = "\n";
+                        else if (ch == "t")
+                            val = "\n";
+                        else if ("ulULE".indexOf(ch) != -1) {
+                            val = {changeCase: ch, local: ch > "a"};
+                        }
+                    }
+
+                    return [val];
+                }},
+                {regex: /}/, onMatch: function(val, state, stack) {
+                    return [stack.length ? stack.shift() : val];
+                }},
+                {regex: /\$(?:\d+|\w+)/, onMatch: TabstopToken},
+                {regex: /\$\{[\dA-Z_a-z]+/, onMatch: function(str, state, stack) {
+                    var t = TabstopToken(str.substr(1), state, stack);
+                    stack.unshift(t[0]);
+                    return t;
+                }, next: "snippetVar"},
+                {regex: /\n/, token: "newline", merge: false}
+            ],
+            snippetVar: [
+                {regex: "\\|" + escape("\\|") + "*\\|", onMatch: function(val, state, stack) {
+                    stack[0].choices = val.slice(1, -1).split(",");
+                }, next: "start"},
+                {regex: "/(" + escape("/") + "+)/(?:(" + escape("/") + "*)/)(\\w*):?",
+                 onMatch: function(val, state, stack) {
+                    var ts = stack[0];
+                    ts.fmtString = val;
+
+                    val = this.splitRegex.exec(val);
+                    ts.guard = val[1];
+                    ts.fmt = val[2];
+                    ts.flag = val[3];
+                    return "";
+                }, next: "start"},
+                {regex: "`" + escape("`") + "*`", onMatch: function(val, state, stack) {
+                    stack[0].code = val.splice(1, -1);
+                    return "";
+                }, next: "start"},
+                {regex: "\\?", onMatch: function(val, state, stack) {
+                    if (stack[0])
+                        stack[0].expectIf = true;
+                }, next: "start"},
+                {regex: "([^:}\\\\]|\\\\.)*:?", token: "", next: "start"}
+            ],
+            formatString: [
+                {regex: "/(" + escape("/") + "+)/", token: "regex"},
+                {regex: "", onMatch: function(val, state, stack) {
+                    stack.inFormatString = true;
+                }, next: "start"}
+            ]
+        });
+        SnippetManager.prototype.getTokenizer = function() {
+            return SnippetManager.$tokenizer;
+        };
+        return SnippetManager.$tokenizer;
+    };
+
+    this.tokenizeTmSnippet = function(str, startState) {
+        return this.getTokenizer().getLineTokens(str, startState).tokens.map(function(x) {
+            return x.value || x;
+        });
+    };
+
+    this.$getDefaultValue = function(editor, name) {
+        if (/^[A-Z]\d+$/.test(name)) {
+            var i = name.substr(1);
+            return (this.variables[name[0] + "__"] || {})[i];
+        }
+        if (/^\d+$/.test(name)) {
+            return (this.variables.__ || {})[name];
+        }
+        name = name.replace(/^TM_/, "");
+
+        if (!editor)
+            return;
+        var s = editor.session;
+        switch(name) {
+            case "CURRENT_WORD":
+                var r = s.getWordRange();
+            case "SELECTION":
+            case "SELECTED_TEXT":
+                return s.getTextRange(r);
+            case "CURRENT_LINE":
+                return s.getLine(editor.getCursorPosition().row);
+            case "PREV_LINE": // not possible in textmate
+                return s.getLine(editor.getCursorPosition().row - 1);
+            case "LINE_INDEX":
+                return editor.getCursorPosition().column;
+            case "LINE_NUMBER":
+                return editor.getCursorPosition().row + 1;
+            case "SOFT_TABS":
+                return s.getUseSoftTabs() ? "YES" : "NO";
+            case "TAB_SIZE":
+                return s.getTabSize();
+            case "FILENAME":
+            case "FILEPATH":
+                return "";
+            case "FULLNAME":
+                return "Ace";
+        }
+    };
+    this.variables = {};
+    this.getVariableValue = function(editor, varName) {
+        if (this.variables.hasOwnProperty(varName))
+            return this.variables[varName](editor, varName) || "";
+        return this.$getDefaultValue(editor, varName) || "";
+    };
+    this.tmStrFormat = function(str, ch, editor) {
+        var flag = ch.flag || "";
+        var re = ch.guard;
+        re = new RegExp(re, flag.replace(/[^gi]/, ""));
+        var fmtTokens = this.tokenizeTmSnippet(ch.fmt, "formatString");
+        var _self = this;
+        var formatted = str.replace(re, function() {
+            _self.variables.__ = arguments;
+            var fmtParts = _self.resolveVariables(fmtTokens, editor);
+            var gChangeCase = "E";
+            for (var i  = 0; i < fmtParts.length; i++) {
+                var ch = fmtParts[i];
+                if (typeof ch == "object") {
+                    fmtParts[i] = "";
+                    if (ch.changeCase && ch.local) {
+                        var next = fmtParts[i + 1];
+                        if (next && typeof next == "string") {
+                            if (ch.changeCase == "u")
+                                fmtParts[i] = next[0].toUpperCase();
+                            else
+                                fmtParts[i] = next[0].toLowerCase();
+                            fmtParts[i + 1] = next.substr(1);
+                        }
+                    } else if (ch.changeCase) {
+                        gChangeCase = ch.changeCase;
+                    }
+                } else if (gChangeCase == "U") {
+                    fmtParts[i] = ch.toUpperCase();
+                } else if (gChangeCase == "L") {
+                    fmtParts[i] = ch.toLowerCase();
+                }
+            }
+            return fmtParts.join("");
+        });
+        this.variables.__ = null;
+        return formatted;
+    };
+
+    this.resolveVariables = function(snippet, editor) {
+        var result = [];
+        for (var i = 0; i < snippet.length; i++) {
+            var ch = snippet[i];
+            if (typeof ch == "string") {
+                result.push(ch);
+            } else if (typeof ch != "object") {
+                continue;
+            } else if (ch.skip) {
+                gotoNext(ch);
+            } else if (ch.processed < i) {
+                continue;
+            } else if (ch.text) {
+                var value = this.getVariableValue(editor, ch.text);
+                if (value && ch.fmtString)
+                    value = this.tmStrFormat(value, ch);
+                ch.processed = i;
+                if (ch.expectIf == null) {
+                    if (value) {
+                        result.push(value);
+                        gotoNext(ch);
+                    }
+                } else {
+                    if (value) {
+                        ch.skip = ch.elseBranch;
+                    } else
+                        gotoNext(ch);
+                }
+            } else if (ch.tabstopId != null) {
+                result.push(ch);
+            } else if (ch.changeCase != null) {
+                result.push(ch);
+            }
+        }
+        function gotoNext(ch) {
+            var i1 = snippet.indexOf(ch, i + 1);
+            if (i1 != -1)
+                i = i1;
+        }
+        return result;
+    };
+
+    this.insertSnippetForSelection = function(editor, snippetText) {
+        var cursor = editor.getCursorPosition();
+        var line = editor.session.getLine(cursor.row);
+        var tabString = editor.session.getTabString();
+        var indentString = line.match(/^\s*/)[0];
+        
+        if (cursor.column < indentString.length)
+            indentString = indentString.slice(0, cursor.column);
+
+        var tokens = this.tokenizeTmSnippet(snippetText);
+        tokens = this.resolveVariables(tokens, editor);
+        tokens = tokens.map(function(x) {
+            if (x == "\n")
+                return x + indentString;
+            if (typeof x == "string")
+                return x.replace(/\t/g, tabString);
+            return x;
+        });
+        var tabstops = [];
+        tokens.forEach(function(p, i) {
+            if (typeof p != "object")
+                return;
+            var id = p.tabstopId;
+            var ts = tabstops[id];
+            if (!ts) {
+                ts = tabstops[id] = [];
+                ts.index = id;
+                ts.value = "";
+            }
+            if (ts.indexOf(p) !== -1)
+                return;
+            ts.push(p);
+            var i1 = tokens.indexOf(p, i + 1);
+            if (i1 === -1)
+                return;
+
+            var value = tokens.slice(i + 1, i1);
+            var isNested = value.some(function(t) {return typeof t === "object"});          
+            if (isNested && !ts.value) {
+                ts.value = value;
+            } else if (value.length && (!ts.value || typeof ts.value !== "string")) {
+                ts.value = value.join("");
+            }
+        });
+        tabstops.forEach(function(ts) {ts.length = 0});
+        var expanding = {};
+        function copyValue(val) {
+            var copy = [];
+            for (var i = 0; i < val.length; i++) {
+                var p = val[i];
+                if (typeof p == "object") {
+                    if (expanding[p.tabstopId])
+                        continue;
+                    var j = val.lastIndexOf(p, i - 1);
+                    p = copy[j] || {tabstopId: p.tabstopId};
+                }
+                copy[i] = p;
+            }
+            return copy;
+        }
+        for (var i = 0; i < tokens.length; i++) {
+            var p = tokens[i];
+            if (typeof p != "object")
+                continue;
+            var id = p.tabstopId;
+            var i1 = tokens.indexOf(p, i + 1);
+            if (expanding[id]) {
+                if (expanding[id] === p)
+                    expanding[id] = null;
+                continue;
+            }
+            
+            var ts = tabstops[id];
+            var arg = typeof ts.value == "string" ? [ts.value] : copyValue(ts.value);
+            arg.unshift(i + 1, Math.max(0, i1 - i));
+            arg.push(p);
+            expanding[id] = p;
+            tokens.splice.apply(tokens, arg);
+
+            if (ts.indexOf(p) === -1)
+                ts.push(p);
+        }
+        var row = 0, column = 0;
+        var text = "";
+        tokens.forEach(function(t) {
+            if (typeof t === "string") {
+                if (t[0] === "\n"){
+                    column = t.length - 1;
+                    row ++;
+                } else
+                    column += t.length;
+                text += t;
+            } else {
+                if (!t.start)
+                    t.start = {row: row, column: column};
+                else
+                    t.end = {row: row, column: column};
+            }
+        });
+        var range = editor.getSelectionRange();
+        var end = editor.session.replace(range, text);
+
+        var tabstopManager = new TabstopManager(editor);
+        var selectionId = editor.inVirtualSelectionMode && editor.selection.index;
+        tabstopManager.addTabstops(tabstops, range.start, end, selectionId);
+    };
+    
+    this.insertSnippet = function(editor, snippetText) {
+        var self = this;
+        if (editor.inVirtualSelectionMode)
+            return self.insertSnippetForSelection(editor, snippetText);
+        
+        editor.forEachSelection(function() {
+            self.insertSnippetForSelection(editor, snippetText);
+        }, null, {keepOrder: true});
+        
+        if (editor.tabstopManager)
+            editor.tabstopManager.tabNext();
+    };
+
+    this.$getScope = function(editor) {
+        var scope = editor.session.$mode.$id || "";
+        scope = scope.split("/").pop();
+        if (scope === "html" || scope === "php") {
+            if (scope === "php" && !editor.session.$mode.inlinePhp) 
+                scope = "html";
+            var c = editor.getCursorPosition();
+            var state = editor.session.getState(c.row);
+            if (typeof state === "object") {
+                state = state[0];
+            }
+            if (state.substring) {
+                if (state.substring(0, 3) == "js-")
+                    scope = "javascript";
+                else if (state.substring(0, 4) == "css-")
+                    scope = "css";
+                else if (state.substring(0, 4) == "php-")
+                    scope = "php";
+            }
+        }
+        
+        return scope;
+    };
+
+    this.getActiveScopes = function(editor) {
+        var scope = this.$getScope(editor);
+        var scopes = [scope];
+        var snippetMap = this.snippetMap;
+        if (snippetMap[scope] && snippetMap[scope].includeScopes) {
+            scopes.push.apply(scopes, snippetMap[scope].includeScopes);
+        }
+        scopes.push("_");
+        return scopes;
+    };
+
+    this.expandWithTab = function(editor, options) {
+        var self = this;
+        var result = editor.forEachSelection(function() {
+            return self.expandSnippetForSelection(editor, options);
+        }, null, {keepOrder: true});
+        if (result && editor.tabstopManager)
+            editor.tabstopManager.tabNext();
+        return result;
+    };
+    
+    this.expandSnippetForSelection = function(editor, options) {
+        var cursor = editor.getCursorPosition();
+        var line = editor.session.getLine(cursor.row);
+        var before = line.substring(0, cursor.column);
+        var after = line.substr(cursor.column);
+
+        var snippetMap = this.snippetMap;
+        var snippet;
+        this.getActiveScopes(editor).some(function(scope) {
+            var snippets = snippetMap[scope];
+            if (snippets)
+                snippet = this.findMatchingSnippet(snippets, before, after);
+            return !!snippet;
+        }, this);
+        if (!snippet)
+            return false;
+        if (options && options.dryRun)
+            return true;
+        editor.session.doc.removeInLine(cursor.row,
+            cursor.column - snippet.replaceBefore.length,
+            cursor.column + snippet.replaceAfter.length
+        );
+
+        this.variables.M__ = snippet.matchBefore;
+        this.variables.T__ = snippet.matchAfter;
+        this.insertSnippetForSelection(editor, snippet.content);
+
+        this.variables.M__ = this.variables.T__ = null;
+        return true;
+    };
+
+    this.findMatchingSnippet = function(snippetList, before, after) {
+        for (var i = snippetList.length; i--;) {
+            var s = snippetList[i];
+            if (s.startRe && !s.startRe.test(before))
+                continue;
+            if (s.endRe && !s.endRe.test(after))
+                continue;
+            if (!s.startRe && !s.endRe)
+                continue;
+
+            s.matchBefore = s.startRe ? s.startRe.exec(before) : [""];
+            s.matchAfter = s.endRe ? s.endRe.exec(after) : [""];
+            s.replaceBefore = s.triggerRe ? s.triggerRe.exec(before)[0] : "";
+            s.replaceAfter = s.endTriggerRe ? s.endTriggerRe.exec(after)[0] : "";
+            return s;
+        }
+    };
+
+    this.snippetMap = {};
+    this.snippetNameMap = {};
+    this.register = function(snippets, scope) {
+        var snippetMap = this.snippetMap;
+        var snippetNameMap = this.snippetNameMap;
+        var self = this;
+        function wrapRegexp(src) {
+            if (src && !/^\^?\(.*\)\$?$|^\\b$/.test(src))
+                src = "(?:" + src + ")";
+
+            return src || "";
+        }
+        function guardedRegexp(re, guard, opening) {
+            re = wrapRegexp(re);
+            guard = wrapRegexp(guard);
+            if (opening) {
+                re = guard + re;
+                if (re && re[re.length - 1] != "$")
+                    re = re + "$";
+            } else {
+                re = re + guard;
+                if (re && re[0] != "^")
+                    re = "^" + re;
+            }
+            return new RegExp(re);
+        }
+
+        function addSnippet(s) {
+            if (!s.scope)
+                s.scope = scope || "_";
+            scope = s.scope;
+            if (!snippetMap[scope]) {
+                snippetMap[scope] = [];
+                snippetNameMap[scope] = {};
+            }
+
+            var map = snippetNameMap[scope];
+            if (s.name) {
+                var old = map[s.name];
+                if (old)
+                    self.unregister(old);
+                map[s.name] = s;
+            }
+            snippetMap[scope].push(s);
+
+            if (s.tabTrigger && !s.trigger) {
+                if (!s.guard && /^\w/.test(s.tabTrigger))
+                    s.guard = "\\b";
+                s.trigger = lang.escapeRegExp(s.tabTrigger);
+            }
+
+            s.startRe = guardedRegexp(s.trigger, s.guard, true);
+            s.triggerRe = new RegExp(s.trigger, "", true);
+
+            s.endRe = guardedRegexp(s.endTrigger, s.endGuard, true);
+            s.endTriggerRe = new RegExp(s.endTrigger, "", true);
+        }
+
+        if (snippets.content)
+            addSnippet(snippets);
+        else if (Array.isArray(snippets))
+            snippets.forEach(addSnippet);
+        
+        this._signal("registerSnippets", {scope: scope});
+    };
+    this.unregister = function(snippets, scope) {
+        var snippetMap = this.snippetMap;
+        var snippetNameMap = this.snippetNameMap;
+
+        function removeSnippet(s) {
+            var nameMap = snippetNameMap[s.scope||scope];
+            if (nameMap && nameMap[s.name]) {
+                delete nameMap[s.name];
+                var map = snippetMap[s.scope||scope];
+                var i = map && map.indexOf(s);
+                if (i >= 0)
+                    map.splice(i, 1);
+            }
+        }
+        if (snippets.content)
+            removeSnippet(snippets);
+        else if (Array.isArray(snippets))
+            snippets.forEach(removeSnippet);
+    };
+    this.parseSnippetFile = function(str) {
+        str = str.replace(/\r/g, "");
+        var list = [], snippet = {};
+        var re = /^#.*|^({[\s\S]*})\s*$|^(\S+) (.*)$|^((?:\n*\t.*)+)/gm;
+        var m;
+        while (m = re.exec(str)) {
+            if (m[1]) {
+                try {
+                    snippet = JSON.parse(m[1]);
+                    list.push(snippet);
+                } catch (e) {}
+            } if (m[4]) {
+                snippet.content = m[4].replace(/^\t/gm, "");
+                list.push(snippet);
+                snippet = {};
+            } else {
+                var key = m[2], val = m[3];
+                if (key == "regex") {
+                    var guardRe = /\/((?:[^\/\\]|\\.)*)|$/g;
+                    snippet.guard = guardRe.exec(val)[1];
+                    snippet.trigger = guardRe.exec(val)[1];
+                    snippet.endTrigger = guardRe.exec(val)[1];
+                    snippet.endGuard = guardRe.exec(val)[1];
+                } else if (key == "snippet") {
+                    snippet.tabTrigger = val.match(/^\S*/)[0];
+                    if (!snippet.name)
+                        snippet.name = val;
+                } else {
+                    snippet[key] = val;
+                }
+            }
+        }
+        return list;
+    };
+    this.getSnippetByName = function(name, editor) {
+        var snippetMap = this.snippetNameMap;
+        var snippet;
+        this.getActiveScopes(editor).some(function(scope) {
+            var snippets = snippetMap[scope];
+            if (snippets)
+                snippet = snippets[name];
+            return !!snippet;
+        }, this);
+        return snippet;
+    };
+
+}).call(SnippetManager.prototype);
+
+
+var TabstopManager = function(editor) {
+    if (editor.tabstopManager)
+        return editor.tabstopManager;
+    editor.tabstopManager = this;
+    this.$onChange = this.onChange.bind(this);
+    this.$onChangeSelection = lang.delayedCall(this.onChangeSelection.bind(this)).schedule;
+    this.$onChangeSession = this.onChangeSession.bind(this);
+    this.$onAfterExec = this.onAfterExec.bind(this);
+    this.attach(editor);
+};
+(function() {
+    this.attach = function(editor) {
+        this.index = 0;
+        this.ranges = [];
+        this.tabstops = [];
+        this.$openTabstops = null;
+        this.selectedTabstop = null;
+
+        this.editor = editor;
+        this.editor.on("change", this.$onChange);
+        this.editor.on("changeSelection", this.$onChangeSelection);
+        this.editor.on("changeSession", this.$onChangeSession);
+        this.editor.commands.on("afterExec", this.$onAfterExec);
+        this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
+    };
+    this.detach = function() {
+        this.tabstops.forEach(this.removeTabstopMarkers, this);
+        this.ranges = null;
+        this.tabstops = null;
+        this.selectedTabstop = null;
+        this.editor.removeListener("change", this.$onChange);
+        this.editor.removeListener("changeSelection", this.$onChangeSelection);
+        this.editor.removeListener("changeSession", this.$onChangeSession);
+        this.editor.commands.removeListener("afterExec", this.$onAfterExec);
+        this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
+        this.editor.tabstopManager = null;
+        this.editor = null;
+    };
+
+    this.onChange = function(e) {
+        var changeRange = e.data.range;
+        var isRemove = e.data.action[0] == "r";
+        var start = changeRange.start;
+        var end = changeRange.end;
+        var startRow = start.row;
+        var endRow = end.row;
+        var lineDif = endRow - startRow;
+        var colDiff = end.column - start.column;
+
+        if (isRemove) {
+            lineDif = -lineDif;
+            colDiff = -colDiff;
+        }
+        if (!this.$inChange && isRemove) {
+            var ts = this.selectedTabstop;
+            var changedOutside = ts && !ts.some(function(r) {
+                return comparePoints(r.start, start) <= 0 && comparePoints(r.end, end) >= 0;
+            });
+            if (changedOutside)
+                return this.detach();
+        }
+        var ranges = this.ranges;
+        for (var i = 0; i < ranges.length; i++) {
+            var r = ranges[i];
+            if (r.end.row < start.row)
+                continue;
+
+            if (isRemove && comparePoints(start, r.start) < 0 && comparePoints(end, r.end) > 0) {
+                this.removeRange(r);
+                i--;
+                continue;
+            }
+
+            if (r.start.row == startRow && r.start.column > start.column)
+                r.start.column += colDiff;
+            if (r.end.row == startRow && r.end.column >= start.column)
+                r.end.column += colDiff;
+            if (r.start.row >= startRow)
+                r.start.row += lineDif;
+            if (r.end.row >= startRow)
+                r.end.row += lineDif;
+
+            if (comparePoints(r.start, r.end) > 0)
+                this.removeRange(r);
+        }
+        if (!ranges.length)
+            this.detach();
+    };
+    this.updateLinkedFields = function() {
+        var ts = this.selectedTabstop;
+        if (!ts || !ts.hasLinkedRanges)
+            return;
+        this.$inChange = true;
+        var session = this.editor.session;
+        var text = session.getTextRange(ts.firstNonLinked);
+        for (var i = ts.length; i--;) {
+            var range = ts[i];
+            if (!range.linked)
+                continue;
+            var fmt = exports.snippetManager.tmStrFormat(text, range.original);
+            session.replace(range, fmt);
+        }
+        this.$inChange = false;
+    };
+    this.onAfterExec = function(e) {
+        if (e.command && !e.command.readOnly)
+            this.updateLinkedFields();
+    };
+    this.onChangeSelection = function() {
+        if (!this.editor)
+            return;
+        var lead = this.editor.selection.lead;
+        var anchor = this.editor.selection.anchor;
+        var isEmpty = this.editor.selection.isEmpty();
+        for (var i = this.ranges.length; i--;) {
+            if (this.ranges[i].linked)
+                continue;
+            var containsLead = this.ranges[i].contains(lead.row, lead.column);
+            var containsAnchor = isEmpty || this.ranges[i].contains(anchor.row, anchor.column);
+            if (containsLead && containsAnchor)
+                return;
+        }
+        this.detach();
+    };
+    this.onChangeSession = function() {
+        this.detach();
+    };
+    this.tabNext = function(dir) {
+        var max = this.tabstops.length;
+        var index = this.index + (dir || 1);
+        index = Math.min(Math.max(index, 1), max);
+        if (index == max)
+            index = 0;
+        this.selectTabstop(index);
+        if (index === 0)
+            this.detach();
+    };
+    this.selectTabstop = function(index) {
+        this.$openTabstops = null;
+        var ts = this.tabstops[this.index];
+        if (ts)
+            this.addTabstopMarkers(ts);
+        this.index = index;
+        ts = this.tabstops[this.index];
+        if (!ts || !ts.length)
+            return;
+        
+        this.selectedTabstop = ts;
+        if (!this.editor.inVirtualSelectionMode) {        
+            var sel = this.editor.multiSelect;
+            sel.toSingleRange(ts.firstNonLinked.clone());
+            for (var i = ts.length; i--;) {
+                if (ts.hasLinkedRanges && ts[i].linked)
+                    continue;
+                sel.addRange(ts[i].clone(), true);
+            }
+            if (sel.ranges[0])
+                sel.addRange(sel.ranges[0].clone());
+        } else {
+            this.editor.selection.setRange(ts.firstNonLinked);
+        }
+        
+        this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
+    };
+    this.addTabstops = function(tabstops, start, end) {
+        if (!this.$openTabstops)
+            this.$openTabstops = [];
+        if (!tabstops[0]) {
+            var p = Range.fromPoints(end, end);
+            moveRelative(p.start, start);
+            moveRelative(p.end, start);
+            tabstops[0] = [p];
+            tabstops[0].index = 0;
+        }
+
+        var i = this.index;
+        var arg = [i + 1, 0];
+        var ranges = this.ranges;
+        tabstops.forEach(function(ts, index) {
+            var dest = this.$openTabstops[index] || ts;
+                
+            for (var i = ts.length; i--;) {
+                var p = ts[i];
+                var range = Range.fromPoints(p.start, p.end || p.start);
+                movePoint(range.start, start);
+                movePoint(range.end, start);
+                range.original = p;
+                range.tabstop = dest;
+                ranges.push(range);
+                if (dest != ts)
+                    dest.unshift(range);
+                else
+                    dest[i] = range;
+                if (p.fmtString) {
+                    range.linked = true;
+                    dest.hasLinkedRanges = true;
+                } else if (!dest.firstNonLinked)
+                    dest.firstNonLinked = range;
+            }
+            if (!dest.firstNonLinked)
+                dest.hasLinkedRanges = false;
+            if (dest === ts) {
+                arg.push(dest);
+                this.$openTabstops[index] = dest;
+            }
+            this.addTabstopMarkers(dest);
+        }, this);
+        
+        if (arg.length > 2) {
+            if (this.tabstops.length)
+                arg.push(arg.splice(2, 1)[0]);
+            this.tabstops.splice.apply(this.tabstops, arg);
+        }
+    };
+
+    this.addTabstopMarkers = function(ts) {
+        var session = this.editor.session;
+        ts.forEach(function(range) {
+            if  (!range.markerId)
+                range.markerId = session.addMarker(range, "ace_snippet-marker", "text");
+        });
+    };
+    this.removeTabstopMarkers = function(ts) {
+        var session = this.editor.session;
+        ts.forEach(function(range) {
+            session.removeMarker(range.markerId);
+            range.markerId = null;
+        });
+    };
+    this.removeRange = function(range) {
+        var i = range.tabstop.indexOf(range);
+        range.tabstop.splice(i, 1);
+        i = this.ranges.indexOf(range);
+        this.ranges.splice(i, 1);
+        this.editor.session.removeMarker(range.markerId);
+        if (!range.tabstop.length) {
+            i = this.tabstops.indexOf(range.tabstop);
+            if (i != -1)
+                this.tabstops.splice(i, 1);
+            if (!this.tabstops.length)
+                this.detach();
+        }
+    };
+
+    this.keyboardHandler = new HashHandler();
+    this.keyboardHandler.bindKeys({
+        "Tab": function(ed) {
+            if (exports.snippetManager && exports.snippetManager.expandWithTab(ed)) {
+                return;
+            }
+
+            ed.tabstopManager.tabNext(1);
+        },
+        "Shift-Tab": function(ed) {
+            ed.tabstopManager.tabNext(-1);
+        },
+        "Esc": function(ed) {
+            ed.tabstopManager.detach();
+        },
+        "Return": function(ed) {
+            return false;
+        }
+    });
+}).call(TabstopManager.prototype);
+
+
+
+var changeTracker = {};
+changeTracker.onChange = Anchor.prototype.onChange;
+changeTracker.setPosition = function(row, column) {
+    this.pos.row = row;
+    this.pos.column = column;
+};
+changeTracker.update = function(pos, delta, $insertRight) {
+    this.$insertRight = $insertRight;
+    this.pos = pos; 
+    this.onChange(delta);
+};
+
+var movePoint = function(point, diff) {
+    if (point.row == 0)
+        point.column += diff.column;
+    point.row += diff.row;
+};
+
+var moveRelative = function(point, start) {
+    if (point.row == start.row)
+        point.column -= start.column;
+    point.row -= start.row;
+};
+
+
+require("./lib/dom").importCssString("\
+.ace_snippet-marker {\
+    -moz-box-sizing: border-box;\
+    box-sizing: border-box;\
+    background: rgba(194, 193, 208, 0.09);\
+    border: 1px dotted rgba(211, 208, 235, 0.62);\
+    position: absolute;\
+}");
+
+exports.snippetManager = new SnippetManager();
+
+
+var Editor = require("./editor").Editor;
+(function() {
+    this.insertSnippet = function(content, options) {
+        return exports.snippetManager.insertSnippet(this, content, options);
+    };
+    this.expandSnippet = function(options) {
+        return exports.snippetManager.expandWithTab(this, options);
+    };
+}).call(Editor.prototype);
+
+});
+
+define("ace/autocomplete/popup",["require","exports","module","ace/edit_session","ace/virtual_renderer","ace/editor","ace/range","ace/lib/event","ace/lib/lang","ace/lib/dom"], function(require, exports, module) {
+"use strict";
+
+var EditSession = require("../edit_session").EditSession;
+var Renderer = require("../virtual_renderer").VirtualRenderer;
+var Editor = require("../editor").Editor;
+var Range = require("../range").Range;
+var event = require("../lib/event");
+var lang = require("../lib/lang");
+var dom = require("../lib/dom");
+
+var $singleLineEditor = function(el) {
+    var renderer = new Renderer(el);
+
+    renderer.$maxLines = 4;
+
+    var editor = new Editor(renderer);
+
+    editor.setHighlightActiveLine(false);
+    editor.setShowPrintMargin(false);
+    editor.renderer.setShowGutter(false);
+    editor.renderer.setHighlightGutterLine(false);
+
+    editor.$mouseHandler.$focusWaitTimout = 0;
+
+    return editor;
+};
+
+var AcePopup = function(parentNode) {
+    var el = dom.createElement("div");
+    var popup = new $singleLineEditor(el);
+
+    if (parentNode)
+        parentNode.appendChild(el);
+    el.style.display = "none";
+    popup.renderer.content.style.cursor = "default";
+    popup.renderer.setStyle("ace_autocomplete");
+
+    popup.setOption("displayIndentGuides", false);
+    popup.setOption("dragDelay", 150);
+
+    var noop = function(){};
+
+    popup.focus = noop;
+    popup.$isFocused = true;
+
+    popup.renderer.$cursorLayer.restartTimer = noop;
+    popup.renderer.$cursorLayer.element.style.opacity = 0;
+
+    popup.renderer.$maxLines = 8;
+    popup.renderer.$keepTextAreaAtCursor = false;
+
+    popup.setHighlightActiveLine(false);
+    popup.session.highlight("");
+    popup.session.$searchHighlight.clazz = "ace_highlight-marker";
+
+    popup.on("mousedown", function(e) {
+        var pos = e.getDocumentPosition();
+        popup.selection.moveToPosition(pos);
+        selectionMarker.start.row = selectionMarker.end.row = pos.row;
+        e.stop();
+    });
+
+    var lastMouseEvent;
+    var hoverMarker = new Range(-1,0,-1,Infinity);
+    var selectionMarker = new Range(-1,0,-1,Infinity);
+    selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine");
+    popup.setSelectOnHover = function(val) {
+        if (!val) {
+            hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine");
+        } else if (hoverMarker.id) {
+            popup.session.removeMarker(hoverMarker.id);
+            hoverMarker.id = null;
+        }
+    }
+    popup.setSelectOnHover(false);
+    popup.on("mousemove", function(e) {
+        if (!lastMouseEvent) {
+            lastMouseEvent = e;
+            return;
+        }
+        if (lastMouseEvent.x == e.x && lastMouseEvent.y == e.y) {
+            return;
+        }
+        lastMouseEvent = e;
+        lastMouseEvent.scrollTop = popup.renderer.scrollTop;
+        var row = lastMouseEvent.getDocumentPosition().row;
+        if (hoverMarker.start.row != row) {
+            if (!hoverMarker.id)
+                popup.setRow(row);
+            setHoverMarker(row);
+        }
+    });
+    popup.renderer.on("beforeRender", function() {
+        if (lastMouseEvent && hoverMarker.start.row != -1) {
+            lastMouseEvent.$pos = null;
+            var row = lastMouseEvent.getDocumentPosition().row;
+            if (!hoverMarker.id)
+                popup.setRow(row);
+            setHoverMarker(row, true);
+        }
+    });
+    popup.renderer.on("afterRender", function() {
+        var row = popup.getRow();
+        var t = popup.renderer.$textLayer;
+        var selected = t.element.childNodes[row - t.config.firstRow];
+        if (selected == t.selectedNode)
+            return;
+        if (t.selectedNode)
+            dom.removeCssClass(t.selectedNode, "ace_selected");
+        t.selectedNode = selected;
+        if (selected)
+            dom.addCssClass(selected, "ace_selected");
+    });
+    var hideHoverMarker = function() { setHoverMarker(-1) };
+    var setHoverMarker = function(row, suppressRedraw) {
+        if (row !== hoverMarker.start.row) {
+            hoverMarker.start.row = hoverMarker.end.row = row;
+            if (!suppressRedraw)
+                popup.session._emit("changeBackMarker");
+            popup._emit("changeHoverMarker");
+        }
+    };
+    popup.getHoveredRow = function() {
+        return hoverMarker.start.row;
+    };
+
+    event.addListener(popup.container, "mouseout", hideHoverMarker);
+    popup.on("hide", hideHoverMarker);
+    popup.on("changeSelection", hideHoverMarker);
+
+    popup.session.doc.getLength = function() {
+        return popup.data.length;
+    };
+    popup.session.doc.getLine = function(i) {
+        var data = popup.data[i];
+        if (typeof data == "string")
+            return data;
+        return (data && data.value) || "";
+    };
+
+    var bgTokenizer = popup.session.bgTokenizer;
+    bgTokenizer.$tokenizeRow = function(i) {
+        var data = popup.data[i];
+        var tokens = [];
+        if (!data)
+            return tokens;
+        if (typeof data == "string")
+            data = {value: data};
+        if (!data.caption)
+            data.caption = data.value || data.name;
+
+        var last = -1;
+        var flag, c;
+        for (var i = 0; i < data.caption.length; i++) {
+            c = data.caption[i];
+            flag = data.matchMask & (1 << i) ? 1 : 0;
+            if (last !== flag) {
+                tokens.push({type: data.className || "" + ( flag ? "completion-highlight" : ""), value: c});
+                last = flag;
+            } else {
+                tokens[tokens.length - 1].value += c;
+            }
+        }
+
+        if (data.meta) {
+            var maxW = popup.renderer.$size.scrollerWidth / popup.renderer.layerConfig.characterWidth;
+            if (data.meta.length + data.caption.length < maxW - 2)
+                tokens.push({type: "rightAlignedText", value: data.meta});
+        }
+        return tokens;
+    };
+    bgTokenizer.$updateOnChange = noop;
+    bgTokenizer.start = noop;
+
+    popup.session.$computeWidth = function() {
+        return this.screenWidth = 0;
+    }
+    popup.isOpen = false;
+    popup.isTopdown = false;
+
+    popup.data = [];
+    popup.setData = function(list) {
+        popup.data = list || [];
+        popup.setValue(lang.stringRepeat("\n", list.length), -1);
+        popup.setRow(0);
+    };
+    popup.getData = function(row) {
+        return popup.data[row];
+    };
+
+    popup.getRow = function() {
+        return selectionMarker.start.row;
+    };
+    popup.setRow = function(line) {
+        line = Math.max(-1, Math.min(this.data.length, line));
+        if (selectionMarker.start.row != line) {
+            popup.selection.clearSelection();
+            selectionMarker.start.row = selectionMarker.end.row = line || 0;
+            popup.session._emit("changeBackMarker");
+            popup.moveCursorTo(line || 0, 0);
+            if (popup.isOpen)
+                popup._signal("select");
+        }
+    };
+
+    popup.on("changeSelection", function() {
+        if (popup.isOpen)
+            popup.setRow(popup.selection.lead.row);
+    });
+
+    popup.hide = function() {
+        this.container.style.display = "none";
+        this._signal("hide");
+        popup.isOpen = false;
+    };
+    popup.show = function(pos, lineHeight, topdownOnly) {
+        var el = this.container;
+        var screenHeight = window.innerHeight;
+        var screenWidth = window.innerWidth;
+        var renderer = this.renderer;
+        var maxH = renderer.$maxLines * lineHeight * 1.4;
+        var top = pos.top + this.$borderSize;
+        if (top + maxH > screenHeight - lineHeight && !topdownOnly) {
+            el.style.top = "";
+            el.style.bottom = screenHeight - top + "px";
+            popup.isTopdown = false;
+        } else {
+            top += lineHeight;
+            el.style.top = top + "px";
+            el.style.bottom = "";
+            popup.isTopdown = true;
+        }
+
+        el.style.display = "";
+        this.renderer.$textLayer.checkForSizeChanges();
+
+        var left = pos.left;
+        if (left + el.offsetWidth > screenWidth)
+            left = screenWidth - el.offsetWidth;
+
+        el.style.left = left + "px";
+
+        this._signal("show");
+        lastMouseEvent = null;
+        popup.isOpen = true;
+    };
+
+    popup.getTextLeftOffset = function() {
+        return this.$borderSize + this.renderer.$padding + this.$imageSize;
+    };
+
+    popup.$imageSize = 0;
+    popup.$borderSize = 1;
+
+    return popup;
+};
+
+dom.importCssString("\
+.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\
+    background-color: #CAD6FA;\
+    z-index: 1;\
+}\
+.ace_editor.ace_autocomplete .ace_line-hover {\
+    border: 1px solid #abbffe;\
+    margin-top: -1px;\
+    background: rgba(233,233,253,0.4);\
+}\
+.ace_editor.ace_autocomplete .ace_line-hover {\
+    position: absolute;\
+    z-index: 2;\
+}\
+.ace_editor.ace_autocomplete .ace_scroller {\
+   background: none;\
+   border: none;\
+   box-shadow: none;\
+}\
+.ace_rightAlignedText {\
+    color: gray;\
+    display: inline-block;\
+    position: absolute;\
+    right: 4px;\
+    text-align: right;\
+    z-index: -1;\
+}\
+.ace_editor.ace_autocomplete .ace_completion-highlight{\
+    color: #000;\
+    text-shadow: 0 0 0.01em;\
+}\
+.ace_editor.ace_autocomplete {\
+    width: 280px;\
+    z-index: 200000;\
+    background: #fbfbfb;\
+    color: #444;\
+    border: 1px lightgray solid;\
+    position: fixed;\
+    box-shadow: 2px 3px 5px rgba(0,0,0,.2);\
+    line-height: 1.4;\
+}");
+
+exports.AcePopup = AcePopup;
+
+});
+
+define("ace/autocomplete/util",["require","exports","module"], function(require, exports, module) {
+"use strict";
+
+exports.parForEach = function(array, fn, callback) {
+    var completed = 0;
+    var arLength = array.length;
+    if (arLength === 0)
+        callback();
+    for (var i = 0; i < arLength; i++) {
+        fn(array[i], function(result, err) {
+            completed++;
+            if (completed === arLength)
+                callback(result, err);
+        });
+    }
+};
+
+var ID_REGEX = /[a-zA-Z_0-9\$\-\u00A2-\uFFFF]/;
+
+exports.retrievePrecedingIdentifier = function(text, pos, regex) {
+    regex = regex || ID_REGEX;
+    var buf = [];
+    for (var i = pos-1; i >= 0; i--) {
+        if (regex.test(text[i]))
+            buf.push(text[i]);
+        else
+            break;
+    }
+    return buf.reverse().join("");
+};
+
+exports.retrieveFollowingIdentifier = function(text, pos, regex) {
+    regex = regex || ID_REGEX;
+    var buf = [];
+    for (var i = pos; i < text.length; i++) {
+        if (regex.test(text[i]))
+            buf.push(text[i]);
+        else
+            break;
+    }
+    return buf;
+};
+
+});
+
+define("ace/autocomplete",["require","exports","module","ace/keyboard/hash_handler","ace/autocomplete/popup","ace/autocomplete/util","ace/lib/event","ace/lib/lang","ace/snippets"], function(require, exports, module) {
+"use strict";
+
+var HashHandler = require("./keyboard/hash_handler").HashHandler;
+var AcePopup = require("./autocomplete/popup").AcePopup;
+var util = require("./autocomplete/util");
+var event = require("./lib/event");
+var lang = require("./lib/lang");
+var snippetManager = require("./snippets").snippetManager;
+
+var Autocomplete = function() {
+    this.autoInsert = true;
+    this.autoSelect = true;
+    this.keyboardHandler = new HashHandler();
+    this.keyboardHandler.bindKeys(this.commands);
+
+    this.blurListener = this.blurListener.bind(this);
+    this.changeListener = this.changeListener.bind(this);
+    this.mousedownListener = this.mousedownListener.bind(this);
+    this.mousewheelListener = this.mousewheelListener.bind(this);
+
+    this.changeTimer = lang.delayedCall(function() {
+        this.updateCompletions(true);
+    }.bind(this));
+};
+
+(function() {
+    this.gatherCompletionsId = 0;
+
+    this.$init = function() {
+        this.popup = new AcePopup(document.body || document.documentElement);
+        this.popup.on("click", function(e) {
+            this.insertMatch();
+            e.stop();
+        }.bind(this));
+        this.popup.focus = this.editor.focus.bind(this.editor);
+    };
+
+    this.openPopup = function(editor, prefix, keepPopupPosition) {
+        if (!this.popup)
+            this.$init();
+
+        this.popup.setData(this.completions.filtered);
+
+        var renderer = editor.renderer;
+        this.popup.setRow(this.autoSelect ? 0 : -1);
+        if (!keepPopupPosition) {
+            this.popup.setTheme(editor.getTheme());
+            this.popup.setFontSize(editor.getFontSize());
+
+            var lineHeight = renderer.layerConfig.lineHeight;
+
+            var pos = renderer.$cursorLayer.getPixelPosition(this.base, true);
+            pos.left -= this.popup.getTextLeftOffset();
+
+            var rect = editor.container.getBoundingClientRect();
+            pos.top += rect.top - renderer.layerConfig.offset;
+            pos.left += rect.left - editor.renderer.scrollLeft;
+            pos.left += renderer.$gutterLayer.gutterWidth;
+
+            this.popup.show(pos, lineHeight);
+        }
+    };
+
+    this.detach = function() {
+        this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
+        this.editor.off("changeSelection", this.changeListener);
+        this.editor.off("blur", this.blurListener);
+        this.editor.off("mousedown", this.mousedownListener);
+        this.editor.off("mousewheel", this.mousewheelListener);
+        this.changeTimer.cancel();
+
+        if (this.popup && this.popup.isOpen) {
+            this.gatherCompletionsId += 1;
+            this.popup.hide();
+        }
+        
+        if (this.base)
+            this.base.detach();
+        this.activated = false;
+        this.completions = this.base = null;
+    };
+
+    this.changeListener = function(e) {
+        var cursor = this.editor.selection.lead;
+        if (cursor.row != this.base.row || cursor.column < this.base.column) {
+            this.detach();
+        }
+        if (this.activated)
+            this.changeTimer.schedule();
+        else
+            this.detach();
+    };
+
+    this.blurListener = function() {
+        var el = document.activeElement;
+        if (el != this.editor.textInput.getElement() && el.parentNode != this.popup.container)
+            this.detach();
+    };
+
+    this.mousedownListener = function(e) {
+        this.detach();
+    };
+
+    this.mousewheelListener = function(e) {
+        this.detach();
+    };
+
+    this.goTo = function(where) {
+        var row = this.popup.getRow();
+        var max = this.popup.session.getLength() - 1;
+
+        switch(where) {
+            case "up": row = row <= 0 ? max : row - 1; break;
+            case "down": row = row >= max ? -1 : row + 1; break;
+            case "start": row = 0; break;
+            case "end": row = max; break;
+        }
+
+        this.popup.setRow(row);
+    };
+
+    this.insertMatch = function(data) {
+        if (!data)
+            data = this.popup.getData(this.popup.getRow());
+        if (!data)
+            return false;
+
+        if (data.completer && data.completer.insertMatch) {
+            data.completer.insertMatch(this.editor);
+        } else {
+            if (this.completions.filterText) {
+                var ranges = this.editor.selection.getAllRanges();
+                for (var i = 0, range; range = ranges[i]; i++) {
+                    range.start.column -= this.completions.filterText.length;
+                    this.editor.session.remove(range);
+                }
+            }
+            if (data.snippet)
+                snippetManager.insertSnippet(this.editor, data.snippet);
+            else
+                this.editor.execCommand("insertstring", data.value || data);
+        }
+        this.detach();
+    };
+
+    this.commands = {
+        "Up": function(editor) { editor.completer.goTo("up"); },
+        "Down": function(editor) { editor.completer.goTo("down"); },
+        "Ctrl-Up|Ctrl-Home": function(editor) { editor.completer.goTo("start"); },
+        "Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); },
+
+        "Esc": function(editor) { editor.completer.detach(); },
+        "Space": function(editor) { editor.completer.detach(); editor.insert(" ");},
+        "Return": function(editor) { return editor.completer.insertMatch(); },
+        "Shift-Return": function(editor) { editor.completer.insertMatch(true); },
+        "Tab": function(editor) {
+            var result = editor.completer.insertMatch();
+            if (!result && !editor.tabstopManager)
+                editor.completer.goTo("down");
+            else
+                return result;
+        },
+
+        "PageUp": function(editor) { editor.completer.popup.gotoPageUp(); },
+        "PageDown": function(editor) { editor.completer.popup.gotoPageDown(); }
+    };
+
+    this.gatherCompletions = function(editor, callback) {
+        var session = editor.getSession();
+        var pos = editor.getCursorPosition();
+
+        var line = session.getLine(pos.row);
+        var prefix = util.retrievePrecedingIdentifier(line, pos.column);
+
+        this.base = session.doc.createAnchor(pos.row, pos.column - prefix.length);
+
+        var matches = [];
+        var total = editor.completers.length;
+        editor.completers.forEach(function(completer, i) {
+            completer.getCompletions(editor, session, pos, prefix, function(err, results) {
+                if (!err)
+                    matches = matches.concat(results);
+                var pos = editor.getCursorPosition();
+                var line = session.getLine(pos.row);
+                callback(null, {
+                    prefix: util.retrievePrecedingIdentifier(line, pos.column, results[0] && results[0].identifierRegex),
+                    matches: matches,
+                    finished: (--total === 0)
+                });
+            });
+        });
+        return true;
+    };
+
+    this.showPopup = function(editor) {
+        if (this.editor)
+            this.detach();
+
+        this.activated = true;
+
+        this.editor = editor;
+        if (editor.completer != this) {
+            if (editor.completer)
+                editor.completer.detach();
+            editor.completer = this;
+        }
+
+        editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
+        editor.on("changeSelection", this.changeListener);
+        editor.on("blur", this.blurListener);
+        editor.on("mousedown", this.mousedownListener);
+        editor.on("mousewheel", this.mousewheelListener);
+
+        this.updateCompletions();
+    };
+
+    this.updateCompletions = function(keepPopupPosition) {
+        if (keepPopupPosition && this.base && this.completions) {
+            var pos = this.editor.getCursorPosition();
+            var prefix = this.editor.session.getTextRange({start: this.base, end: pos});
+            if (prefix == this.completions.filterText)
+                return;
+            this.completions.setFilter(prefix);
+            if (!this.completions.filtered.length)
+                return this.detach();
+            if (this.completions.filtered.length == 1
+            && this.completions.filtered[0].value == prefix
+            && !this.completions.filtered[0].snippet)
+                return this.detach();
+            this.openPopup(this.editor, prefix, keepPopupPosition);
+            return;
+        }
+        var _id = this.gatherCompletionsId;
+        this.gatherCompletions(this.editor, function(err, results) {
+            var detachIfFinished = function() {
+                if (!results.finished) return;
+                return this.detach();
+            }.bind(this);
+
+            var prefix = results.prefix;
+            var matches = results && results.matches;
+            
+            if (!matches || !matches.length)
+                return detachIfFinished();
+            if (prefix.indexOf(results.prefix) !== 0 || _id != this.gatherCompletionsId)
+                return;
+
+            this.completions = new FilteredList(matches);
+            this.completions.setFilter(prefix);
+            var filtered = this.completions.filtered;
+            if (!filtered.length)
+                return detachIfFinished();
+            if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet)
+                return detachIfFinished();
+            if (this.autoInsert && filtered.length == 1)
+                return this.insertMatch(filtered[0]);
+
+            this.openPopup(this.editor, prefix, keepPopupPosition);
+        }.bind(this));
+    };
+
+    this.cancelContextMenu = function() {
+        this.editor.$mouseHandler.cancelContextMenu();
+    };
+
+}).call(Autocomplete.prototype);
+
+Autocomplete.startCommand = {
+    name: "startAutocomplete",
+    exec: function(editor) {
+        if (!editor.completer)
+            editor.completer = new Autocomplete();
+        editor.completer.autoInsert = 
+        editor.completer.autoSelect = true;
+        editor.completer.showPopup(editor);
+        editor.completer.cancelContextMenu();
+    },
+    bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
+};
+
+var FilteredList = function(array, filterText, mutateData) {
+    this.all = array;
+    this.filtered = array;
+    this.filterText = filterText || "";
+};
+(function(){
+    this.setFilter = function(str) {
+        if (str.length > this.filterText && str.lastIndexOf(this.filterText, 0) === 0)
+            var matches = this.filtered;
+        else
+            var matches = this.all;
+
+        this.filterText = str;
+        matches = this.filterCompletions(matches, this.filterText);
+        matches = matches.sort(function(a, b) {
+            return b.exactMatch - a.exactMatch || b.score - a.score;
+        });
+        var prev = null;
+        matches = matches.filter(function(item){
+            var caption = item.value || item.caption || item.snippet;
+            if (caption === prev) return false;
+            prev = caption;
+            return true;
+        });
+
+        this.filtered = matches;
+    };
+    this.filterCompletions = function(items, needle) {
+        var results = [];
+        var upper = needle.toUpperCase();
+        var lower = needle.toLowerCase();
+        loop: for (var i = 0, item; item = items[i]; i++) {
+            var caption = item.value || item.caption || item.snippet;
+            if (!caption) continue;
+            var lastIndex = -1;
+            var matchMask = 0;
+            var penalty = 0;
+            var index, distance;
+            for (var j = 0; j < needle.length; j++) {
+                var i1 = caption.indexOf(lower[j], lastIndex + 1);
+                var i2 = caption.indexOf(upper[j], lastIndex + 1);
+                index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2;
+                if (index < 0)
+                    continue loop;
+                distance = index - lastIndex - 1;
+                if (distance > 0) {
+                    if (lastIndex === -1)
+                        penalty += 10;
+                    penalty += distance;
+                }
+                matchMask = matchMask | (1 << index);
+                lastIndex = index;
+            }
+            item.matchMask = matchMask;
+            item.exactMatch = penalty ? 0 : 1;
+            item.score = (item.score || 0) - penalty;
+            results.push(item);
+        }
+        return results;
+    };
+}).call(FilteredList.prototype);
+
+exports.Autocomplete = Autocomplete;
+exports.FilteredList = FilteredList;
+
+});
+
+define("ace/autocomplete/text_completer",["require","exports","module","ace/range"], function(require, exports, module) {
+    var Range = require("../range").Range;
+    
+    var splitRegex = /[^a-zA-Z_0-9\$\-\u00C0-\u1FFF\u2C00-\uD7FF\w]+/;
+
+    function getWordIndex(doc, pos) {
+        var textBefore = doc.getTextRange(Range.fromPoints({row: 0, column:0}, pos));
+        return textBefore.split(splitRegex).length - 1;
+    }
+    function wordDistance(doc, pos) {
+        var prefixPos = getWordIndex(doc, pos);
+        var words = doc.getValue().split(splitRegex);
+        var wordScores = Object.create(null);
+        
+        var currentWord = words[prefixPos];
+
+        words.forEach(function(word, idx) {
+            if (!word || word === currentWord) return;
+
+            var distance = Math.abs(prefixPos - idx);
+            var score = words.length - distance;
+            if (wordScores[word]) {
+                wordScores[word] = Math.max(score, wordScores[word]);
+            } else {
+                wordScores[word] = score;
+            }
+        });
+        return wordScores;
+    }
+
+    exports.getCompletions = function(editor, session, pos, prefix, callback) {
+        var wordScore = wordDistance(session, pos, prefix);
+        var wordList = Object.keys(wordScore);
+        callback(null, wordList.map(function(word) {
+            return {
+                caption: word,
+                value: word,
+                score: wordScore[word],
+                meta: "local"
+            };
+        }));
+    };
+});
+
+define("ace/ext/language_tools",["require","exports","module","ace/snippets","ace/autocomplete","ace/config","ace/autocomplete/util","ace/autocomplete/text_completer","ace/editor","ace/config"], function(require, exports, module) {
+"use strict";
+
+var snippetManager = require("../snippets").snippetManager;
+var Autocomplete = require("../autocomplete").Autocomplete;
+var config = require("../config");
+var util = require("../autocomplete/util");
+
+var textCompleter = require("../autocomplete/text_completer");
+var keyWordCompleter = {
+    getCompletions: function(editor, session, pos, prefix, callback) {
+        var state = editor.session.getState(pos.row);
+        var completions = session.$mode.getCompletions(state, session, pos, prefix);
+        callback(null, completions);
+    }
+};
+
+var snippetCompleter = {
+    getCompletions: function(editor, session, pos, prefix, callback) {
+        var snippetMap = snippetManager.snippetMap;
+        var completions = [];
+        snippetManager.getActiveScopes(editor).forEach(function(scope) {
+            var snippets = snippetMap[scope] || [];
+            for (var i = snippets.length; i--;) {
+                var s = snippets[i];
+                var caption = s.name || s.tabTrigger;
+                if (!caption)
+                    continue;
+                completions.push({
+                    caption: caption,
+                    snippet: s.content,
+                    meta: s.tabTrigger && !s.name ? s.tabTrigger + "\u21E5 " : "snippet"
+                });
+            }
+        }, this);
+        callback(null, completions);
+    }
+};
+
+var completers = [snippetCompleter, textCompleter, keyWordCompleter];
+exports.addCompleter = function(completer) {
+    completers.push(completer);
+};
+exports.textCompleter = textCompleter;
+exports.keyWordCompleter = keyWordCompleter;
+exports.snippetCompleter = snippetCompleter;
+
+var expandSnippet = {
+    name: "expandSnippet",
+    exec: function(editor) {
+        var success = snippetManager.expandWithTab(editor);
+        if (!success)
+            editor.execCommand("indent");
+    },
+    bindKey: "Tab"
+};
+
+var onChangeMode = function(e, editor) {
+    loadSnippetsForMode(editor.session.$mode);
+};
+
+var loadSnippetsForMode = function(mode) {
+    var id = mode.$id;
+    if (!snippetManager.files)
+        snippetManager.files = {};
+    loadSnippetFile(id);
+    if (mode.modes)
+        mode.modes.forEach(loadSnippetsForMode);
+};
+
+var loadSnippetFile = function(id) {
+    if (!id || snippetManager.files[id])
+        return;
+    var snippetFilePath = id.replace("mode", "snippets");
+    snippetManager.files[id] = {};
+    config.loadModule(snippetFilePath, function(m) {
+        if (m) {
+            snippetManager.files[id] = m;
+            if (!m.snippets && m.snippetText)
+                m.snippets = snippetManager.parseSnippetFile(m.snippetText);
+            snippetManager.register(m.snippets || [], m.scope);
+            if (m.includeScopes) {
+                snippetManager.snippetMap[m.scope].includeScopes = m.includeScopes;
+                m.includeScopes.forEach(function(x) {
+                    loadSnippetFile("ace/mode/" + x);
+                });
+            }
+        }
+    });
+};
+
+function getCompletionPrefix(editor) {
+    var pos = editor.getCursorPosition();
+    var line = editor.session.getLine(pos.row);
+    var prefix = util.retrievePrecedingIdentifier(line, pos.column);
+    editor.completers.forEach(function(completer) {
+        if (completer.identifierRegexps) {
+            completer.identifierRegexps.forEach(function(identifierRegex) {
+                if (!prefix && identifierRegex)
+                    prefix = util.retrievePrecedingIdentifier(line, pos.column, identifierRegex);
+            });
+        }
+    });
+    return prefix;
+}
+
+var doLiveAutocomplete = function(e) {
+    var editor = e.editor;
+    var text = e.args || "";
+    var hasCompleter = editor.completer && editor.completer.activated;
+    if (e.command.name === "backspace") {
+        if (hasCompleter && !getCompletionPrefix(editor))
+            editor.completer.detach();
+    }
+    else if (e.command.name === "insertstring") {
+        var prefix = getCompletionPrefix(editor);
+        if (prefix && !hasCompleter) {
+            if (!editor.completer) {
+                editor.completer = new Autocomplete();
+            }
+            editor.completer.autoSelect = false;
+            editor.completer.autoInsert = false;
+            editor.completer.showPopup(editor);
+        }
+    }
+};
+
+var Editor = require("../editor").Editor;
+require("../config").defineOptions(Editor.prototype, "editor", {
+    enableBasicAutocompletion: {
+        set: function(val) {
+            if (val) {
+                if (!this.completers)
+                    this.completers = Array.isArray(val)? val: completers;
+                this.commands.addCommand(Autocomplete.startCommand);
+            } else {
+                this.commands.removeCommand(Autocomplete.startCommand);
+            }
+        },
+        value: false
+    },
+    enableLiveAutocompletion: {
+        set: function(val) {
+            if (val) {
+                if (!this.completers)
+                    this.completers = Array.isArray(val)? val: completers;
+                this.commands.on('afterExec', doLiveAutocomplete);
+            } else {
+                this.commands.removeListener('afterExec', doLiveAutocomplete);
+            }
+        },
+        value: false
+    },
+    enableSnippets: {
+        set: function(val) {
+            if (val) {
+                this.commands.addCommand(expandSnippet);
+                this.on("changeMode", onChangeMode);
+                onChangeMode(null, this);
+            } else {
+                this.commands.removeCommand(expandSnippet);
+                this.off("changeMode", onChangeMode);
+            }
+        },
+        value: false
+    }
+});
+});
+;
+                (function() {
+                    window.require(["ace/ext/language_tools"], function() {});
+                })();
+            

+ 53 - 0
services/web/public/js/ace/ext-linking.js

@@ -0,0 +1,53 @@
+define("ace/ext/linking",["require","exports","module","ace/editor","ace/config"], function(require, exports, module) {
+
+var Editor = require("ace/editor").Editor;
+
+require("../config").defineOptions(Editor.prototype, "editor", {
+    enableLinking: {
+        set: function(val) {
+            if (val) {
+                this.on("click", onClick);
+                this.on("mousemove", onMouseMove);
+            } else {
+                this.off("click", onClick);
+                this.off("mousemove", onMouseMove);
+            }
+        },
+        value: false
+    }
+})
+
+function onMouseMove(e) {
+    var editor = e.editor;
+    var ctrl = e.getAccelKey();
+
+    if (ctrl) {
+        var editor = e.editor;
+        var docPos = e.getDocumentPosition();
+        var session = editor.session;
+        var token = session.getTokenAt(docPos.row, docPos.column);
+
+        editor._emit("linkHover", {position: docPos, token: token});
+    }
+}
+
+function onClick(e) {
+    var ctrl = e.getAccelKey();
+    var button = e.getButton();
+
+    if (button == 0 && ctrl) {
+        var editor = e.editor;
+        var docPos = e.getDocumentPosition();
+        var session = editor.session;
+        var token = session.getTokenAt(docPos.row, docPos.column);
+
+        editor._emit("linkClick", {position: docPos, token: token});
+    }
+}
+
+});
+;
+                (function() {
+                    window.require(["ace/ext/linking"], function() {});
+                })();
+            

+ 14 - 11
services/web/public/js/ace/ext/modelist.js → services/web/public/js/ace/ext-modelist.js

@@ -1,13 +1,7 @@
-define(function(require, exports, module) {
+define("ace/ext/modelist",["require","exports","module"], function(require, exports, module) {
 "use strict";
 
 var modes = [];
-/**
- * Suggests a mode based on the file extension present in the given path
- * @param {string} path The path to the file
- * @returns {object} Returns an object containing information about the
- *  suggested mode.
- */
 function getModeForPath(path) {
     var mode = modesByName.text;
     var fileName = path.split(/[\/\\]/).pop();
@@ -39,8 +33,6 @@ var Mode = function(name, caption, extensions) {
 Mode.prototype.supportsFile = function(filename) {
     return filename.match(this.extRe);
 };
-
-// todo firstlinematch
 var supportedModes = {
     ABAP:        ["abap"],
     ActionScript:["as"],
@@ -52,7 +44,8 @@ var supportedModes = {
     BatchFile:   ["bat|cmd"],
     C9Search:    ["c9search_results"],
     C_Cpp:       ["cpp|c|cc|cxx|h|hh|hpp"],
-    Clojure:     ["clj"],
+    Cirru:       ["cirru|cr"],
+    Clojure:     ["clj|cljs"],
     Cobol:       ["CBL|COB"],
     coffee:      ["coffee|cf|cson|^Cakefile"],
     ColdFusion:  ["cfm"],
@@ -62,11 +55,14 @@ var supportedModes = {
     D:           ["d|di"],
     Dart:        ["dart"],
     Diff:        ["diff|patch"],
+    Dockerfile:  ["^Dockerfile"],
     Dot:         ["dot"],
     Erlang:      ["erl|hrl"],
     EJS:         ["ejs"],
     Forth:       ["frt|fs|ldr"],
     FTL:         ["ftl"],
+    Gherkin:     ["feature"],
+    Gitignore:   ["^.gitignore"],
     Glsl:        ["glsl|frag|vert"],
     golang:      ["go"],
     Groovy:      ["groovy"],
@@ -99,6 +95,7 @@ var supportedModes = {
     Makefile:    ["^Makefile|^GNUmakefile|^makefile|^OCamlMakefile|make"],
     MATLAB:      ["matlab"],
     Markdown:    ["md|markdown"],
+    MEL:         ["mel"],
     MySQL:       ["mysql"],
     MUSHCode:    ["mc|mush"],
     Nix:         ["nix"],
@@ -121,6 +118,7 @@ var supportedModes = {
     SASS:        ["sass"],
     SCAD:        ["scad"],
     Scala:       ["scala"],
+    Smarty:      ["smarty|tpl"],
     Scheme:      ["scm|rkt"],
     SCSS:        ["scss"],
     SH:          ["sh|bash|^.bashrc"],
@@ -138,6 +136,7 @@ var supportedModes = {
     Toml:        ["toml"],
     Twig:        ["twig"],
     Typescript:  ["ts|typescript|str"],
+    Vala:        ["vala"],
     VBScript:    ["vbs"],
     Velocity:    ["vm"],
     Verilog:     ["v|vh|sv|svh"],
@@ -172,4 +171,8 @@ module.exports = {
 };
 
 });
-
+;
+                (function() {
+                    window.require(["ace/ext/modelist"], function() {});
+                })();
+            

+ 478 - 0
services/web/public/js/ace/ext-old_ie.js

@@ -0,0 +1,478 @@
+define("ace/ext/searchbox",["require","exports","module","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/keyboard/hash_handler","ace/lib/keys"], function(require, exports, module) {
+"use strict";
+
+var dom = require("../lib/dom");
+var lang = require("../lib/lang");
+var event = require("../lib/event");
+var searchboxCss = "\
+.ace_search {\
+background-color: #ddd;\
+border: 1px solid #cbcbcb;\
+border-top: 0 none;\
+max-width: 297px;\
+overflow: hidden;\
+margin: 0;\
+padding: 4px;\
+padding-right: 6px;\
+padding-bottom: 0;\
+position: absolute;\
+top: 0px;\
+z-index: 99;\
+white-space: normal;\
+}\
+.ace_search.left {\
+border-left: 0 none;\
+border-radius: 0px 0px 5px 0px;\
+left: 0;\
+}\
+.ace_search.right {\
+border-radius: 0px 0px 0px 5px;\
+border-right: 0 none;\
+right: 0;\
+}\
+.ace_search_form, .ace_replace_form {\
+border-radius: 3px;\
+border: 1px solid #cbcbcb;\
+float: left;\
+margin-bottom: 4px;\
+overflow: hidden;\
+}\
+.ace_search_form.ace_nomatch {\
+outline: 1px solid red;\
+}\
+.ace_search_field {\
+background-color: white;\
+border-right: 1px solid #cbcbcb;\
+border: 0 none;\
+-webkit-box-sizing: border-box;\
+-moz-box-sizing: border-box;\
+box-sizing: border-box;\
+display: block;\
+float: left;\
+height: 22px;\
+outline: 0;\
+padding: 0 7px;\
+width: 214px;\
+margin: 0;\
+}\
+.ace_searchbtn,\
+.ace_replacebtn {\
+background: #fff;\
+border: 0 none;\
+border-left: 1px solid #dcdcdc;\
+cursor: pointer;\
+display: block;\
+float: left;\
+height: 22px;\
+margin: 0;\
+padding: 0;\
+position: relative;\
+}\
+.ace_searchbtn:last-child,\
+.ace_replacebtn:last-child {\
+border-top-right-radius: 3px;\
+border-bottom-right-radius: 3px;\
+}\
+.ace_searchbtn:disabled {\
+background: none;\
+cursor: default;\
+}\
+.ace_searchbtn {\
+background-position: 50% 50%;\
+background-repeat: no-repeat;\
+width: 27px;\
+}\
+.ace_searchbtn.prev {\
+background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=);    \
+}\
+.ace_searchbtn.next {\
+background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=);    \
+}\
+.ace_searchbtn_close {\
+background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;\
+border-radius: 50%;\
+border: 0 none;\
+color: #656565;\
+cursor: pointer;\
+display: block;\
+float: right;\
+font-family: Arial;\
+font-size: 16px;\
+height: 14px;\
+line-height: 16px;\
+margin: 5px 1px 9px 5px;\
+padding: 0;\
+text-align: center;\
+width: 14px;\
+}\
+.ace_searchbtn_close:hover {\
+background-color: #656565;\
+background-position: 50% 100%;\
+color: white;\
+}\
+.ace_replacebtn.prev {\
+width: 54px\
+}\
+.ace_replacebtn.next {\
+width: 27px\
+}\
+.ace_button {\
+margin-left: 2px;\
+cursor: pointer;\
+-webkit-user-select: none;\
+-moz-user-select: none;\
+-o-user-select: none;\
+-ms-user-select: none;\
+user-select: none;\
+overflow: hidden;\
+opacity: 0.7;\
+border: 1px solid rgba(100,100,100,0.23);\
+padding: 1px;\
+-moz-box-sizing: border-box;\
+box-sizing:    border-box;\
+color: black;\
+}\
+.ace_button:hover {\
+background-color: #eee;\
+opacity:1;\
+}\
+.ace_button:active {\
+background-color: #ddd;\
+}\
+.ace_button.checked {\
+border-color: #3399ff;\
+opacity:1;\
+}\
+.ace_search_options{\
+margin-bottom: 3px;\
+text-align: right;\
+-webkit-user-select: none;\
+-moz-user-select: none;\
+-o-user-select: none;\
+-ms-user-select: none;\
+user-select: none;\
+}";
+var HashHandler = require("../keyboard/hash_handler").HashHandler;
+var keyUtil = require("../lib/keys");
+
+dom.importCssString(searchboxCss, "ace_searchbox");
+
+var html = '<div class="ace_search right">\
+    <button type="button" action="hide" class="ace_searchbtn_close"></button>\
+    <div class="ace_search_form">\
+        <input class="ace_search_field" placeholder="Search for" spellcheck="false"></input>\
+        <button type="button" action="findNext" class="ace_searchbtn next"></button>\
+        <button type="button" action="findPrev" class="ace_searchbtn prev"></button>\
+    </div>\
+    <div class="ace_replace_form">\
+        <input class="ace_search_field" placeholder="Replace with" spellcheck="false"></input>\
+        <button type="button" action="replaceAndFindNext" class="ace_replacebtn">Replace</button>\
+        <button type="button" action="replaceAll" class="ace_replacebtn">All</button>\
+    </div>\
+    <div class="ace_search_options">\
+        <span action="toggleRegexpMode" class="ace_button" title="RegExp Search">.*</span>\
+        <span action="toggleCaseSensitive" class="ace_button" title="CaseSensitive Search">Aa</span>\
+        <span action="toggleWholeWords" class="ace_button" title="Whole Word Search">\\b</span>\
+    </div>\
+</div>'.replace(/>\s+/g, ">");
+
+var SearchBox = function(editor, range, showReplaceForm) {
+    var div = dom.createElement("div");
+    div.innerHTML = html;
+    this.element = div.firstChild;
+
+    this.$init();
+    this.setEditor(editor);
+};
+
+(function() {
+    this.setEditor = function(editor) {
+        editor.searchBox = this;
+        editor.container.appendChild(this.element);
+        this.editor = editor;
+    };
+
+    this.$initElements = function(sb) {
+        this.searchBox = sb.querySelector(".ace_search_form");
+        this.replaceBox = sb.querySelector(".ace_replace_form");
+        this.searchOptions = sb.querySelector(".ace_search_options");
+        this.regExpOption = sb.querySelector("[action=toggleRegexpMode]");
+        this.caseSensitiveOption = sb.querySelector("[action=toggleCaseSensitive]");
+        this.wholeWordOption = sb.querySelector("[action=toggleWholeWords]");
+        this.searchInput = this.searchBox.querySelector(".ace_search_field");
+        this.replaceInput = this.replaceBox.querySelector(".ace_search_field");
+    };
+    
+    this.$init = function() {
+        var sb = this.element;
+        
+        this.$initElements(sb);
+        
+        var _this = this;
+        event.addListener(sb, "mousedown", function(e) {
+            setTimeout(function(){
+                _this.activeInput.focus();
+            }, 0);
+            event.stopPropagation(e);
+        });
+        event.addListener(sb, "click", function(e) {
+            var t = e.target || e.srcElement;
+            var action = t.getAttribute("action");
+            if (action && _this[action])
+                _this[action]();
+            else if (_this.$searchBarKb.commands[action])
+                _this.$searchBarKb.commands[action].exec(_this);
+            event.stopPropagation(e);
+        });
+
+        event.addCommandKeyListener(sb, function(e, hashId, keyCode) {
+            var keyString = keyUtil.keyCodeToString(keyCode);
+            var command = _this.$searchBarKb.findKeyCommand(hashId, keyString);
+            if (command && command.exec) {
+                command.exec(_this);
+                event.stopEvent(e);
+            }
+        });
+
+        this.$onChange = lang.delayedCall(function() {
+            _this.find(false, false);
+        });
+
+        event.addListener(this.searchInput, "input", function() {
+            _this.$onChange.schedule(20);
+        });
+        event.addListener(this.searchInput, "focus", function() {
+            _this.activeInput = _this.searchInput;
+            _this.searchInput.value && _this.highlight();
+        });
+        event.addListener(this.replaceInput, "focus", function() {
+            _this.activeInput = _this.replaceInput;
+            _this.searchInput.value && _this.highlight();
+        });
+    };
+    this.$closeSearchBarKb = new HashHandler([{
+        bindKey: "Esc",
+        name: "closeSearchBar",
+        exec: function(editor) {
+            editor.searchBox.hide();
+        }
+    }]);
+    this.$searchBarKb = new HashHandler();
+    this.$searchBarKb.bindKeys({
+        "Ctrl-f|Command-f|Ctrl-H|Command-Option-F": function(sb) {
+            var isReplace = sb.isReplace = !sb.isReplace;
+            sb.replaceBox.style.display = isReplace ? "" : "none";
+            sb[isReplace ? "replaceInput" : "searchInput"].focus();
+        },
+        "Ctrl-G|Command-G": function(sb) {
+            sb.findNext();
+        },
+        "Ctrl-Shift-G|Command-Shift-G": function(sb) {
+            sb.findPrev();
+        },
+        "esc": function(sb) {
+            setTimeout(function() { sb.hide();});
+        },
+        "Return": function(sb) {
+            if (sb.activeInput == sb.replaceInput)
+                sb.replace();
+            sb.findNext();
+        },
+        "Shift-Return": function(sb) {
+            if (sb.activeInput == sb.replaceInput)
+                sb.replace();
+            sb.findPrev();
+        },
+        "Tab": function(sb) {
+            (sb.activeInput == sb.replaceInput ? sb.searchInput : sb.replaceInput).focus();
+        }
+    });
+
+    this.$searchBarKb.addCommands([{
+        name: "toggleRegexpMode",
+        bindKey: {win: "Alt-R|Alt-/", mac: "Ctrl-Alt-R|Ctrl-Alt-/"},
+        exec: function(sb) {
+            sb.regExpOption.checked = !sb.regExpOption.checked;
+            sb.$syncOptions();
+        }
+    }, {
+        name: "toggleCaseSensitive",
+        bindKey: {win: "Alt-C|Alt-I", mac: "Ctrl-Alt-R|Ctrl-Alt-I"},
+        exec: function(sb) {
+            sb.caseSensitiveOption.checked = !sb.caseSensitiveOption.checked;
+            sb.$syncOptions();
+        }
+    }, {
+        name: "toggleWholeWords",
+        bindKey: {win: "Alt-B|Alt-W", mac: "Ctrl-Alt-B|Ctrl-Alt-W"},
+        exec: function(sb) {
+            sb.wholeWordOption.checked = !sb.wholeWordOption.checked;
+            sb.$syncOptions();
+        }
+    }]);
+
+    this.$syncOptions = function() {
+        dom.setCssClass(this.regExpOption, "checked", this.regExpOption.checked);
+        dom.setCssClass(this.wholeWordOption, "checked", this.wholeWordOption.checked);
+        dom.setCssClass(this.caseSensitiveOption, "checked", this.caseSensitiveOption.checked);
+        this.find(false, false);
+    };
+
+    this.highlight = function(re) {
+        this.editor.session.highlight(re || this.editor.$search.$options.re);
+        this.editor.renderer.updateBackMarkers()
+    };
+    this.find = function(skipCurrent, backwards) {
+        var range = this.editor.find(this.searchInput.value, {
+            skipCurrent: skipCurrent,
+            backwards: backwards,
+            wrap: true,
+            regExp: this.regExpOption.checked,
+            caseSensitive: this.caseSensitiveOption.checked,
+            wholeWord: this.wholeWordOption.checked
+        });
+        var noMatch = !range && this.searchInput.value;
+        dom.setCssClass(this.searchBox, "ace_nomatch", noMatch);
+        this.editor._emit("findSearchBox", { match: !noMatch });
+        this.highlight();
+    };
+    this.findNext = function() {
+        this.find(true, false);
+    };
+    this.findPrev = function() {
+        this.find(true, true);
+    };
+    this.replace = function() {
+        if (!this.editor.getReadOnly())
+            this.editor.replace(this.replaceInput.value);
+    };    
+    this.replaceAndFindNext = function() {
+        if (!this.editor.getReadOnly()) {
+            this.editor.replace(this.replaceInput.value);
+            this.findNext()
+        }
+    };
+    this.replaceAll = function() {
+        if (!this.editor.getReadOnly())
+            this.editor.replaceAll(this.replaceInput.value);
+    };
+
+    this.hide = function() {
+        this.element.style.display = "none";
+        this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb);
+        this.editor.focus();
+    };
+    this.show = function(value, isReplace) {
+        this.element.style.display = "";
+        this.replaceBox.style.display = isReplace ? "" : "none";
+
+        this.isReplace = isReplace;
+
+        if (value)
+            this.searchInput.value = value;
+        this.searchInput.focus();
+        this.searchInput.select();
+
+        this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb);
+    };
+
+}).call(SearchBox.prototype);
+
+exports.SearchBox = SearchBox;
+
+exports.Search = function(editor, isReplace) {
+    var sb = editor.searchBox || new SearchBox(editor);
+    sb.show(editor.session.getTextRange(), isReplace);
+};
+
+});
+
+define("ace/ext/old_ie",["require","exports","module","ace/lib/useragent","ace/tokenizer","ace/ext/searchbox","ace/mode/text"], function(require, exports, module) {
+"use strict";
+var MAX_TOKEN_COUNT = 1000;
+var useragent = require("../lib/useragent");
+var TokenizerModule = require("../tokenizer");
+
+function patch(obj, name, regexp, replacement) {
+    eval("obj['" + name + "']=" + obj[name].toString().replace(
+        regexp, replacement
+    ));
+}
+
+if (useragent.isIE && useragent.isIE < 10 && window.top.document.compatMode === "BackCompat")
+    useragent.isOldIE = true;
+
+if (typeof document != "undefined" && !document.documentElement.querySelector) {    
+    useragent.isOldIE = true;
+    var qs = function(el, selector) {
+        if (selector.charAt(0) == ".") {
+            var classNeme = selector.slice(1);
+        } else {
+            var m = selector.match(/(\w+)=(\w+)/);
+            var attr = m && m[1];
+            var attrVal = m && m[2];
+        }
+        for (var i = 0; i < el.all.length; i++) {
+            var ch = el.all[i];
+            if (classNeme) {
+                if (ch.className.indexOf(classNeme) != -1)
+                    return ch;
+            } else if (attr) {
+                if (ch.getAttribute(attr) == attrVal)
+                    return ch;
+            }
+        }
+    };
+    var sb = require("./searchbox").SearchBox.prototype;
+    patch(
+        sb, "$initElements",
+        /([^\s=]*).querySelector\((".*?")\)/g, 
+        "qs($1, $2)"
+    );
+}
+    
+var compliantExecNpcg = /()??/.exec("")[1] === undefined;
+if (compliantExecNpcg)
+    return;
+var proto = TokenizerModule.Tokenizer.prototype;
+TokenizerModule.Tokenizer_orig = TokenizerModule.Tokenizer;
+proto.getLineTokens_orig = proto.getLineTokens;
+
+patch(
+    TokenizerModule, "Tokenizer",
+    "ruleRegExps.push(adjustedregex);\n", 
+    function(m) {
+        return m + '\
+        if (state[i].next && RegExp(adjustedregex).test(""))\n\
+            rule._qre = RegExp(adjustedregex, "g");\n\
+        ';
+    }
+);
+TokenizerModule.Tokenizer.prototype = proto;
+patch(
+    proto, "getLineTokens",
+    /if \(match\[i \+ 1\] === undefined\)\s*continue;/, 
+    "if (!match[i + 1]) {\n\
+        if (value)continue;\n\
+        var qre = state[mapping[i]]._qre;\n\
+        if (!qre) continue;\n\
+        qre.lastIndex = lastIndex;\n\
+        if (!qre.exec(line) || qre.lastIndex != lastIndex)\n\
+            continue;\n\
+    }"
+);
+
+patch(
+    require("../mode/text").Mode.prototype, "getTokenizer",
+    /Tokenizer/,
+    "TokenizerModule.Tokenizer"
+);
+
+useragent.isOldIE = true;
+
+});
+;
+                (function() {
+                    window.require(["ace/ext/old_ie"], function() {});
+                })();
+            

+ 154 - 47
services/web/public/js/ace/ext/searchbox.js → services/web/public/js/ace/ext-searchbox.js

@@ -1,40 +1,157 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
+define("ace/ext/searchbox",["require","exports","module","ace/lib/dom","ace/lib/lang","ace/lib/event","ace/keyboard/hash_handler","ace/lib/keys"], function(require, exports, module) {
 "use strict";
 
 var dom = require("../lib/dom");
 var lang = require("../lib/lang");
 var event = require("../lib/event");
-var searchboxCss = require("../requirejs/text!./searchbox.css");
+var searchboxCss = "\
+.ace_search {\
+background-color: #ddd;\
+border: 1px solid #cbcbcb;\
+border-top: 0 none;\
+max-width: 297px;\
+overflow: hidden;\
+margin: 0;\
+padding: 4px;\
+padding-right: 6px;\
+padding-bottom: 0;\
+position: absolute;\
+top: 0px;\
+z-index: 99;\
+white-space: normal;\
+}\
+.ace_search.left {\
+border-left: 0 none;\
+border-radius: 0px 0px 5px 0px;\
+left: 0;\
+}\
+.ace_search.right {\
+border-radius: 0px 0px 0px 5px;\
+border-right: 0 none;\
+right: 0;\
+}\
+.ace_search_form, .ace_replace_form {\
+border-radius: 3px;\
+border: 1px solid #cbcbcb;\
+float: left;\
+margin-bottom: 4px;\
+overflow: hidden;\
+}\
+.ace_search_form.ace_nomatch {\
+outline: 1px solid red;\
+}\
+.ace_search_field {\
+background-color: white;\
+border-right: 1px solid #cbcbcb;\
+border: 0 none;\
+-webkit-box-sizing: border-box;\
+-moz-box-sizing: border-box;\
+box-sizing: border-box;\
+display: block;\
+float: left;\
+height: 22px;\
+outline: 0;\
+padding: 0 7px;\
+width: 214px;\
+margin: 0;\
+}\
+.ace_searchbtn,\
+.ace_replacebtn {\
+background: #fff;\
+border: 0 none;\
+border-left: 1px solid #dcdcdc;\
+cursor: pointer;\
+display: block;\
+float: left;\
+height: 22px;\
+margin: 0;\
+padding: 0;\
+position: relative;\
+}\
+.ace_searchbtn:last-child,\
+.ace_replacebtn:last-child {\
+border-top-right-radius: 3px;\
+border-bottom-right-radius: 3px;\
+}\
+.ace_searchbtn:disabled {\
+background: none;\
+cursor: default;\
+}\
+.ace_searchbtn {\
+background-position: 50% 50%;\
+background-repeat: no-repeat;\
+width: 27px;\
+}\
+.ace_searchbtn.prev {\
+background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=);    \
+}\
+.ace_searchbtn.next {\
+background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=);    \
+}\
+.ace_searchbtn_close {\
+background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;\
+border-radius: 50%;\
+border: 0 none;\
+color: #656565;\
+cursor: pointer;\
+display: block;\
+float: right;\
+font-family: Arial;\
+font-size: 16px;\
+height: 14px;\
+line-height: 16px;\
+margin: 5px 1px 9px 5px;\
+padding: 0;\
+text-align: center;\
+width: 14px;\
+}\
+.ace_searchbtn_close:hover {\
+background-color: #656565;\
+background-position: 50% 100%;\
+color: white;\
+}\
+.ace_replacebtn.prev {\
+width: 54px\
+}\
+.ace_replacebtn.next {\
+width: 27px\
+}\
+.ace_button {\
+margin-left: 2px;\
+cursor: pointer;\
+-webkit-user-select: none;\
+-moz-user-select: none;\
+-o-user-select: none;\
+-ms-user-select: none;\
+user-select: none;\
+overflow: hidden;\
+opacity: 0.7;\
+border: 1px solid rgba(100,100,100,0.23);\
+padding: 1px;\
+-moz-box-sizing: border-box;\
+box-sizing:    border-box;\
+color: black;\
+}\
+.ace_button:hover {\
+background-color: #eee;\
+opacity:1;\
+}\
+.ace_button:active {\
+background-color: #ddd;\
+}\
+.ace_button.checked {\
+border-color: #3399ff;\
+opacity:1;\
+}\
+.ace_search_options{\
+margin-bottom: 3px;\
+text-align: right;\
+-webkit-user-select: none;\
+-moz-user-select: none;\
+-o-user-select: none;\
+-ms-user-select: none;\
+user-select: none;\
+}";
 var HashHandler = require("../keyboard/hash_handler").HashHandler;
 var keyUtil = require("../lib/keys");
 
@@ -133,8 +250,6 @@ var SearchBox = function(editor, range, showReplaceForm) {
             _this.searchInput.value && _this.highlight();
         });
     };
-
-    //keybinging outsite of the searchbox
     this.$closeSearchBarKb = new HashHandler([{
         bindKey: "Esc",
         name: "closeSearchBar",
@@ -142,8 +257,6 @@ var SearchBox = function(editor, range, showReplaceForm) {
             editor.searchBox.hide();
         }
     }]);
-
-    //keybinging outsite of the searchbox
     this.$searchBarKb = new HashHandler();
     this.$searchBarKb.bindKeys({
         "Ctrl-f|Command-f|Ctrl-H|Command-Option-F": function(sb) {
@@ -273,14 +386,8 @@ exports.Search = function(editor, isReplace) {
 };
 
 });
-
-
-/* ------------------------------------------------------------------------------------------
- * TODO
- * --------------------------------------------------------------------------------------- */
-/*
-- move search form to the left if it masks current word
-- includ all options that search has. ex: regex
-- searchbox.searchbox is not that pretty. we should have just searchbox
-- disable prev button if it makes sence
-*/
+;
+                (function() {
+                    window.require(["ace/ext/searchbox"], function() {});
+                })();
+            

+ 622 - 0
services/web/public/js/ace/ext-settings_menu.js

@@ -0,0 +1,622 @@
+define("ace/ext/menu_tools/element_generator",["require","exports","module"], function(require, exports, module) {
+'use strict';
+module.exports.createOption = function createOption (obj) {
+    var attribute;
+    var el = document.createElement('option');
+    for(attribute in obj) {
+        if(obj.hasOwnProperty(attribute)) {
+            if(attribute === 'selected') {
+                el.setAttribute(attribute, obj[attribute]);
+            } else {
+                el[attribute] = obj[attribute];
+            }
+        }
+    }
+    return el;
+};
+module.exports.createCheckbox = function createCheckbox (id, checked, clss) {
+    var el = document.createElement('input');
+    el.setAttribute('type', 'checkbox');
+    el.setAttribute('id', id);
+    el.setAttribute('name', id);
+    el.setAttribute('value', checked);
+    el.setAttribute('class', clss);
+    if(checked) {
+        el.setAttribute('checked', 'checked');
+    }
+    return el;
+};
+module.exports.createInput = function createInput (id, value, clss) {
+    var el = document.createElement('input');
+    el.setAttribute('type', 'text');
+    el.setAttribute('id', id);
+    el.setAttribute('name', id);
+    el.setAttribute('value', value);
+    el.setAttribute('class', clss);
+    return el;
+};
+module.exports.createLabel = function createLabel (text, labelFor) {
+    var el = document.createElement('label');
+    el.setAttribute('for', labelFor);
+    el.textContent = text;
+    return el;
+};
+module.exports.createSelection = function createSelection (id, values, clss) {
+    var el = document.createElement('select');
+    el.setAttribute('id', id);
+    el.setAttribute('name', id);
+    el.setAttribute('class', clss);
+    values.forEach(function(item) {
+        el.appendChild(module.exports.createOption(item));
+    });
+    return el;
+};
+
+});
+
+define("ace/ext/modelist",["require","exports","module"], function(require, exports, module) {
+"use strict";
+
+var modes = [];
+function getModeForPath(path) {
+    var mode = modesByName.text;
+    var fileName = path.split(/[\/\\]/).pop();
+    for (var i = 0; i < modes.length; i++) {
+        if (modes[i].supportsFile(fileName)) {
+            mode = modes[i];
+            break;
+        }
+    }
+    return mode;
+}
+
+var Mode = function(name, caption, extensions) {
+    this.name = name;
+    this.caption = caption;
+    this.mode = "ace/mode/" + name;
+    this.extensions = extensions;
+    if (/\^/.test(extensions)) {
+        var re = extensions.replace(/\|(\^)?/g, function(a, b){
+            return "$|" + (b ? "^" : "^.*\\.");
+        }) + "$";
+    } else {
+        var re = "^.*\\.(" + extensions + ")$";
+    }
+
+    this.extRe = new RegExp(re, "gi");
+};
+
+Mode.prototype.supportsFile = function(filename) {
+    return filename.match(this.extRe);
+};
+var supportedModes = {
+    ABAP:        ["abap"],
+    ActionScript:["as"],
+    ADA:         ["ada|adb"],
+    Apache_Conf: ["^htaccess|^htgroups|^htpasswd|^conf|htaccess|htgroups|htpasswd"],
+    AsciiDoc:    ["asciidoc"],
+    Assembly_x86:["asm"],
+    AutoHotKey:  ["ahk"],
+    BatchFile:   ["bat|cmd"],
+    C9Search:    ["c9search_results"],
+    C_Cpp:       ["cpp|c|cc|cxx|h|hh|hpp"],
+    Cirru:       ["cirru|cr"],
+    Clojure:     ["clj|cljs"],
+    Cobol:       ["CBL|COB"],
+    coffee:      ["coffee|cf|cson|^Cakefile"],
+    ColdFusion:  ["cfm"],
+    CSharp:      ["cs"],
+    CSS:         ["css"],
+    Curly:       ["curly"],
+    D:           ["d|di"],
+    Dart:        ["dart"],
+    Diff:        ["diff|patch"],
+    Dockerfile:  ["^Dockerfile"],
+    Dot:         ["dot"],
+    Erlang:      ["erl|hrl"],
+    EJS:         ["ejs"],
+    Forth:       ["frt|fs|ldr"],
+    FTL:         ["ftl"],
+    Gherkin:     ["feature"],
+    Gitignore:   ["^.gitignore"],
+    Glsl:        ["glsl|frag|vert"],
+    golang:      ["go"],
+    Groovy:      ["groovy"],
+    HAML:        ["haml"],
+    Handlebars:  ["hbs|handlebars|tpl|mustache"],
+    Haskell:     ["hs"],
+    haXe:        ["hx"],
+    HTML:        ["html|htm|xhtml"],
+    HTML_Ruby:   ["erb|rhtml|html.erb"],
+    INI:         ["ini|conf|cfg|prefs"],
+    Jack:        ["jack"],
+    Jade:        ["jade"],
+    Java:        ["java"],
+    JavaScript:  ["js|jsm"],
+    JSON:        ["json"],
+    JSONiq:      ["jq"],
+    JSP:         ["jsp"],
+    JSX:         ["jsx"],
+    Julia:       ["jl"],
+    LaTeX:       ["tex|latex|ltx|bib"],
+    LESS:        ["less"],
+    Liquid:      ["liquid"],
+    Lisp:        ["lisp"],
+    LiveScript:  ["ls"],
+    LogiQL:      ["logic|lql"],
+    LSL:         ["lsl"],
+    Lua:         ["lua"],
+    LuaPage:     ["lp"],
+    Lucene:      ["lucene"],
+    Makefile:    ["^Makefile|^GNUmakefile|^makefile|^OCamlMakefile|make"],
+    MATLAB:      ["matlab"],
+    Markdown:    ["md|markdown"],
+    MEL:         ["mel"],
+    MySQL:       ["mysql"],
+    MUSHCode:    ["mc|mush"],
+    Nix:         ["nix"],
+    ObjectiveC:  ["m|mm"],
+    OCaml:       ["ml|mli"],
+    Pascal:      ["pas|p"],
+    Perl:        ["pl|pm"],
+    pgSQL:       ["pgsql"],
+    PHP:         ["php|phtml"],
+    Powershell:  ["ps1"],
+    Prolog:      ["plg|prolog"],
+    Properties:  ["properties"],
+    Protobuf:    ["proto"],
+    Python:      ["py"],
+    R:           ["r"],
+    RDoc:        ["Rd"],
+    RHTML:       ["Rhtml"],
+    Ruby:        ["rb|ru|gemspec|rake|^Guardfile|^Rakefile|^Gemfile"],
+    Rust:        ["rs"],
+    SASS:        ["sass"],
+    SCAD:        ["scad"],
+    Scala:       ["scala"],
+    Smarty:      ["smarty|tpl"],
+    Scheme:      ["scm|rkt"],
+    SCSS:        ["scss"],
+    SH:          ["sh|bash|^.bashrc"],
+    SJS:         ["sjs"],
+    Space:       ["space"],
+    snippets:    ["snippets"],
+    Soy_Template:["soy"],
+    SQL:         ["sql"],
+    Stylus:      ["styl|stylus"],
+    SVG:         ["svg"],
+    Tcl:         ["tcl"],
+    Tex:         ["tex"],
+    Text:        ["txt"],
+    Textile:     ["textile"],
+    Toml:        ["toml"],
+    Twig:        ["twig"],
+    Typescript:  ["ts|typescript|str"],
+    Vala:        ["vala"],
+    VBScript:    ["vbs"],
+    Velocity:    ["vm"],
+    Verilog:     ["v|vh|sv|svh"],
+    XML:         ["xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl"],
+    XQuery:      ["xq"],
+    YAML:        ["yaml|yml"]
+};
+
+var nameOverrides = {
+    ObjectiveC: "Objective-C",
+    CSharp: "C#",
+    golang: "Go",
+    C_Cpp: "C/C++",
+    coffee: "CoffeeScript",
+    HTML_Ruby: "HTML (Ruby)",
+    FTL: "FreeMarker"
+};
+var modesByName = {};
+for (var name in supportedModes) {
+    var data = supportedModes[name];
+    var displayName = (nameOverrides[name] || name).replace(/_/g, " ");
+    var filename = name.toLowerCase();
+    var mode = new Mode(filename, displayName, data[0]);
+    modesByName[filename] = mode;
+    modes.push(mode);
+}
+
+module.exports = {
+    getModeForPath: getModeForPath,
+    modes: modes,
+    modesByName: modesByName
+};
+
+});
+
+define("ace/ext/themelist",["require","exports","module","ace/lib/fixoldbrowsers"], function(require, exports, module) {
+"use strict";
+require("ace/lib/fixoldbrowsers");
+
+var themeData = [
+    ["Chrome"         ],
+    ["Clouds"         ],
+    ["Crimson Editor" ],
+    ["Dawn"           ],
+    ["Dreamweaver"    ],
+    ["Eclipse"        ],
+    ["GitHub"         ],
+    ["Solarized Light"],
+    ["TextMate"       ],
+    ["Tomorrow"       ],
+    ["XCode"          ],
+    ["Kuroir"],
+    ["KatzenMilch"],
+    ["Ambiance"             ,"ambiance"                ,  "dark"],
+    ["Chaos"                ,"chaos"                   ,  "dark"],
+    ["Clouds Midnight"      ,"clouds_midnight"         ,  "dark"],
+    ["Cobalt"               ,"cobalt"                  ,  "dark"],
+    ["idle Fingers"         ,"idle_fingers"            ,  "dark"],
+    ["krTheme"              ,"kr_theme"                ,  "dark"],
+    ["Merbivore"            ,"merbivore"               ,  "dark"],
+    ["Merbivore Soft"       ,"merbivore_soft"          ,  "dark"],
+    ["Mono Industrial"      ,"mono_industrial"         ,  "dark"],
+    ["Monokai"              ,"monokai"                 ,  "dark"],
+    ["Pastel on dark"       ,"pastel_on_dark"          ,  "dark"],
+    ["Solarized Dark"       ,"solarized_dark"          ,  "dark"],
+    ["Terminal"             ,"terminal"                ,  "dark"],
+    ["Tomorrow Night"       ,"tomorrow_night"          ,  "dark"],
+    ["Tomorrow Night Blue"  ,"tomorrow_night_blue"     ,  "dark"],
+    ["Tomorrow Night Bright","tomorrow_night_bright"   ,  "dark"],
+    ["Tomorrow Night 80s"   ,"tomorrow_night_eighties" ,  "dark"],
+    ["Twilight"             ,"twilight"                ,  "dark"],
+    ["Vibrant Ink"          ,"vibrant_ink"             ,  "dark"]
+];
+
+
+exports.themesByName = {};
+exports.themes = themeData.map(function(data) {
+    var name = data[1] || data[0].replace(/ /g, "_").toLowerCase();
+    var theme = {
+        caption: data[0],
+        theme: "ace/theme/" + name,
+        isDark: data[2] == "dark",
+        name: name
+    };
+    exports.themesByName[name] = theme;
+    return theme;
+});
+
+});
+
+define("ace/ext/menu_tools/add_editor_menu_options",["require","exports","module","ace/ext/modelist","ace/ext/themelist"], function(require, exports, module) {
+'use strict';
+module.exports.addEditorMenuOptions = function addEditorMenuOptions (editor) {
+    var modelist = require('../modelist');
+    var themelist = require('../themelist');
+    editor.menuOptions = {
+        setNewLineMode: [{
+            textContent: "unix",
+            value: "unix"
+        }, {
+            textContent: "windows",
+            value: "windows"
+        }, {
+            textContent: "auto",
+            value: "auto"
+        }],
+        setTheme: [],
+        setMode: [],
+        setKeyboardHandler: [{
+            textContent: "ace",
+            value: ""
+        }, {
+            textContent: "vim",
+            value: "ace/keyboard/vim"
+        }, {
+            textContent: "emacs",
+            value: "ace/keyboard/emacs"
+        }, {
+            textContent: "textarea",
+            value: "ace/keyboard/textarea"
+        }, {
+            textContent: "sublime",
+            value: "ace/keyboard/sublime"
+        }]
+    };
+
+    editor.menuOptions.setTheme = themelist.themes.map(function(theme) {
+        return {
+            textContent: theme.caption,
+            value: theme.theme
+        };
+    });
+
+    editor.menuOptions.setMode = modelist.modes.map(function(mode) {
+        return {
+            textContent: mode.name,
+            value: mode.mode
+        };
+    });
+};
+
+
+});
+
+define("ace/ext/menu_tools/get_set_functions",["require","exports","module"], function(require, exports, module) {
+'use strict';
+module.exports.getSetFunctions = function getSetFunctions (editor) {
+    var out = [];
+    var my = {
+        'editor' : editor,
+        'session' : editor.session,
+        'renderer' : editor.renderer
+    };
+    var opts = [];
+    var skip = [
+        'setOption',
+        'setUndoManager',
+        'setDocument',
+        'setValue',
+        'setBreakpoints',
+        'setScrollTop',
+        'setScrollLeft',
+        'setSelectionStyle',
+        'setWrapLimitRange'
+    ];
+    ['renderer', 'session', 'editor'].forEach(function(esra) {
+        var esr = my[esra];
+        var clss = esra;
+        for(var fn in esr) {
+            if(skip.indexOf(fn) === -1) {
+                if(/^set/.test(fn) && opts.indexOf(fn) === -1) {
+                    opts.push(fn);
+                    out.push({
+                        'functionName' : fn,
+                        'parentObj' : esr,
+                        'parentName' : clss
+                    });
+                }
+            }
+        }
+    });
+    return out;
+};
+
+});
+
+define("ace/ext/menu_tools/generate_settings_menu",["require","exports","module","ace/ext/menu_tools/element_generator","ace/ext/menu_tools/add_editor_menu_options","ace/ext/menu_tools/get_set_functions"], function(require, exports, module) {
+'use strict';
+var egen = require('./element_generator');
+var addEditorMenuOptions = require('./add_editor_menu_options').addEditorMenuOptions;
+var getSetFunctions = require('./get_set_functions').getSetFunctions;
+module.exports.generateSettingsMenu = function generateSettingsMenu (editor) {
+    var elements = [];
+    function cleanupElementsList() {
+        elements.sort(function(a, b) {
+            var x = a.getAttribute('contains');
+            var y = b.getAttribute('contains');
+            return x.localeCompare(y);
+        });
+    }
+    function wrapElements() {
+        var topmenu = document.createElement('div');
+        topmenu.setAttribute('id', 'ace_settingsmenu');
+        elements.forEach(function(element) {
+            topmenu.appendChild(element);
+        });
+        return topmenu;
+    }
+    function createNewEntry(obj, clss, item, val) {
+        var el;
+        var div = document.createElement('div');
+        div.setAttribute('contains', item);
+        div.setAttribute('class', 'ace_optionsMenuEntry');
+        div.setAttribute('style', 'clear: both;');
+
+        div.appendChild(egen.createLabel(
+            item.replace(/^set/, '').replace(/([A-Z])/g, ' $1').trim(),
+            item
+        ));
+
+        if (Array.isArray(val)) {
+            el = egen.createSelection(item, val, clss);
+            el.addEventListener('change', function(e) {
+                try{
+                    editor.menuOptions[e.target.id].forEach(function(x) {
+                        if(x.textContent !== e.target.textContent) {
+                            delete x.selected;
+                        }
+                    });
+                    obj[e.target.id](e.target.value);
+                } catch (err) {
+                    throw new Error(err);
+                }
+            });
+        } else if(typeof val === 'boolean') {
+            el = egen.createCheckbox(item, val, clss);
+            el.addEventListener('change', function(e) {
+                try{
+                    obj[e.target.id](!!e.target.checked);
+                } catch (err) {
+                    throw new Error(err);
+                }
+            });
+        } else {
+            el = egen.createInput(item, val, clss);
+            el.addEventListener('change', function(e) {
+                try{
+                    if(e.target.value === 'true') {
+                        obj[e.target.id](true);
+                    } else if(e.target.value === 'false') {
+                        obj[e.target.id](false);
+                    } else {
+                        obj[e.target.id](e.target.value);
+                    }
+                } catch (err) {
+                    throw new Error(err);
+                }
+            });
+        }
+        el.style.cssText = 'float:right;';
+        div.appendChild(el);
+        return div;
+    }
+    function makeDropdown(item, esr, clss, fn) {
+        var val = editor.menuOptions[item];
+        var currentVal = esr[fn]();
+        if (typeof currentVal == 'object')
+            currentVal = currentVal.$id;
+        val.forEach(function(valuex) {
+            if (valuex.value === currentVal)
+                valuex.selected = 'selected';
+        });
+        return createNewEntry(esr, clss, item, val);
+    }
+    function handleSet(setObj) {
+        var item = setObj.functionName;
+        var esr = setObj.parentObj;
+        var clss = setObj.parentName;
+        var val;
+        var fn = item.replace(/^set/, 'get');
+        if(editor.menuOptions[item] !== undefined) {
+            elements.push(makeDropdown(item, esr, clss, fn));
+        } else if(typeof esr[fn] === 'function') {
+            try {
+                val = esr[fn]();
+                if(typeof val === 'object') {
+                    val = val.$id;
+                }
+                elements.push(
+                    createNewEntry(esr, clss, item, val)
+                );
+            } catch (e) {
+            }
+        }
+    }
+    addEditorMenuOptions(editor);
+    getSetFunctions(editor).forEach(function(setObj) {
+        handleSet(setObj);
+    });
+    cleanupElementsList();
+    return wrapElements();
+};
+
+});
+
+define("ace/ext/menu_tools/overlay_page",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
+'use strict';
+var dom = require("../../lib/dom");
+var cssText = "#ace_settingsmenu, #kbshortcutmenu {\
+background-color: #F7F7F7;\
+color: black;\
+box-shadow: -5px 4px 5px rgba(126, 126, 126, 0.55);\
+padding: 1em 0.5em 2em 1em;\
+overflow: auto;\
+position: absolute;\
+margin: 0;\
+bottom: 0;\
+right: 0;\
+top: 0;\
+z-index: 9991;\
+cursor: default;\
+}\
+.ace_dark #ace_settingsmenu, .ace_dark #kbshortcutmenu {\
+box-shadow: -20px 10px 25px rgba(126, 126, 126, 0.25);\
+background-color: rgba(255, 255, 255, 0.6);\
+color: black;\
+}\
+.ace_optionsMenuEntry:hover {\
+background-color: rgba(100, 100, 100, 0.1);\
+-webkit-transition: all 0.5s;\
+transition: all 0.3s\
+}\
+.ace_closeButton {\
+background: rgba(245, 146, 146, 0.5);\
+border: 1px solid #F48A8A;\
+border-radius: 50%;\
+padding: 7px;\
+position: absolute;\
+right: -8px;\
+top: -8px;\
+z-index: 1000;\
+}\
+.ace_closeButton{\
+background: rgba(245, 146, 146, 0.9);\
+}\
+.ace_optionsMenuKey {\
+color: darkslateblue;\
+font-weight: bold;\
+}\
+.ace_optionsMenuCommand {\
+color: darkcyan;\
+font-weight: normal;\
+}";
+dom.importCssString(cssText);
+module.exports.overlayPage = function overlayPage(editor, contentElement, top, right, bottom, left) {
+    top = top ? 'top: ' + top + ';' : '';
+    bottom = bottom ? 'bottom: ' + bottom + ';' : '';
+    right = right ? 'right: ' + right + ';' : '';
+    left = left ? 'left: ' + left + ';' : '';
+
+    var closer = document.createElement('div');
+    var contentContainer = document.createElement('div');
+
+    function documentEscListener(e) {
+        if (e.keyCode === 27) {
+            closer.click();
+        }
+    }
+
+    closer.style.cssText = 'margin: 0; padding: 0; ' +
+        'position: fixed; top:0; bottom:0; left:0; right:0;' +
+        'z-index: 9990; ' +
+        'background-color: rgba(0, 0, 0, 0.3);';
+    closer.addEventListener('click', function() {
+        document.removeEventListener('keydown', documentEscListener);
+        closer.parentNode.removeChild(closer);
+        editor.focus();
+        closer = null;
+    });
+    document.addEventListener('keydown', documentEscListener);
+
+    contentContainer.style.cssText = top + right + bottom + left;
+    contentContainer.addEventListener('click', function(e) {
+        e.stopPropagation();
+    });
+
+    var wrapper = dom.createElement("div");
+    wrapper.style.position = "relative";
+    
+    var closeButton = dom.createElement("div");
+    closeButton.className = "ace_closeButton";
+    closeButton.addEventListener('click', function() {
+        closer.click();
+    });
+    
+    wrapper.appendChild(closeButton);
+    contentContainer.appendChild(wrapper);
+    
+    contentContainer.appendChild(contentElement);
+    closer.appendChild(contentContainer);
+    document.body.appendChild(closer);
+    editor.blur();
+};
+
+});
+
+define("ace/ext/settings_menu",["require","exports","module","ace/ext/menu_tools/generate_settings_menu","ace/ext/menu_tools/overlay_page","ace/editor"], function(require, exports, module) {
+"use strict";
+var generateSettingsMenu = require('./menu_tools/generate_settings_menu').generateSettingsMenu;
+var overlayPage = require('./menu_tools/overlay_page').overlayPage;
+function showSettingsMenu(editor) {
+    var sm = document.getElementById('ace_settingsmenu');
+    if (!sm)    
+        overlayPage(editor, generateSettingsMenu(editor), '0', '0', '0');
+}
+module.exports.init = function(editor) {
+    var Editor = require("ace/editor").Editor;
+    Editor.prototype.showSettingsMenu = function() {
+        showSettingsMenu(this);
+    };
+};
+});
+;
+                (function() {
+                    window.require(["ace/ext/settings_menu"], function() {});
+                })();
+            

+ 6 - 3
services/web/public/js/ace/ext/spellcheck.js → services/web/public/js/ace/ext-spellcheck.js

@@ -1,4 +1,4 @@
-define(function(require, exports, module) {
+define("ace/ext/spellcheck",["require","exports","module","ace/lib/event","ace/editor","ace/config"], function(require, exports, module) {
 "use strict";
 var event = require("../lib/event");
 
@@ -49,7 +49,6 @@ exports.contextMenuHandler = function(e){
         return newVal;
     });
 };
-// todo support highlighting with typo.js
 var Editor = require("../editor").Editor;
 require("../config").defineOptions(Editor.prototype, "editor", {
     spellcheck: {
@@ -66,4 +65,8 @@ require("../config").defineOptions(Editor.prototype, "editor", {
 });
 
 });
-
+;
+                (function() {
+                    window.require(["ace/ext/spellcheck"], function() {});
+                })();
+            

+ 12 - 138
services/web/public/js/ace/split.js → services/web/public/js/ace/ext-split.js

@@ -1,34 +1,4 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
+define("ace/split",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter","ace/editor","ace/virtual_renderer","ace/edit_session"], function(require, exports, module) {
 "use strict";
 
 var oop = require("./lib/oop");
@@ -39,13 +9,6 @@ var Editor = require("./editor").Editor;
 var Renderer = require("./virtual_renderer").VirtualRenderer;
 var EditSession = require("./edit_session").EditSession;
 
-/** 
- * @class Split
- *
- *
- *
- **/
-
 
 var Split = function(container, theme, splits) {
     this.BELOW = 1;
@@ -115,97 +78,37 @@ var Split = function(container, theme, splits) {
         }
         this.resize();
     };
-
-    /**
-     * 
-     * Returns the number of splits.
-     * @returns {Number}
-     **/
     this.getSplits = function() {
         return this.$splits;
     };
-
-    /**
-     * @param {Number} idx The index of the editor you want
-     *
-     * Returns the editor identified by the index `idx`.
-     *
-     **/
     this.getEditor = function(idx) {
         return this.$editors[idx];
     };
-
-    /**
-     * 
-     * Returns the current editor.
-     * @returns {Editor}
-     **/
     this.getCurrentEditor = function() {
         return this.$cEditor;
     };
-
-    /** 
-     * Focuses the current editor.
-     * @related Editor.focus
-     **/
     this.focus = function() {
         this.$cEditor.focus();
     };
-
-    /** 
-     * Blurs the current editor.
-     * @related Editor.blur
-     **/
     this.blur = function() {
         this.$cEditor.blur();
     };
-
-    /** 
-     * 
-     * @param {String} theme The name of the theme to set
-     * 
-     * Sets a theme for each of the available editors.
-     * @related Editor.setTheme
-     **/
     this.setTheme = function(theme) {
         this.$editors.forEach(function(editor) {
             editor.setTheme(theme);
         });
     };
-
-    /** 
-     * 
-     * @param {String} keybinding 
-     * 
-     * Sets the keyboard handler for the editor.
-     * @related editor.setKeyboardHandler
-     **/
     this.setKeyboardHandler = function(keybinding) {
         this.$editors.forEach(function(editor) {
             editor.setKeyboardHandler(keybinding);
         });
     };
-
-    /** 
-     * 
-     * @param {Function} callback A callback function to execute
-     * @param {String} scope The default scope for the callback
-     * 
-     * Executes `callback` on all of the available editors. 
-     *
-     **/
     this.forEach = function(callback, scope) {
         this.$editors.forEach(callback, scope);
     };
 
 
     this.$fontSize = "";
-    /** 
-     * @param {Number} size The new font size
-     * 
-     * Sets the font size, in pixels, for all the available editors.
-     *
-     **/
     this.setFontSize = function(size) {
         this.$fontSize = size;
         this.forEach(function(editor) {
@@ -221,12 +124,7 @@ var Split = function(container, theme, splits) {
             var undoManagerProxy = new UndoManagerProxy(undoManager, s);
             s.setUndoManager(undoManagerProxy);
         }
-
-        // Overwrite the default $informUndoManager function such that new delas
-        // aren't added to the undo manager from the new and the old session.
         s.$informUndoManager = lang.delayedCall(function() { s.$deltas = []; });
-
-        // Copy over 'settings' from the session.
         s.setTabSize(session.getTabSize());
         s.setUseSoftTabs(session.getUseSoftTabs());
         s.setOverwrite(session.getOverwrite());
@@ -239,15 +137,6 @@ var Split = function(container, theme, splits) {
 
         return s;
     };
-
-   /** 
-     * 
-     * @param {EditSession} session The new edit session
-     * @param {Number} idx The editor's index you're interested in
-     * 
-     * Sets a new [[EditSession `EditSession`]] for the indicated editor.
-     * @related Editor.setSession
-     **/
     this.setSession = function(session, idx) {
         var editor;
         if (idx == null) {
@@ -255,12 +144,6 @@ var Split = function(container, theme, splits) {
         } else {
             editor = this.$editors[idx];
         }
-
-        // Check if the session is used already by any of the editors in the
-        // split. If it is, we have to clone the session as two editors using
-        // the same session can cause terrible side effects (e.g. UndoQueue goes
-        // wrong). This also gives the user of Split the possibility to treat
-        // each session on each split editor different.
         var isUsed = this.$editors.some(function(editor) {
            return editor.session === session;
         });
@@ -269,27 +152,11 @@ var Split = function(container, theme, splits) {
             session = this.$cloneSession(session);
         }
         editor.setSession(session);
-
-        // Return the session set on the editor. This might be a cloned one.
         return session;
     };
-
-   /** 
-     * 
-     * Returns the orientation.
-     * @returns {Number}
-     **/
     this.getOrientation = function() {
         return this.$orientation;
     };
-
-   /** 
-     * 
-     * Sets the orientation.
-     * @param {Number} orientation The new orientation value
-     *
-     *
-     **/
     this.setOrientation = function(orientation) {
         if (this.$orientation == orientation) {
             return;
@@ -297,10 +164,6 @@ var Split = function(container, theme, splits) {
         this.$orientation = orientation;
         this.resize();
     };
-
-   /**  
-     * Resizes the editor.
-     **/
     this.resize = function() {
         var width = this.$container.clientWidth;
         var height = this.$container.clientHeight;
@@ -371,3 +234,14 @@ function UndoManagerProxy(undoManager, session) {
 
 exports.Split = Split;
 });
+
+define("ace/ext/split",["require","exports","module","ace/split"], function(require, exports, module) {
+"use strict";
+module.exports = require("../split");
+
+});
+;
+                (function() {
+                    window.require(["ace/ext/split"], function() {});
+                })();
+            

+ 36 - 69
services/web/public/js/ace/ext/static_highlight.js → services/web/public/js/ace/ext-static_highlight.js

@@ -1,39 +1,36 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
+define("ace/ext/static_highlight",["require","exports","module","ace/edit_session","ace/layer/text","ace/config","ace/lib/dom"], function(require, exports, module) {
 "use strict";
 
 var EditSession = require("../edit_session").EditSession;
 var TextLayer = require("../layer/text").Text;
-var baseStyles = require("../requirejs/text!./static.css");
+var baseStyles = ".ace_static_highlight {\
+font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'Droid Sans Mono', monospace;\
+font-size: 12px;\
+}\
+.ace_static_highlight .ace_gutter {\
+width: 25px !important;\
+display: block;\
+float: left;\
+text-align: right;\
+padding: 0 3px 0 0;\
+margin-right: 3px;\
+position: static !important;\
+}\
+.ace_static_highlight .ace_line { clear: both; }\
+.ace_static_highlight .ace_gutter-cell {\
+-moz-user-select: -moz-none;\
+-khtml-user-select: none;\
+-webkit-user-select: none;\
+user-select: none;\
+}\
+.ace_static_highlight .ace_gutter-cell:before {\
+content: counter(ace_line, decimal);\
+counter-increment: ace_line;\
+}\
+.ace_static_highlight {\
+counter-reset: ace_line;\
+}\
+";
 var config = require("../config");
 var dom = require("../lib/dom");
 
@@ -79,32 +76,9 @@ var highlight = function(el, opts, callback) {
     });
 };
 
-/**
- * Transforms a given input code snippet into HTML using the given mode
- *
- * @param {string} input Code snippet
- * @param {string|mode} mode String specifying the mode to load such as
- *  `ace/mode/javascript` or, a mode loaded from `/ace/mode`
- *  (use 'ServerSideHiglighter.getMode').
- * @param {string|theme} theme String specifying the theme to load such as
- *  `ace/theme/twilight` or, a theme loaded from `/ace/theme`.
- * @param {number} lineStart A number indicating the first line number. Defaults
- *  to 1.
- * @param {boolean} disableGutter Specifies whether or not to disable the gutter.
- *  `true` disables the gutter, `false` enables the gutter. Defaults to `false`.
- * @param {function} callback When specifying the mode or theme as a string,
- *  this method has no return value and you must specify a callback function. The
- *  callback will receive the rendered object containing the properties `html`
- *  and `css`.
- * @returns {object} An object containing the properties `html` and `css`.
- */
-
 highlight.render = function(input, mode, theme, lineStart, disableGutter, callback) {
-    var waiting = 0;
+    var waiting = 1;
     var modeCache = EditSession.prototype.$modes;
-
-    // if either the theme or the mode were specified as objects
-    // then we need to lazily load them.
     if (typeof theme == "string") {
         waiting++;
         config.loadModule(['theme', theme], function(m) {
@@ -121,23 +95,13 @@ highlight.render = function(input, mode, theme, lineStart, disableGutter, callba
             --waiting || done();
         });
     }
-
-    // loads or passes the specified mode module then calls renderer
     function done() {
         var result = highlight.renderSync(input, mode, theme, lineStart, disableGutter);
         return callback ? callback(result) : result;
     }
-    return waiting || done();
+    return --waiting || done();
 };
 
-/* 
- * Transforms a given input code snippet into HTML using the given mode
- * @param {string} input Code snippet
- * @param {mode} mode Mode loaded from /ace/mode (use 'ServerSideHiglighter.getMode')
- * @param {string} r Code snippet
- * @returns {object} An object containing: html, css
- */
-
 highlight.renderSync = function(input, mode, theme, lineStart, disableGutter) {
     lineStart = parseInt(lineStart || 1, 10);
 
@@ -164,8 +128,6 @@ highlight.renderSync = function(input, mode, theme, lineStart, disableGutter) {
         textLayer.$renderLine(stringBuilder, ix, true, false);
         stringBuilder.push("\n</div>");
     }
-
-    // let's prepare the whole html
     var html = "<div class='" + theme.cssClass + "'>" +
         "<div class='ace_static_highlight' style='counter-reset:ace_line " + (lineStart - 1) + "'>" +
             stringBuilder.join("") +
@@ -184,3 +146,8 @@ highlight.renderSync = function(input, mode, theme, lineStart, disableGutter) {
 module.exports = highlight;
 module.exports.highlight =highlight;
 });
+;
+                (function() {
+                    window.require(["ace/ext/static_highlight"], function() {});
+                })();
+            

+ 7 - 3
services/web/public/js/ace/ext/statusbar.js → services/web/public/js/ace/ext-statusbar.js

@@ -1,6 +1,5 @@
-define(function(require, exports, module) {
+define("ace/ext/statusbar",["require","exports","module","ace/lib/dom","ace/lib/lang"], function(require, exports, module) {
 "use strict";
-/** simple statusbar **/
 var dom = require("ace/lib/dom");
 var lang = require("ace/lib/lang");
 
@@ -46,4 +45,9 @@ var StatusBar = function(editor, parentNode) {
 
 exports.StatusBar = StatusBar;
 
-});
+});
+;
+                (function() {
+                    window.require(["ace/ext/statusbar"], function() {});
+                })();
+            

+ 242 - 168
services/web/public/js/ace/ext/textarea.js → services/web/public/js/ace/ext-textarea.js

@@ -1,34 +1,195 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
+define("ace/theme/textmate",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
+"use strict";
+
+exports.isDark = false;
+exports.cssClass = "ace-tm";
+exports.cssText = ".ace-tm .ace_gutter {\
+background: #f0f0f0;\
+color: #333;\
+}\
+.ace-tm .ace_print-margin {\
+width: 1px;\
+background: #e8e8e8;\
+}\
+.ace-tm .ace_fold {\
+background-color: #6B72E6;\
+}\
+.ace-tm {\
+background-color: #FFFFFF;\
+color: black;\
+}\
+.ace-tm .ace_cursor {\
+color: black;\
+}\
+.ace-tm .ace_invisible {\
+color: rgb(191, 191, 191);\
+}\
+.ace-tm .ace_storage,\
+.ace-tm .ace_keyword {\
+color: blue;\
+}\
+.ace-tm .ace_constant {\
+color: rgb(197, 6, 11);\
+}\
+.ace-tm .ace_constant.ace_buildin {\
+color: rgb(88, 72, 246);\
+}\
+.ace-tm .ace_constant.ace_language {\
+color: rgb(88, 92, 246);\
+}\
+.ace-tm .ace_constant.ace_library {\
+color: rgb(6, 150, 14);\
+}\
+.ace-tm .ace_invalid {\
+background-color: rgba(255, 0, 0, 0.1);\
+color: red;\
+}\
+.ace-tm .ace_support.ace_function {\
+color: rgb(60, 76, 114);\
+}\
+.ace-tm .ace_support.ace_constant {\
+color: rgb(6, 150, 14);\
+}\
+.ace-tm .ace_support.ace_type,\
+.ace-tm .ace_support.ace_class {\
+color: rgb(109, 121, 222);\
+}\
+.ace-tm .ace_keyword.ace_operator {\
+color: rgb(104, 118, 135);\
+}\
+.ace-tm .ace_string {\
+color: rgb(3, 106, 7);\
+}\
+.ace-tm .ace_comment {\
+color: rgb(76, 136, 107);\
+}\
+.ace-tm .ace_comment.ace_doc {\
+color: rgb(0, 102, 255);\
+}\
+.ace-tm .ace_comment.ace_doc.ace_tag {\
+color: rgb(128, 159, 191);\
+}\
+.ace-tm .ace_constant.ace_numeric {\
+color: rgb(0, 0, 205);\
+}\
+.ace-tm .ace_variable {\
+color: rgb(49, 132, 149);\
+}\
+.ace-tm .ace_xml-pe {\
+color: rgb(104, 104, 91);\
+}\
+.ace-tm .ace_entity.ace_name.ace_function {\
+color: #0000A2;\
+}\
+.ace-tm .ace_heading {\
+color: rgb(12, 7, 255);\
+}\
+.ace-tm .ace_list {\
+color:rgb(185, 6, 144);\
+}\
+.ace-tm .ace_meta.ace_tag {\
+color:rgb(0, 22, 142);\
+}\
+.ace-tm .ace_string.ace_regex {\
+color: rgb(255, 0, 0)\
+}\
+.ace-tm .ace_marker-layer .ace_selection {\
+background: rgb(181, 213, 255);\
+}\
+.ace-tm.ace_multiselect .ace_selection.ace_start {\
+box-shadow: 0 0 3px 0px white;\
+border-radius: 2px;\
+}\
+.ace-tm .ace_marker-layer .ace_step {\
+background: rgb(252, 255, 0);\
+}\
+.ace-tm .ace_marker-layer .ace_stack {\
+background: rgb(164, 229, 101);\
+}\
+.ace-tm .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid rgb(192, 192, 192);\
+}\
+.ace-tm .ace_marker-layer .ace_active-line {\
+background: rgba(0, 0, 0, 0.07);\
+}\
+.ace-tm .ace_gutter-active-line {\
+background-color : #dcdcdc;\
+}\
+.ace-tm .ace_marker-layer .ace_selected-word {\
+background: rgb(250, 250, 255);\
+border: 1px solid rgb(200, 200, 250);\
+}\
+.ace-tm .ace_indent-guide {\
+background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\
+}\
+";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});
+
+define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"], function(require, exports, module) {
+"use strict";
+
+require("./lib/fixoldbrowsers");
+
+var dom = require("./lib/dom");
+var event = require("./lib/event");
+
+var Editor = require("./editor").Editor;
+var EditSession = require("./edit_session").EditSession;
+var UndoManager = require("./undomanager").UndoManager;
+var Renderer = require("./virtual_renderer").VirtualRenderer;
+require("./worker/worker_client");
+require("./keyboard/hash_handler");
+require("./placeholder");
+require("./multi_select");
+require("./mode/folding/fold_mode");
+require("./theme/textmate");
+require("./ext/error_marker");
+
+exports.config = require("./config");
+exports.require = require;
+exports.edit = function(el) {
+    if (typeof(el) == "string") {
+        var _id = el;
+        el = document.getElementById(_id);
+        if (!el)
+            throw new Error("ace.edit can't find div #" + _id);
+    }
+
+    if (el.env && el.env.editor instanceof Editor)
+        return el.env.editor;
+
+    var doc = exports.createEditSession(dom.getInnerText(el));
+    el.innerHTML = '';
+
+    var editor = new Editor(new Renderer(el));
+    editor.setSession(doc);
+
+    var env = {
+        document: doc,
+        editor: editor,
+        onResize: editor.resize.bind(editor, null)
+    };
+    event.addListener(window, "resize", env.onResize);
+    editor.on("destroy", function() {
+        event.removeListener(window, "resize", env.onResize);
+    });
+    el.env = editor.env = env;
+    return editor;
+};
+exports.createEditSession = function(text, mode) {
+    var doc = new EditSession(text, mode);
+    doc.setUndoManager(new UndoManager());
+    return doc;
+}
+exports.EditSession = EditSession;
+exports.UndoManager = UndoManager;
+});
+
+define("ace/ext/textarea",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/lib/net","ace/ace","ace/theme/textmate"], function(require, exports, module) {
 "use strict";
 
 var event = require("../lib/event");
@@ -39,16 +200,6 @@ var ace = require("../ace");
 require("../theme/textmate");
 
 module.exports = exports = ace;
-
-/*
- * Returns the CSS property of element.
- *   1) If the CSS property is on the style object of the element, use it, OR
- *   2) Compute the CSS property
- *
- * If the property can't get computed, is 'auto' or 'intrinsic', the former
- * calculated property is used (this can happen in cases where the textarea
- * is hidden and has no dimension styles).
- */
 var getCSSProperty = function(element, container, property) {
     var ret = element.style[property];
 
@@ -78,19 +229,7 @@ function setupContainer(element, getValue) {
     }
 
     var parentNode = element.parentNode;
-
-    // This will hold the editor.
     var container = document.createElement('div');
-
-    // To put Ace in the place of the textarea, we have to copy a few of the
-    // textarea's style attributes to the div container.
-    //
-    // The problem is that the properties have to get computed (they might be
-    // defined by a CSS file on the page - you can't access such rules that
-    // apply to an element via elm.style). Computed properties are converted to
-    // pixels although the dimension might be given as percentage. When the
-    // window resizes, the dimensions defined by percentages changes, so the
-    // properties have to get recomputed to get the new/true pixels.
     var resizeEvent = function() {
         var style = 'position:relative;';
         [
@@ -99,51 +238,20 @@ function setupContainer(element, getValue) {
             style += item + ':' +
                         getCSSProperty(element, container, item) + ';';
         });
-
-        // Calculating the width/height of the textarea is somewhat tricky. To
-        // do it right, you have to include the paddings to the sides as well
-        // (eg. width = width + padding-left, -right).  This works well, as
-        // long as the width of the element is not set or given in pixels. In
-        // this case and after the textarea is hidden, getCSSProperty(element,
-        // container, 'width') will still return pixel value. If the element
-        // has realtiv dimensions (e.g. width='95<percent>')
-        // getCSSProperty(...) will return pixel values only as long as the
-        // textarea is visible. After it is hidden getCSSProperty will return
-        // the relative dimensions as they are set on the element (in the case
-        // of width, 95<percent>).
-        // Making the sum of pixel vaules (e.g. padding) and realtive values
-        // (e.g. <percent>) is not possible. As such the padding styles are
-        // ignored.
-
-        // The complete width is the width of the textarea + the padding
-        // to the left and right.
         var width = getCSSProperty(element, container, 'width') || (element.clientWidth + "px");
         var height = getCSSProperty(element, container, 'height')  || (element.clientHeight + "px");
         style += 'height:' + height + ';width:' + width + ';';
-
-        // Set the display property to 'inline-block'.
         style += 'display:inline-block;';
         container.setAttribute('style', style);
     };
     event.addListener(window, 'resize', resizeEvent);
-
-    // Call the resizeEvent once, so that the size of the container is
-    // calculated.
     resizeEvent();
-
-    // Insert the div container after the element.
     parentNode.insertBefore(container, element.nextSibling);
-
-    // Override the forms onsubmit function. Set the innerHTML and value
-    // of the textarea before submitting.
     while (parentNode !== document) {
         if (parentNode.tagName.toUpperCase() === 'FORM') {
             var oldSumit = parentNode.onsubmit;
-            // Override the onsubmit function of the form.
             parentNode.onsubmit = function(evt) {
                 element.value = getValue();
-                // If there is a onsubmit function already, then call
-                // it with the current context and pass the event.
                 if (oldSumit) {
                     oldSumit.call(this, evt);
                 }
@@ -155,17 +263,13 @@ function setupContainer(element, getValue) {
     return container;
 }
 
-exports.transformTextarea = function(element, loader) {
+exports.transformTextarea = function(element, options) {
     var session;
     var container = setupContainer(element, function() {
         return session.getValue();
     });
-
-    // Hide the element.
     element.style.display = 'none';
     container.style.background = 'white';
-
-    //
     var editorDiv = document.createElement("div");
     applyStyles(editorDiv, {
         top: "0px",
@@ -215,23 +319,15 @@ exports.transformTextarea = function(element, loader) {
     applyStyles(settingDiv, settingDivStyles);
     container.appendChild(settingDiv);
 
-    // Power up ace on the textarea:
-    var options = {};
-
+    options = options || exports.defaultOptions;
     var editor = ace.edit(editorDiv);
     session = editor.getSession();
 
     session.setValue(element.value || element.innerHTML);
     editor.focus();
-
-    // Add the settingPanel opener to the editor's div.
     container.appendChild(settingOpener);
-
-    // Create the API.
-    setupApi(editor, editorDiv, settingDiv, ace, options, loader);
-
-    // Create the setting's panel.
-    setupSettingPanel(settingDiv, settingOpener, editor, options);
+    setupApi(editor, editorDiv, settingDiv, ace, options, load);
+    setupSettingPanel(settingDiv, settingOpener, editor);
 
     var state = "";
     event.addListener(settingOpener, "mousemove", function(e) {
@@ -296,37 +392,15 @@ function setupApi(editor, editorDiv, settingDiv, ace, options, loader) {
     };
 
     editor.$setOption = editor.setOption;
+    editor.$getOption = editor.getOption;
     editor.setOption = function(key, value) {
-        if (options[key] == value) return;
-
         switch (key) {
             case "mode":
-                if (value != "text") {
-                    // Load the required mode file. Files get loaded only once.
-                    loader("mode-" + value + ".js", "ace/mode/" + value, function() {
-                        var aceMode = require("../mode/" + value).Mode;
-                        session.setMode(new aceMode());
-                    });
-                } else {
-                    session.setMode(new (require("../mode/text").Mode));
-                }
+                editor.$setOption("mode", "ace/mode/" + value)
             break;
-
             case "theme":
-                if (value != "textmate") {
-                    // Load the required theme file. Files get loaded only once.
-                    loader("theme-" + value + ".js", "ace/theme/" + value, function() {
-                        editor.setTheme("ace/theme/" + value);
-                    });
-                } else {
-                    editor.setTheme("ace/theme/textmate");
-                }
+                editor.$setOption("theme", "ace/theme/" + value)
             break;
-
-            case "fontSize":
-                editorDiv.style.fontSize = value;
-            break;
-
             case "keybindings":
                 switch (value) {
                     case "vim":
@@ -341,58 +415,55 @@ function setupApi(editor, editorDiv, settingDiv, ace, options, loader) {
             break;
 
             case "softWrap":
-                switch (value) {
-                    case "off":
-                        session.setUseWrapMode(false);
-                        renderer.setPrintMarginColumn(80);
-                    break;
-                    case "40":
-                        session.setUseWrapMode(true);
-                        session.setWrapLimitRange(40, 40);
-                        renderer.setPrintMarginColumn(40);
-                    break;
-                    case "80":
-                        session.setUseWrapMode(true);
-                        session.setWrapLimitRange(80, 80);
-                        renderer.setPrintMarginColumn(80);
-                    break;
-                    case "free":
-                        session.setUseWrapMode(true);
-                        session.setWrapLimitRange(null, null);
-                        renderer.setPrintMarginColumn(80);
-                    break;
-                }
+            case "fontSize":
+                editor.$setOption(key, value);
             break;
             
             default:
                 editor.$setOption(key, toBool(value));
         }
-
-        options[key] = value;
     };
 
     editor.getOption = function(key) {
-        return options[key];
-    };
+        switch (key) {
+            case "mode":
+                return editor.$getOption("mode").substr("ace/mode/".length)
+            break;
 
-    editor.getOptions = function() {
-        return options;
-    };
+            case "theme":
+                return editor.$getOption("theme").substr("ace/theme/".length)
+            break;
 
-    editor.setOptions(exports.options);
+            case "keybindings":
+                var value = editor.getKeyboardHandler()
+                switch (value && value.$id) {
+                    case "ace/keyboard/vim":
+                        return "vim";
+                    case "ace/keyboard/emacs":
+                        return "emacs";
+                    default:
+                        return "ace";
+                }
+            break;
 
+            default:
+                return editor.$getOption(key);
+        }
+    };
+
+    editor.setOptions(options);
     return editor;
 }
 
-function setupSettingPanel(settingDiv, settingOpener, editor, options) {
+function setupSettingPanel(settingDiv, settingOpener, editor) {
     var BOOL = null;
 
     var desc = {
         mode:            "Mode:",
-        gutter:          "Display Gutter:",
+        wrap:            "Soft Wrap:",
         theme:           "Theme:",
         fontSize:        "Font Size:",
-        softWrap:        "Soft Wrap:",
+        showGutter:      "Display Gutter:",
         keybindings:     "Keyboard",
         showPrintMargin: "Show Print Margin:",
         useSoftTabs:     "Use Soft Tabs:",
@@ -445,7 +516,7 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
             twilight:         "Twilight",
             vibrant_ink:      "Vibrant Ink"
         },
-        gutter: BOOL,
+        showGutter: BOOL,
         fontSize: {
             "10px": "10px",
             "11px": "11px",
@@ -453,7 +524,7 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
             "14px": "14px",
             "16px": "16px"
         },
-        softWrap: {
+        wrap: {
             off:    "Off",
             40:     "40",
             80:     "80",
@@ -476,7 +547,7 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
         if (!obj) {
             builder.push(
                 "<input type='checkbox' title='", option, "' ",
-                    cValue == "true" ? "checked='true'" : "",
+                    cValue + "" == "true" ? "checked='true'" : "",
                "'></input>"
             );
             return;
@@ -496,10 +567,10 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
         builder.push("</select>");
     }
 
-    for (var option in options) {
+    for (var option in exports.defaultOptions) {
         table.push("<tr><td>", desc[option], "</td>");
         table.push("<td>");
-        renderOption(table, option, optionValues[option], options[option]);
+        renderOption(table, option, optionValues[option], editor.getOption(option));
         table.push("</td></tr>");
     }
     table.push("</table>");
@@ -530,14 +601,12 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
     settingDiv.appendChild(button);
     settingDiv.hideButton = button;
 }
-
-// Default startup options.
-exports.options = {
-    mode:               "text",
+exports.defaultOptions = {
+    mode:               "javascript",
     theme:              "textmate",
-    gutter:             "false",
+    wrap:               "off",
     fontSize:           "12px",
-    softWrap:           "off",
+    showGutter:         "false",
     keybindings:        "ace",
     showPrintMargin:    "false",
     useSoftTabs:        "true",
@@ -545,3 +614,8 @@ exports.options = {
 };
 
 });
+;
+                (function() {
+                    window.require(["ace/ext/textarea"], function() {});
+                })();
+            

+ 59 - 0
services/web/public/js/ace/ext-themelist.js

@@ -0,0 +1,59 @@
+define("ace/ext/themelist",["require","exports","module","ace/lib/fixoldbrowsers"], function(require, exports, module) {
+"use strict";
+require("ace/lib/fixoldbrowsers");
+
+var themeData = [
+    ["Chrome"         ],
+    ["Clouds"         ],
+    ["Crimson Editor" ],
+    ["Dawn"           ],
+    ["Dreamweaver"    ],
+    ["Eclipse"        ],
+    ["GitHub"         ],
+    ["Solarized Light"],
+    ["TextMate"       ],
+    ["Tomorrow"       ],
+    ["XCode"          ],
+    ["Kuroir"],
+    ["KatzenMilch"],
+    ["Ambiance"             ,"ambiance"                ,  "dark"],
+    ["Chaos"                ,"chaos"                   ,  "dark"],
+    ["Clouds Midnight"      ,"clouds_midnight"         ,  "dark"],
+    ["Cobalt"               ,"cobalt"                  ,  "dark"],
+    ["idle Fingers"         ,"idle_fingers"            ,  "dark"],
+    ["krTheme"              ,"kr_theme"                ,  "dark"],
+    ["Merbivore"            ,"merbivore"               ,  "dark"],
+    ["Merbivore Soft"       ,"merbivore_soft"          ,  "dark"],
+    ["Mono Industrial"      ,"mono_industrial"         ,  "dark"],
+    ["Monokai"              ,"monokai"                 ,  "dark"],
+    ["Pastel on dark"       ,"pastel_on_dark"          ,  "dark"],
+    ["Solarized Dark"       ,"solarized_dark"          ,  "dark"],
+    ["Terminal"             ,"terminal"                ,  "dark"],
+    ["Tomorrow Night"       ,"tomorrow_night"          ,  "dark"],
+    ["Tomorrow Night Blue"  ,"tomorrow_night_blue"     ,  "dark"],
+    ["Tomorrow Night Bright","tomorrow_night_bright"   ,  "dark"],
+    ["Tomorrow Night 80s"   ,"tomorrow_night_eighties" ,  "dark"],
+    ["Twilight"             ,"twilight"                ,  "dark"],
+    ["Vibrant Ink"          ,"vibrant_ink"             ,  "dark"]
+];
+
+
+exports.themesByName = {};
+exports.themes = themeData.map(function(data) {
+    var name = data[1] || data[0].replace(/ /g, "_").toLowerCase();
+    var theme = {
+        caption: data[0],
+        theme: "ace/theme/" + name,
+        isDark: data[2] == "dark",
+        name: name
+    };
+    exports.themesByName[name] = theme;
+    return theme;
+});
+
+});
+;
+                (function() {
+                    window.require(["ace/ext/themelist"], function() {});
+                })();
+            

+ 11 - 44
services/web/public/js/ace/ext/whitespace.js → services/web/public/js/ace/ext-whitespace.js

@@ -1,39 +1,7 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Distributed under the BSD license:
- *
- * Copyright (c) 2010, Ajax.org B.V.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of Ajax.org B.V. nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define(function(require, exports, module) {
+define("ace/ext/whitespace",["require","exports","module","ace/lib/lang"], function(require, exports, module) {
 "use strict";
 
 var lang = require("../lib/lang");
-
-// based on http://www.freehackers.org/Indent_Finder
 exports.$detectIndentation = function(lines, fallback) {
     var stats = [];
     var changes = [];
@@ -42,11 +10,9 @@ exports.$detectIndentation = function(lines, fallback) {
     var max = Math.min(lines.length, 1000);
     for (var i = 0; i < max; i++) {
         var line = lines[i];
-        // ignore empty and comment lines
         if (!/^\s*[^*+\-\s]/.test(line))
             continue;
 
-        var tabs = line.match(/^\t*/)[0].length;
         if (line[0] == "\t")
             tabIndents++;
 
@@ -59,12 +25,10 @@ exports.$detectIndentation = function(lines, fallback) {
             stats[spaces] = (stats[spaces] || 0) + 1;
         }
         prevSpaces = spaces;
-
-        // ignore lines ending with backslash
-        while (line[line.length - 1] == "\\")
+        while (i < max && line[line.length - 1] == "\\")
             line = lines[i++];
     }
-
+    
     function getScore(indent) {
         var score = 0;
         for (var i = indent; i < stats.length; i += indent)
@@ -79,13 +43,12 @@ exports.$detectIndentation = function(lines, fallback) {
     for (var i = 1; i < 12; i++) {
         if (i == 1) {
             spaceIndents = getScore(i);
-            var score = 1;
+            var score = stats.length && 1;
         } else
             var score = getScore(i) / spaceIndents;
 
-        if (changes[i]) {
+        if (changes[i])
             score += changes[i] / changesTotal;
-        }
 
         if (score > first.score)
             first = {score: score, length: i};
@@ -97,7 +60,7 @@ exports.$detectIndentation = function(lines, fallback) {
     if (tabIndents > spaceIndents + 1)
         return {ch: "\t", length: tabLength};
 
-    if (spaceIndents + 1 > tabIndents)
+    if (spaceIndents > tabIndents + 1)
         return {ch: " ", length: tabLength};
 };
 
@@ -187,7 +150,6 @@ exports.commands = [{
     name: "detectIndentation",
     exec: function(editor) {
         exports.detectIndentation(editor.session);
-        // todo show message?
     }
 }, {
     name: "trimTrailingSpace",
@@ -210,3 +172,8 @@ exports.commands = [{
 }];
 
 });
+;
+                (function() {
+                    window.require(["ace/ext/whitespace"], function() {});
+                })();
+            

Некоторые файлы не были показаны из-за большого количества измененных файлов