liquid_cms 0.2.0.11 → 0.2.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. data/CHANGELOG.rdoc +7 -0
  2. data/TODO.rdoc +1 -1
  3. data/app/controllers/cms/main_controller.rb +3 -2
  4. data/app/helpers/cms/common_helper.rb +9 -2
  5. data/app/helpers/cms/components_helper.rb +10 -4
  6. data/app/models/cms/component.rb +4 -0
  7. data/app/views/cms/assets/_list.html.erb +4 -4
  8. data/app/views/cms/components/_list.html.erb +5 -1
  9. data/app/views/cms/pages/_list.html.erb +4 -4
  10. data/app/views/cms/shared/_sidebar.html.erb +32 -8
  11. data/app/views/layouts/cms.html.erb +5 -2
  12. data/generators/liquid_cms/templates/config/locales/cms/en.yml +3 -2
  13. data/generators/liquid_cms/templates/public/cms/codemirror/LICENSE +0 -0
  14. data/generators/liquid_cms/templates/public/cms/codemirror/css/csscolors.css +0 -0
  15. data/generators/liquid_cms/templates/public/cms/codemirror/css/docs.css +17 -3
  16. data/generators/liquid_cms/templates/public/cms/codemirror/css/font.js +15 -0
  17. data/generators/liquid_cms/templates/public/cms/codemirror/css/jscolors.css +0 -0
  18. data/generators/liquid_cms/templates/public/cms/codemirror/css/sparqlcolors.css +0 -0
  19. data/generators/liquid_cms/templates/public/cms/codemirror/css/xmlcolors.css +0 -0
  20. data/generators/liquid_cms/templates/public/cms/codemirror/js/codemirror.js +59 -26
  21. data/generators/liquid_cms/templates/public/cms/codemirror/js/editor.js +149 -71
  22. data/generators/liquid_cms/templates/public/cms/codemirror/js/highlight.js +2 -2
  23. data/generators/liquid_cms/templates/public/cms/codemirror/js/mirrorframe.js +2 -2
  24. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsecss.js +5 -3
  25. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsedummy.js +0 -0
  26. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsehtmlmixed.js +28 -9
  27. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsejavascript.js +0 -0
  28. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsesparql.js +0 -0
  29. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsexml.js +6 -1
  30. data/generators/liquid_cms/templates/public/cms/codemirror/js/select.js +48 -21
  31. data/generators/liquid_cms/templates/public/cms/codemirror/js/stringstream.js +15 -1
  32. data/generators/liquid_cms/templates/public/cms/codemirror/js/tokenize.js +0 -0
  33. data/generators/liquid_cms/templates/public/cms/codemirror/js/tokenizejavascript.js +1 -1
  34. data/generators/liquid_cms/templates/public/cms/codemirror/js/undo.js +17 -14
  35. data/generators/liquid_cms/templates/public/cms/codemirror/js/unittests.js +44 -0
  36. data/generators/liquid_cms/templates/public/cms/codemirror/js/util.js +6 -3
  37. data/generators/liquid_cms/templates/public/cms/javascripts/cms.js +15 -1
  38. data/generators/liquid_cms/templates/public/cms/javascripts/livepipe.js +181 -0
  39. data/generators/liquid_cms/templates/public/cms/javascripts/tabs.js +149 -0
  40. data/generators/liquid_cms/templates/public/cms/stylesheets/ie9.css +4 -0
  41. data/generators/liquid_cms/templates/public/cms/stylesheets/sidebar.css +132 -0
  42. data/generators/liquid_cms/templates/public/cms/stylesheets/styles.css +1 -74
  43. data/generators/liquid_cms/templates/public/cms/stylesheets/themes/dark.css +2 -1
  44. data/lib/liquid_cms/context.rb +4 -0
  45. data/lib/liquid_cms/version.rb +1 -1
  46. data/liquid_cms.gemspec +1 -1
  47. data/test/functional/assets_controller_test.rb +3 -3
  48. data/test/rails_app/config/locales/cms/en.yml +8 -0
  49. metadata +11 -16
  50. data/generators/liquid_cms/templates/public/cms/codemirror/bigtest.html +0 -1296
  51. data/generators/liquid_cms/templates/public/cms/codemirror/css/people.jpg +0 -0
  52. data/generators/liquid_cms/templates/public/cms/codemirror/csstest.html +0 -60
  53. data/generators/liquid_cms/templates/public/cms/codemirror/highlight.html +0 -82
  54. data/generators/liquid_cms/templates/public/cms/codemirror/htmltest.html +0 -52
  55. data/generators/liquid_cms/templates/public/cms/codemirror/index.html +0 -245
  56. data/generators/liquid_cms/templates/public/cms/codemirror/jstest.html +0 -56
  57. data/generators/liquid_cms/templates/public/cms/codemirror/manual.html +0 -759
  58. data/generators/liquid_cms/templates/public/cms/codemirror/mixedtest.html +0 -52
  59. data/generators/liquid_cms/templates/public/cms/codemirror/sparqltest.html +0 -41
  60. data/generators/liquid_cms/templates/public/cms/codemirror/story.html +0 -671
@@ -1,5 +1,12 @@
1
1
  == 0.2.0.11
2
2
 
3
+ * Enhancements
4
+ * Upgrade codemirror syntax highlighting to 0.94
5
+ * Remember component folders view state.
6
+ * Add tabs for each resource type.
7
+
8
+ == 0.2.0.11
9
+
3
10
  * Fixes
4
11
  * Fix translation issue in forms.
5
12
 
data/TODO.rdoc CHANGED
@@ -3,6 +3,6 @@
3
3
  * No functions are currently limited to specific user roles. All CMS users can perform the same actions.
4
4
  * Implement caching and expiration logic.
5
5
  * Provide example apache/nginx/etc. conf files that properly scope cms assets for each context.
6
- * Generate CMS documentation from rdoc comments in source files.
6
+ * Generate CMS documentation from rdoc comments in source files?
7
7
  * Search functions. Find templates based on search text.
8
8
  * Missing tests... add more coverage.
@@ -4,7 +4,7 @@ class Cms::MainController < Cms::SetupController
4
4
 
5
5
  layout 'cms'
6
6
 
7
- before_filter :load_pages_and_assets
7
+ before_filter :load_resources
8
8
 
9
9
  authenticate_user :all, :only => %w(index)
10
10
 
@@ -13,10 +13,11 @@ class Cms::MainController < Cms::SetupController
13
13
  end
14
14
 
15
15
  protected
16
- def load_pages_and_assets
16
+ def load_resources
17
17
  @context = Cms::Context.new(@cms_context)
18
18
 
19
19
  @pages = @context.pages.ordered.all(:conditions => {:layout_page_id => nil})
20
20
  @assets = @context.assets.ordered
21
+ @components = @context.components
21
22
  end
22
23
  end
@@ -1,4 +1,12 @@
1
1
  module Cms::CommonHelper
2
+ # js cookie accessor
3
+ def cookie_jar(key)
4
+ # if the cookie hasn't been set, need to return '{}' to return a proper hash
5
+ # and if the cookie has been set, but is 'null', we need to then return a ruby hash
6
+ # __CJ_ is the cookiejar js lib postfix value for cookies
7
+ ActiveSupport::JSON.decode(cookies["__CJ_#{key}".to_sym] || '{}') || {}
8
+ end
9
+
2
10
  def cms_icon(name, options = {})
3
11
  image_tag "/cms/images/icons/#{name}", options.merge(:size => '16x16')
4
12
  end
@@ -94,7 +102,6 @@ module Cms::CommonHelper
94
102
  end
95
103
 
96
104
  def asset_preview_option
97
- # __CJ_ is the cookiejar js lib postfix value for cookies
98
- ActiveSupport::JSON.decode(cookies[:__CJ_toggle] || '{}')['on'] == false ? 'style="display:none"' : ''
105
+ cookie_jar('toggle')['on'] == false ? 'style="display:none"' : ''
99
106
  end
100
107
  end
@@ -1,4 +1,8 @@
1
1
  module Cms::ComponentsHelper
2
+ def component_folder_open?(folder_id)
3
+ cookie_jar('component_folders')[folder_id].present?
4
+ end
5
+
2
6
  def component_edit_link(path)
3
7
  full_path = Cms::Component.component_path(@context, path)
4
8
  link_to(truncate(File.basename(path), :length => 15), {:controller => 'cms/components', :action => 'edit', :url => CGI::escape(full_path)})
@@ -9,15 +13,17 @@ module Cms::ComponentsHelper
9
13
  link_to(cms_icon('delete.png', :title => 'Delete'), {:controller => 'cms/components', :action => 'destroy', :url => CGI::escape(full_path)}, :confirm => "Are you sure you want to remove '#{full_path}'?")
10
14
  end
11
15
 
12
- def list_files(path, hidden = false)
16
+ def list_files(files, hidden = false)
13
17
  html = ''
14
18
  html += hidden ? %[<ul class="tree" style="display:none">] : %[<ul class="tree">]
15
- for file in Dir[File.expand_path(path)+"/*"] do
19
+ for file in files do
16
20
  html += "<li>"
17
21
  if File.directory?(file)
18
- html += cms_icon('folder.png', :class => 'folder') + ' ' + component_delete_link(file) + ' '
22
+ folder_id = "folder_#{Digest::MD5.hexdigest(file)}"
23
+
24
+ html += cms_icon('folder.png', :class => 'folder', :id => folder_id) + ' ' + component_delete_link(file) + ' '
19
25
  html += content_tag(:span, File.basename(file), :title => Cms::Component.component_path(@context, file))
20
- html += list_files(file, true)
26
+ html += list_files(Cms::Component.files(file), !component_folder_open?(folder_id))
21
27
  else
22
28
  html += file_type_icon(File.basename(file)) + ' '
23
29
  html += component_delete_link(file) + ' '
@@ -21,6 +21,10 @@ class Cms::Component
21
21
  path.sub(full_path(context).to_s + "/", '')
22
22
  end
23
23
 
24
+ def self.files(path)
25
+ Dir[File.expand_path(path) + "/*"]
26
+ end
27
+
24
28
  def self.valid_type?(file)
25
29
  %w(.css .js .png .jpg .jpeg .gif .json .xml .fla .ico).include?(File.extname(file).downcase)
26
30
  end
@@ -1,4 +1,8 @@
1
1
  <div id="assets">
2
+ <p>
3
+ <%= cms_icon 'picture_add.png' %> <%= link_to t('assets.actions.index.new_link'), new_cms_asset_path %>
4
+ </p>
5
+
2
6
  <% if @assets.empty? %>
3
7
  <p><%= t 'assets.actions.index.none' %></p>
4
8
  <% else %>
@@ -7,8 +11,4 @@
7
11
  <%= render :partial => 'cms/assets/asset', :collection => @assets %>
8
12
  </ul>
9
13
  <% end %>
10
-
11
- <p>
12
- <%= cms_icon 'picture_add.png' %> <%= link_to t('assets.actions.index.new_link'), new_cms_asset_path %>
13
- </p>
14
14
  </div>
@@ -4,5 +4,9 @@
4
4
  <%= submit_tag 'Upload' %> <em>.zip files only</em>
5
5
  <% end %>
6
6
 
7
- <%= list_files Cms::Component.full_path(@context) %>
7
+ <% if @components.empty? %>
8
+ <p><%= t 'components.actions.index.none' %></p>
9
+ <% else %>
10
+ <%= list_files @components %>
11
+ <% end %>
8
12
  </div>
@@ -1,4 +1,8 @@
1
1
  <div id="pages">
2
+ <p>
3
+ <%= cms_icon 'page_add.png' %> <%= link_to t('pages.actions.index.new_link'), new_cms_page_path %>
4
+ </p>
5
+
2
6
  <% if @pages.empty? %>
3
7
  <p><%= t 'pages.actions.index.none' %></p>
4
8
  <% else %>
@@ -6,8 +10,4 @@
6
10
  <%= render :partial => 'cms/pages/page', :collection => @pages %>
7
11
  </ul>
8
12
  <% end %>
9
-
10
- <p>
11
- <%= cms_icon 'page_add.png' %> <%= link_to t('pages.actions.index.new_link'), new_cms_page_path %>
12
- </p>
13
13
  </div>
@@ -1,11 +1,35 @@
1
- <h2><%= t 'pages.actions.index.title' %></h2>
2
- <%= render 'cms/pages/list' %>
1
+ <ul id="cms_menus" class="tabs clearfix">
2
+ <li><%= link_to t('pages.actions.index.title'), '#cms_pages' %></li>
3
+ <li><%= link_to t('assets.actions.index.title'), '#cms_assets' %></li>
4
+ <li><%= link_to t('components.actions.index.title'), '#cms_components' %></li>
5
+ </ul>
3
6
 
4
- <h2><%= t 'assets.actions.index.title' %></h2>
5
- <%= render 'cms/assets/list' %>
7
+ <div id="tab_container">
8
+ <div id="cms_pages">
9
+ <h2><%= t 'pages.actions.index.title' %></h2>
10
+ <%= render 'cms/pages/list' %>
11
+ </div>
6
12
 
7
- <h2><%= t 'components.actions.index.title' %></h2>
8
- <%= render 'cms/components/list' %>
13
+ <div id="cms_assets">
14
+ <h2><%= t 'assets.actions.index.title' %></h2>
15
+ <%= render 'cms/assets/list' %>
16
+ </div>
17
+
18
+ <div id="cms_components">
19
+ <h2><%= t 'components.actions.index.title' %></h2>
20
+ <%= render 'cms/components/list' %>
21
+ </div>
22
+ </div>
23
+
24
+ <% javascript_tag do %>
25
+ new Control.Tabs('cms_menus', {
26
+ defaultTab: jar.get('active_tab') || 'first',
27
+ afterChange: function(new_container) {
28
+ // save the active tab
29
+ jar.put('active_tab', new_container.getAttribute('id'));
30
+ }
31
+ });
32
+ <% end %>
33
+
34
+ <p class="documentation"><%= cms_icon 'page.png' %> <%= link_to 'Documentation', cms_documentation_path %></p>
9
35
 
10
- <hr style="border:1px solid #BBB" />
11
- <p style="text-align:right"><%= cms_icon 'page.png' %> <%= link_to 'Documentation', cms_documentation_path %></p>
@@ -5,15 +5,18 @@
5
5
  <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
6
6
  <title>Liquid CMS</title>
7
7
  <%= javascript_include_tag 'prototype', 'effects' %>
8
- <%= javascript_include_tag '/cms/javascripts/humanmsg', '/cms/javascripts/cookiejar', '/cms/javascripts/cms' %>
8
+ <%= javascript_include_tag '/cms/javascripts/humanmsg', '/cms/javascripts/cookiejar', '/cms/javascripts/cms', '/cms/javascripts/livepipe', '/cms/javascripts/tabs', :cache => 'cms_scripts' %>
9
9
  <%= javascript_include_tag '/cms/codemirror/js/codemirror' %>
10
10
  <%= stylesheet_link_tag '/cms/stylesheets/simple_form' %>
11
- <%= stylesheet_link_tag '/cms/stylesheets/clearfix', '/cms/stylesheets/humanmsg', '/cms/stylesheets/styles' %>
11
+ <%= stylesheet_link_tag '/cms/stylesheets/clearfix', '/cms/stylesheets/humanmsg', '/cms/stylesheets/styles', '/cms/stylesheets/sidebar', :cache => 'cms_styles' %>
12
12
  <%= stylesheet_link_tag '/cms/stylesheets/themes/dark' %>
13
13
  <%= yield :cms_styles %>
14
14
  <!--[if lte IE 8]>
15
15
  <%= stylesheet_link_tag '/cms/stylesheets/ie' %>
16
16
  <![endif]-->
17
+ <!--[if lte IE 9]>
18
+ <%= stylesheet_link_tag '/cms/stylesheets/ie9' %>
19
+ <![endif]-->
17
20
  </head>
18
21
  <body id="cms" class="cms_<%= params[:controller].camelize.demodulize.downcase %>">
19
22
  <div id="header">
@@ -2,7 +2,7 @@ en:
2
2
  pages:
3
3
  actions:
4
4
  index:
5
- title: 'Site Pages'
5
+ title: 'Pages'
6
6
  none: 'There are currently no pages.'
7
7
  new_link: 'Create a new page'
8
8
  new:
@@ -17,7 +17,7 @@ en:
17
17
  assets:
18
18
  actions:
19
19
  index:
20
- title: 'Site Assets'
20
+ title: 'Assets'
21
21
  none: 'There are currently no assets.'
22
22
  new_link: 'Upload a new asset'
23
23
  new:
@@ -33,6 +33,7 @@ en:
33
33
  actions:
34
34
  index:
35
35
  title: 'Components'
36
+ none: 'There are currently no components.'
36
37
 
37
38
  simple_form:
38
39
  "yes": 'Yes'
@@ -1,10 +1,13 @@
1
1
  body {
2
- font-family: Droid Sans, Arial, sans-serif;
2
+ font-family: Arial, sans-serif;
3
3
  line-height: 1.5;
4
4
  max-width: 64.3em;
5
5
  margin: 3em auto;
6
6
  padding: 0 1em;
7
7
  }
8
+ body.droid {
9
+ font-family: Droid Sans, Arial, sans-serif;
10
+ }
8
11
 
9
12
  h1 {
10
13
  letter-spacing: -3px;
@@ -27,7 +30,7 @@ h3 {
27
30
  }
28
31
 
29
32
  pre {
30
- font-family: Droid Sans Mono, Courier New, monospaced;
33
+ font-family: Courier New, monospaced;
31
34
  background-color: #eee;
32
35
  -moz-border-radius: 6px;
33
36
  -webkit-border-radius: 6px;
@@ -44,6 +47,13 @@ pre.code {
44
47
  padding: .5em 1em;
45
48
  line-height: 1.2em;
46
49
  margin-top: .5em;
50
+ position: relative;
51
+ }
52
+
53
+ img.logo {
54
+ position: absolute;
55
+ right: -25px;
56
+ bottom: 4px;
47
57
  }
48
58
 
49
59
  a:link, a:visited, .quasilink {
@@ -52,10 +62,14 @@ a:link, a:visited, .quasilink {
52
62
  text-decoration: none;
53
63
  }
54
64
 
55
- a:hover {
65
+ a:hover, .quasilink:hover {
56
66
  color: #800004;
57
67
  }
58
68
 
69
+ h1 a:link, h1 a:visited, h1 a:hover {
70
+ color: black;
71
+ }
72
+
59
73
  ul {
60
74
  margin: 0;
61
75
  padding-left: 1.2em;
@@ -0,0 +1,15 @@
1
+ function waitForStyles() {
2
+ for (var i = 0; i < document.styleSheets.length; i++)
3
+ if (/googleapis/.test(document.styleSheets[i].href))
4
+ return document.body.className += " droid";
5
+ setTimeout(waitForStyles, 100);
6
+ }
7
+ setTimeout(function() {
8
+ if (/AppleWebKit/.test(navigator.userAgent) && /iP[oa]d|iPhone/.test(navigator.userAgent)) return;
9
+ var link = document.createElement("LINK");
10
+ link.type = "text/css";
11
+ link.rel = "stylesheet";
12
+ link.href = "http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold";
13
+ document.documentElement.getElementsByTagName("HEAD")[0].appendChild(link);
14
+ waitForStyles();
15
+ }, 20);
@@ -21,6 +21,12 @@ var CodeMirror = (function(){
21
21
  for (var i = 0; i < array.length; i++)
22
22
  action(array[i]);
23
23
  }
24
+ function createHTMLElement(el) {
25
+ if (document.createElementNS && document.documentElement.namespaceURI !== null)
26
+ return document.createElementNS("http://www.w3.org/1999/xhtml", el)
27
+ else
28
+ return document.createElement(el)
29
+ }
24
30
 
25
31
  // These default options can be overridden by passing a set of
26
32
  // options to a specific CodeMirror constructor. See manual.html for
@@ -37,6 +43,7 @@ var CodeMirror = (function(){
37
43
  lineNumberTime: 50,
38
44
  continuousScanning: false,
39
45
  saveFunction: null,
46
+ onLoad: null,
40
47
  onChange: null,
41
48
  undoDepth: 50,
42
49
  undoDelay: 800,
@@ -47,22 +54,27 @@ var CodeMirror = (function(){
47
54
  height: "300px",
48
55
  minHeight: 100,
49
56
  autoMatchParens: false,
57
+ markParen: null,
58
+ unmarkParen: null,
50
59
  parserConfig: null,
51
60
  tabMode: "indent", // or "spaces", "default", "shift"
52
61
  enterMode: "indent", // or "keep", "flat"
53
62
  electricChars: true,
54
63
  reindentOnLoad: false,
55
64
  activeTokens: null,
56
- cursorActivity: null,
65
+ onCursorActivity: null,
57
66
  lineNumbers: false,
58
67
  firstLineNumber: 1,
68
+ onLineNumberClick: null,
59
69
  indentUnit: 2,
60
- domain: null
70
+ domain: null,
71
+ noScriptCaching: false,
72
+ incrementalLoading: false
61
73
  });
62
74
 
63
75
  function addLineNumberDiv(container, firstNum) {
64
- var nums = document.createElement("DIV"),
65
- scroller = document.createElement("DIV");
76
+ var nums = createHTMLElement("div"),
77
+ scroller = createHTMLElement("div");
66
78
  nums.style.position = "absolute";
67
79
  nums.style.height = "100%";
68
80
  if (nums.style.setExpression) {
@@ -87,18 +99,19 @@ var CodeMirror = (function(){
87
99
  if (typeof options.stylesheet == "string")
88
100
  options.stylesheet = [options.stylesheet];
89
101
 
90
- var html = ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><head>"];
102
+ var sp = " spellcheck=\"" + (options.disableSpellcheck ? "false" : "true") + "\"";
103
+ var html = ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html" + sp + "><head>"];
91
104
  // Hack to work around a bunch of IE8-specific problems.
92
105
  html.push("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\"/>");
106
+ var queryStr = options.noScriptCaching ? "?nocache=" + new Date().getTime().toString(16) : "";
93
107
  forEach(options.stylesheet, function(file) {
94
- html.push("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file + "\"/>");
108
+ html.push("<link rel=\"stylesheet\" type=\"text/css\" href=\"" + file + queryStr + "\"/>");
95
109
  });
96
110
  forEach(options.basefiles.concat(options.parserfile), function(file) {
97
111
  if (!/^https?:/.test(file)) file = options.path + file;
98
- html.push("<script type=\"text/javascript\" src=\"" + file + "\"><" + "/script>");
112
+ html.push("<script type=\"text/javascript\" src=\"" + file + queryStr + "\"><" + "/script>");
99
113
  });
100
- html.push("</head><body style=\"border-width: 0;\" class=\"editbox\" spellcheck=\"" +
101
- (options.disableSpellcheck ? "false" : "true") + "\"></body></html>");
114
+ html.push("</head><body style=\"border-width: 0;\" class=\"editbox\"" + sp + "></body></html>");
102
115
  return html.join("");
103
116
  }
104
117
 
@@ -112,8 +125,9 @@ var CodeMirror = (function(){
112
125
  // Backward compatibility for deprecated options.
113
126
  if (options.dumbTabs) options.tabMode = "spaces";
114
127
  else if (options.normalTab) options.tabMode = "default";
128
+ if (options.cursorActivity) options.onCursorActivity = options.cursorActivity;
115
129
 
116
- var frame = this.frame = document.createElement("IFRAME");
130
+ var frame = this.frame = createHTMLElement("iframe");
117
131
  if (options.iframeClass) frame.className = options.iframeClass;
118
132
  frame.frameBorder = 0;
119
133
  frame.style.border = "0";
@@ -123,17 +137,18 @@ var CodeMirror = (function(){
123
137
  // always add it, redundant as it sounds.
124
138
  frame.style.display = "block";
125
139
 
126
- var div = this.wrapping = document.createElement("DIV");
140
+ var div = this.wrapping = createHTMLElement("div");
127
141
  div.style.position = "relative";
128
142
  div.className = "CodeMirror-wrapping";
129
143
  div.style.width = options.width;
130
144
  div.style.height = (options.height == "dynamic") ? options.minHeight + "px" : options.height;
131
145
  // This is used by Editor.reroutePasteEvent
132
- var teHack = this.textareaHack = document.createElement("TEXTAREA");
146
+ var teHack = this.textareaHack = createHTMLElement("textarea");
133
147
  div.appendChild(teHack);
134
148
  teHack.style.position = "absolute";
135
149
  teHack.style.left = "-10000px";
136
150
  teHack.style.width = "10px";
151
+ teHack.tabIndex = 100000;
137
152
 
138
153
  // Link back to this object, so that the editor can fetch options
139
154
  // and add a reference to itself.
@@ -163,7 +178,9 @@ var CodeMirror = (function(){
163
178
 
164
179
  CodeMirror.prototype = {
165
180
  init: function() {
181
+ // Deprecated, but still supported.
166
182
  if (this.options.initCallback) this.options.initCallback(this);
183
+ if (this.options.onLoad) this.options.onLoad(this);
167
184
  if (this.options.lineNumbers) this.activateLineNumbers();
168
185
  if (this.options.reindentOnLoad) this.reindent();
169
186
  if (this.options.height == "dynamic") this.setDynamicHeight();
@@ -177,7 +194,8 @@ var CodeMirror = (function(){
177
194
 
178
195
  focusIfIE: function() {
179
196
  // in IE, a lot of selection-related functionality only works when the frame is focused
180
- if (this.win.select.ie_selection) this.focus();
197
+ if (this.win.select.ie_selection && document.activeElement != this.frame)
198
+ this.focus();
181
199
  },
182
200
  focus: function() {
183
201
  this.win.focus();
@@ -257,12 +275,12 @@ var CodeMirror = (function(){
257
275
  setEnterMode: function(mode) {this.options.enterMode = mode;},
258
276
  setLineNumbers: function(on) {
259
277
  if (on && !this.lineNumbers) {
260
- this.lineNumbers = addLineNumberDiv(this.wrapping);
278
+ this.lineNumbers = addLineNumberDiv(this.wrapping,this.options.firstLineNumber);
261
279
  this.activateLineNumbers();
262
280
  }
263
281
  else if (!on && this.lineNumbers) {
264
282
  this.wrapping.removeChild(this.lineNumbers);
265
- this.wrapping.style.marginLeft = "";
283
+ this.wrapping.style.paddingLeft = "";
266
284
  this.lineNumbers = null;
267
285
  }
268
286
  },
@@ -312,6 +330,15 @@ var CodeMirror = (function(){
312
330
  nums = this.lineNumbers, scroller = nums.firstChild, self = this;
313
331
  var barWidth = null;
314
332
 
333
+ nums.onclick = function(e) {
334
+ var handler = self.options.onLineNumberClick;
335
+ if (handler) {
336
+ var div = (e || window.event).target || (e || window.event).srcElement;
337
+ var num = div == nums ? NaN : Number(div.innerHTML);
338
+ if (!isNaN(num)) handler(num, div);
339
+ }
340
+ };
341
+
315
342
  function sizeBar() {
316
343
  if (frame.offsetWidth == 0) return;
317
344
  for (var root = frame; root.parentNode; root = root.parentNode){}
@@ -341,7 +368,7 @@ var CodeMirror = (function(){
341
368
  var targetHeight = 50 + Math.max(body.offsetHeight, Math.max(frame.offsetHeight, body.scrollHeight || 0)),
342
369
  lastNumber = Math.ceil(targetHeight / lineHeight);
343
370
  for (var i = scroller.childNodes.length; i <= lastNumber; i++) {
344
- var div = document.createElement("DIV");
371
+ var div = createHTMLElement("div");
345
372
  div.appendChild(document.createTextNode(fill ? String(i + self.options.firstLineNumber) : "\u00a0"));
346
373
  scroller.appendChild(div);
347
374
  }
@@ -368,7 +395,7 @@ var CodeMirror = (function(){
368
395
  function setNum(n, node) {
369
396
  // Does not typically happen (but can, if you mess with the
370
397
  // document during the numbering)
371
- if (!lineNum) lineNum = scroller.appendChild(document.createElement("DIV"));
398
+ if (!lineNum) lineNum = scroller.appendChild(createHTMLElement("div"));
372
399
  if (styleNums) styleNums(lineNum, node, n);
373
400
  // Changes are accumulated, so that the document layout
374
401
  // doesn't have to be recomputed during the pass
@@ -389,7 +416,11 @@ var CodeMirror = (function(){
389
416
  setNum(next++, node.previousSibling);
390
417
  for (; node && !win.isBR(node); node = node.nextSibling) {
391
418
  var bott = node.offsetTop + node.offsetHeight;
392
- while (scroller.offsetHeight && bott - 3 > pos) setNum("&nbsp;");
419
+ while (scroller.offsetHeight && bott - 3 > pos) {
420
+ var oldPos = pos;
421
+ setNum("&nbsp;");
422
+ if (pos <= oldPos) break;
423
+ }
393
424
  }
394
425
  if (node) node = node.nextSibling;
395
426
  if (new Date().getTime() > endTime) {
@@ -433,7 +464,7 @@ var CodeMirror = (function(){
433
464
  },
434
465
 
435
466
  setDynamicHeight: function() {
436
- var self = this, activity = self.options.cursorActivity, win = self.win, body = win.document.body,
467
+ var self = this, activity = self.options.onCursorActivity, win = self.win, body = win.document.body,
437
468
  lineHeight = null, timeout = null, vmargin = 2 * self.frame.offsetTop;
438
469
  body.style.overflowY = "hidden";
439
470
  win.document.documentElement.style.overflowY = "hidden";
@@ -456,7 +487,7 @@ var CodeMirror = (function(){
456
487
  self.wrapping.style.height = Math.max(vmargin + computedHeight, self.options.minHeight) + "px";
457
488
  }
458
489
  setTimeout(updateHeight, 300);
459
- self.options.cursorActivity = function(x) {
490
+ self.options.onCursorActivity = function(x) {
460
491
  if (activity) activity(x);
461
492
  clearTimeout(timeout);
462
493
  timeout = setTimeout(updateHeight, 100);
@@ -485,10 +516,10 @@ var CodeMirror = (function(){
485
516
  options.height = area.style.height;
486
517
  if (options.content == null) options.content = area.value;
487
518
 
519
+ function updateField() {
520
+ area.value = mirror.getCode();
521
+ }
488
522
  if (area.form) {
489
- function updateField() {
490
- area.value = mirror.getCode();
491
- }
492
523
  if (typeof area.form.addEventListener == "function")
493
524
  area.form.addEventListener("submit", updateField, false);
494
525
  else
@@ -501,7 +532,7 @@ var CodeMirror = (function(){
501
532
  area.form.submit();
502
533
  area.form.submit = wrapSubmit;
503
534
  }
504
- area.form.submit = wrapSubmit;
535
+ try {area.form.submit = wrapSubmit;} catch(e){}
505
536
  }
506
537
 
507
538
  function insert(frame) {
@@ -513,11 +544,13 @@ var CodeMirror = (function(){
513
544
 
514
545
  area.style.display = "none";
515
546
  var mirror = new CodeMirror(insert, options);
547
+ mirror.save = updateField;
516
548
  mirror.toTextArea = function() {
549
+ updateField();
517
550
  area.parentNode.removeChild(mirror.wrapping);
518
551
  area.style.display = "";
519
552
  if (area.form) {
520
- area.form.submit = realSubmit;
553
+ try {area.form.submit = realSubmit;} catch(e) {}
521
554
  if (typeof area.form.removeEventListener == "function")
522
555
  area.form.removeEventListener("submit", updateField, false);
523
556
  else
@@ -533,7 +566,7 @@ var CodeMirror = (function(){
533
566
  var match;
534
567
  if (window.opera)
535
568
  return Number(window.opera.version()) >= 9.52;
536
- else if (/Apple Computers, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./)))
569
+ else if (/Apple Computer, Inc/.test(navigator.vendor) && (match = navigator.userAgent.match(/Version\/(\d+(?:\.\d+)?)\./)))
537
570
  return Number(match[1]) >= 3;
538
571
  else if (document.selection && window.ActiveXObject && (match = navigator.userAgent.match(/MSIE (\d+(?:\.\d*)?)\b/)))
539
572
  return Number(match[1]) >= 6;