tchao 3 лет назад
Родитель
Сommit
177758b4b3
100 измененных файлов с 51451 добавлено и 0 удалено
  1. 2298 0
      web/js/codemirror/keymap/vim.js
  2. 239 0
      web/js/codemirror/lib/codemirror.css
  3. 4553 0
      web/js/codemirror/lib/codemirror.js
  4. 85 0
      web/js/codemirror/lib/util/closetag.js
  5. 29 0
      web/js/codemirror/lib/util/colorize.js
  6. 36 0
      web/js/codemirror/lib/util/continuecomment.js
  7. 28 0
      web/js/codemirror/lib/util/continuelist.js
  8. 32 0
      web/js/codemirror/lib/util/dialog.css
  9. 75 0
      web/js/codemirror/lib/util/dialog.js
  10. 182 0
      web/js/codemirror/lib/util/foldcode.js
  11. 108 0
      web/js/codemirror/lib/util/formatting.js
  12. 137 0
      web/js/codemirror/lib/util/javascript-hint.js
  13. 51 0
      web/js/codemirror/lib/util/loadmode.js
  14. 46 0
      web/js/codemirror/lib/util/match-highlighter.js
  15. 63 0
      web/js/codemirror/lib/util/matchbrackets.js
  16. 95 0
      web/js/codemirror/lib/util/multiplex.js
  17. 59 0
      web/js/codemirror/lib/util/overlay.js
  18. 117 0
      web/js/codemirror/lib/util/pig-hint.js
  19. 90 0
      web/js/codemirror/lib/util/runmode-standalone.js
  20. 52 0
      web/js/codemirror/lib/util/runmode.js
  21. 119 0
      web/js/codemirror/lib/util/search.js
  22. 119 0
      web/js/codemirror/lib/util/searchcursor.js
  23. 16 0
      web/js/codemirror/lib/util/simple-hint.css
  24. 102 0
      web/js/codemirror/lib/util/simple-hint.js
  25. 131 0
      web/js/codemirror/lib/util/xml-hint.js
  26. 465 0
      web/js/codemirror/mode/css/css.js
  27. 501 0
      web/js/codemirror/mode/css/test.js
  28. 210 0
      web/js/codemirror/mode/groovy/groovy.js
  29. 84 0
      web/js/codemirror/mode/htmlmixed/htmlmixed.js
  30. 411 0
      web/js/codemirror/mode/javascript/javascript.js
  31. 87 0
      web/js/codemirror/mode/meta.js
  32. 203 0
      web/js/codemirror/mode/mysql/mysql.js
  33. 216 0
      web/js/codemirror/mode/plsql/plsql.js
  34. 352 0
      web/js/codemirror/mode/sql/sql.js
  35. 324 0
      web/js/codemirror/mode/xml/xml.js
  36. 784 0
      web/js/haiya/form/me/lyhtztbg.js
  37. 656 0
      web/js/haiya/form/me/lytksb.js
  38. 709 0
      web/js/haiya/form/me/lyzzlbg.js
  39. 768 0
      web/js/haiya/form/me/zlhtqd.js
  40. 772 0
      web/js/haiya/form/me/zlzlybg.js
  41. 38 0
      web/js/haiya/form/me/zycsjxhtqd.js
  42. 388 0
      web/js/haiya/form/op/hdcdxy.js
  43. 673 0
      web/js/haiya/form/op/zjkysq.js
  44. 1702 0
      web/js/haiya/oa/gzjhkhFunc.js
  45. 30 0
      web/js/haiya/oa/hyVoteResult.js
  46. 52 0
      web/js/haiya/oa/sysquestionnaire/SysAnswerController.js
  47. 355 0
      web/js/haiya/utils/HyUtil.js
  48. 163 0
      web/js/haiya/utils/TableFix.js
  49. 1438 0
      web/js/jqplot/excanvas.js
  50. 259 0
      web/js/jqplot/jquery.jqplot.css
  51. 11381 0
      web/js/jqplot/jquery.jqplot.js
  52. 1 0
      web/js/jqplot/jquery.jqplot.min.css
  53. 9046 0
      web/js/jqplot/jquery.js
  54. 4 0
      web/js/jqplot/jquery.min.js
  55. 313 0
      web/js/jqplot/plugins/jqplot.BezierCurveRenderer.js
  56. 57 0
      web/js/jqplot/plugins/jqplot.BezierCurveRenderer.min.js
  57. 797 0
      web/js/jqplot/plugins/jqplot.barRenderer.js
  58. 57 0
      web/js/jqplot/plugins/jqplot.barRenderer.min.js
  59. 57 0
      web/js/jqplot/plugins/jqplot.blockRenderer.min.js
  60. 57 0
      web/js/jqplot/plugins/jqplot.bubbleRenderer.min.js
  61. 203 0
      web/js/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js
  62. 243 0
      web/js/jqplot/plugins/jqplot.canvasAxisTickRenderer.js
  63. 57 0
      web/js/jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js
  64. 673 0
      web/js/jqplot/plugins/jqplot.categoryAxisRenderer.js
  65. 116 0
      web/js/jqplot/plugins/jqplot.ciParser.js
  66. 57 0
      web/js/jqplot/plugins/jqplot.ciParser.min.js
  67. 57 0
      web/js/jqplot/plugins/jqplot.cursor.min.js
  68. 225 0
      web/js/jqplot/plugins/jqplot.dragable.js
  69. 57 0
      web/js/jqplot/plugins/jqplot.dragable.min.js
  70. 57 0
      web/js/jqplot/plugins/jqplot.enhancedLegendRenderer.min.js
  71. 57 0
      web/js/jqplot/plugins/jqplot.funnelRenderer.min.js
  72. 465 0
      web/js/jqplot/plugins/jqplot.highlighter.js
  73. 57 0
      web/js/jqplot/plugins/jqplot.highlighter.min.js
  74. 475 0
      web/js/jqplot/plugins/jqplot.json2.js
  75. 57 0
      web/js/jqplot/plugins/jqplot.json2.min.js
  76. 529 0
      web/js/jqplot/plugins/jqplot.logAxisRenderer.js
  77. 57 0
      web/js/jqplot/plugins/jqplot.logAxisRenderer.min.js
  78. 611 0
      web/js/jqplot/plugins/jqplot.mekkoAxisRenderer.js
  79. 57 0
      web/js/jqplot/plugins/jqplot.mekkoAxisRenderer.min.js
  80. 437 0
      web/js/jqplot/plugins/jqplot.mekkoRenderer.js
  81. 1028 0
      web/js/jqplot/plugins/jqplot.meterGaugeRenderer.js
  82. 57 0
      web/js/jqplot/plugins/jqplot.meterGaugeRenderer.min.js
  83. 373 0
      web/js/jqplot/plugins/jqplot.ohlcRenderer.js
  84. 904 0
      web/js/jqplot/plugins/jqplot.pieRenderer.js
  85. 57 0
      web/js/jqplot/plugins/jqplot.pointLabels.min.js
  86. 728 0
      web/js/jqplot/plugins/jqplot.pyramidAxisRenderer.js
  87. 429 0
      web/js/jqplot/plugins/jqplot.pyramidGridRenderer.js
  88. 57 0
      web/js/jqplot/plugins/jqplot.pyramidRenderer.min.js
  89. 223 0
      web/js/jqplot/plugins/jqplot.trendline.js
  90. 47 0
      web/js/jquery-ui-portlet/css/jquery.portlet.css
  91. 12 0
      web/js/jquery-ui-portlet/css/jquery.portlet.min.css
  92. 19 0
      web/js/jquery-ui-portlet/demo/ajax-fixed-height.json
  93. 19 0
      web/js/jquery-ui-portlet/demo/ajax.json
  94. 124 0
      web/js/jquery-ui-portlet/demo/colspan.html
  95. 144 0
      web/js/jquery-ui-portlet/demo/disable-drag.html
  96. 131 0
      web/js/jquery-ui-portlet/demo/fixed-height.html
  97. 130 0
      web/js/jquery-ui-portlet/demo/icon.html
  98. 147 0
      web/js/jquery-ui-portlet/demo/mix.html
  99. 3 0
      web/js/jquery-ui-portlet/demo/portlet.html
  100. 0 0
      web/js/jquery-ui-portlet/demo/single-view.html

Разница между файлами не показана из-за своего большого размера
+ 2298 - 0
web/js/codemirror/keymap/vim.js


+ 239 - 0
web/js/codemirror/lib/codemirror.css

@@ -0,0 +1,239 @@
+/* BASICS */
+
+.CodeMirror {
+  /* Set height, width, borders, and global font properties here */
+  font-family: monospace;
+  height: 300px;
+}
+.CodeMirror-scroll {
+  /* Set scrolling behaviour here */
+  overflow: auto;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+  padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+  padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler {
+  background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+  border-right: 1px solid #ddd;
+  background-color: #f7f7f7;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+  padding: 0 3px 0 5px;
+  min-width: 20px;
+  text-align: right;
+  color: #999;
+}
+
+/* CURSOR */
+
+.CodeMirror pre.CodeMirror-cursor {
+  border-left: 1px solid black;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror pre.CodeMirror-secondarycursor {
+  border-left: 1px solid silver;
+}
+.cm-keymap-fat-cursor pre.CodeMirror-cursor {
+  width: auto;
+  border: 0;
+  background: transparent;
+  background: rgba(0, 200, 0, .4);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
+}
+/* Kludge to turn off filter in ie9+, which also accepts rgba */
+.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable {color: black;}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-property {color: black;}
+.cm-s-default .cm-operator {color: black;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-error {color: #f00;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-emstrong {font-style: italic; font-weight: bold;}
+.cm-link {text-decoration: underline;}
+
+.cm-invalidchar {color: #f00;}
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+   the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+  line-height: 1;
+  position: relative;
+  overflow: hidden;
+}
+
+.CodeMirror-scroll {
+  /* 30px is the magic margin used to hide the element's real scrollbars */
+  /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
+  margin-bottom: -30px; margin-right: -30px;
+  padding-bottom: 30px; padding-right: 30px;
+  height: 100%;
+  outline: none; /* Prevent dragging from highlighting the element */
+  position: relative;
+}
+.CodeMirror-sizer {
+  position: relative;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+   before actuall scrolling happens, thus preventing shaking and
+   flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
+  position: absolute;
+  z-index: 6;
+  display: none;
+}
+.CodeMirror-vscrollbar {
+  right: 0; top: 0;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+  bottom: 0; left: 0;
+  overflow-y: hidden;
+  overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+  right: 0; bottom: 0;
+  z-index: 6;
+}
+
+.CodeMirror-gutters {
+  position: absolute; left: 0; top: 0;
+  height: 100%;
+  z-index: 3;
+}
+.CodeMirror-gutter {
+  height: 100%;
+  display: inline-block;
+  /* Hack to make IE7 behave */
+  *zoom:1;
+  *display:inline;
+}
+.CodeMirror-gutter-elt {
+  position: absolute;
+  cursor: default;
+  z-index: 4;
+}
+
+.CodeMirror-lines {
+  cursor: text;
+}
+.CodeMirror pre {
+  /* Reset some styles that the rest of the page might have set */
+  -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
+  border-width: 0;
+  background: transparent;
+  font-family: inherit;
+  font-size: inherit;
+  margin: 0;
+  white-space: pre;
+  word-wrap: normal;
+  line-height: inherit;
+  color: inherit;
+  z-index: 2;
+  position: relative;
+  overflow: visible;
+}
+.CodeMirror-wrap pre {
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  word-break: normal;
+}
+.CodeMirror-linebackground {
+  position: absolute;
+  left: 0; right: 0; top: 0; bottom: 0;
+  z-index: 0;
+}
+
+.CodeMirror-linewidget {
+  position: relative;
+  z-index: 2;
+}
+
+.CodeMirror-wrap .CodeMirror-scroll {
+  overflow-x: hidden;
+}
+
+.CodeMirror-measure {
+  position: absolute;
+  width: 100%; height: 0px;
+  overflow: hidden;
+  visibility: hidden;
+}
+.CodeMirror-measure pre { position: static; }
+
+.CodeMirror pre.CodeMirror-cursor {
+  position: absolute;
+  visibility: hidden;
+  border-right: none;
+  width: 0;
+}
+.CodeMirror-focused pre.CodeMirror-cursor {
+  visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+
+.CodeMirror-searching {
+  background: #ffa;
+  background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+@media print {
+  /* Hide the cursor when printing */
+  .CodeMirror pre.CodeMirror-cursor {
+    visibility: hidden;
+  }
+}

Разница между файлами не показана из-за своего большого размера
+ 4553 - 0
web/js/codemirror/lib/codemirror.js


+ 85 - 0
web/js/codemirror/lib/util/closetag.js

@@ -0,0 +1,85 @@
+/**
+ * Tag-closer extension for CodeMirror.
+ *
+ * This extension adds an "autoCloseTags" option that can be set to
+ * either true to get the default behavior, or an object to further
+ * configure its behavior.
+ *
+ * These are supported options:
+ *
+ * `whenClosing` (default true)
+ *   Whether to autoclose when the '/' of a closing tag is typed.
+ * `whenOpening` (default true)
+ *   Whether to autoclose the tag when the final '>' of an opening
+ *   tag is typed.
+ * `dontCloseTags` (default is empty tags for HTML, none for XML)
+ *   An array of tag names that should not be autoclosed.
+ * `indentTags` (default is block tags for HTML, none for XML)
+ *   An array of tag names that should, when opened, cause a
+ *   blank line to be added inside the tag, and the blank line and
+ *   closing line to be indented.
+ *
+ * See demos/closetag.html for a usage example.
+ */
+
+(function() {
+  CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
+    if (val && (old == CodeMirror.Init || !old)) {
+      var map = {name: "autoCloseTags"};
+      if (typeof val != "object" || val.whenClosing)
+        map["'/'"] = function(cm) { autoCloseTag(cm, '/'); };
+      if (typeof val != "object" || val.whenOpening)
+        map["'>'"] = function(cm) { autoCloseTag(cm, '>'); };
+      cm.addKeyMap(map);
+    } else if (!val && (old != CodeMirror.Init && old)) {
+      cm.removeKeyMap("autoCloseTags");
+    }
+  });
+
+  var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
+                       "source", "track", "wbr"];
+  var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
+                    "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
+
+  function autoCloseTag(cm, ch) {
+    var pos = cm.getCursor(), tok = cm.getTokenAt(pos);
+    var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
+    if (inner.mode.name != "xml") throw CodeMirror.Pass;
+
+    var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html";
+    var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
+    var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
+
+    if (ch == ">" && state.tagName) {
+      var tagName = state.tagName;
+      if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
+      var lowerTagName = tagName.toLowerCase();
+      // Don't process the '>' at the end of an end-tag or self-closing tag
+      if (tok.type == "tag" && state.type == "closeTag" ||
+          /\/\s*$/.test(tok.string) ||
+          dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1)
+        throw CodeMirror.Pass;
+
+      var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1;
+      cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "</" + tagName + ">",
+                          doIndent ? {line: pos.line + 1, ch: 0} : {line: pos.line, ch: pos.ch + 1});
+      if (doIndent) {
+        cm.indentLine(pos.line + 1);
+        cm.indentLine(pos.line + 2);
+      }
+      return;
+    } else if (ch == "/" && tok.type == "tag" && tok.string == "<") {
+      var tagName = state.context && state.context.tagName;
+      if (tagName) cm.replaceSelection("/" + tagName + ">", "end");
+      return;
+    }
+    throw CodeMirror.Pass;
+  }
+
+  function indexOf(collection, elt) {
+    if (collection.indexOf) return collection.indexOf(elt);
+    for (var i = 0, e = collection.length; i < e; ++i)
+      if (collection[i] == elt) return i;
+    return -1;
+  }
+})();

+ 29 - 0
web/js/codemirror/lib/util/colorize.js

@@ -0,0 +1,29 @@
+CodeMirror.colorize = (function() {
+
+  var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/;
+
+  function textContent(node, out) {
+    if (node.nodeType == 3) return out.push(node.nodeValue);
+    for (var ch = node.firstChild; ch; ch = ch.nextSibling) {
+      textContent(ch, out);
+      if (isBlock.test(node.nodeType)) out.push("\n");
+    }
+  }
+
+  return function(collection, defaultMode) {
+    if (!collection) collection = document.body.getElementsByTagName("pre");
+
+    for (var i = 0; i < collection.length; ++i) {
+      var node = collection[i];
+      var mode = node.getAttribute("data-lang") || defaultMode;
+      if (!mode) continue;
+
+      var text = [];
+      textContent(node, text);
+      node.innerHTML = "";
+      CodeMirror.runMode(text.join(""), mode, node);
+
+      node.className += " cm-s-default";
+    }
+  };
+})();

+ 36 - 0
web/js/codemirror/lib/util/continuecomment.js

@@ -0,0 +1,36 @@
+(function() {
+  var modes = ["clike", "css", "javascript"];
+  for (var i = 0; i < modes.length; ++i)
+    CodeMirror.extendMode(modes[i], {blockCommentStart: "/*",
+                                     blockCommentEnd: "*/",
+                                     blockCommentContinue: " * "});
+
+  CodeMirror.commands.newlineAndIndentContinueComment = function(cm) {
+    var pos = cm.getCursor(), token = cm.getTokenAt(pos);
+    var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode;
+    var space;
+
+    if (token.type == "comment" && mode.blockCommentStart) {
+      var end = token.string.indexOf(mode.blockCommentEnd);
+      var full = cm.getRange({line: pos.line, ch: 0}, {line: pos.line, ch: token.end}), found;
+      if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) {
+        // Comment ended, don't continue it
+      } else if (token.string.indexOf(mode.blockCommentStart) == 0) {
+        space = full.slice(0, token.start);
+        if (!/^\s*$/.test(space)) {
+          space = "";
+          for (var i = 0; i < token.start; ++i) space += " ";
+        }
+      } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 &&
+                 found + mode.blockCommentContinue.length > token.start &&
+                 /^\s*$/.test(full.slice(0, found))) {
+        space = full.slice(0, found);
+      }
+    }
+
+    if (space != null)
+      cm.replaceSelection("\n" + space + mode.blockCommentContinue, "end");
+    else
+      cm.execCommand("newlineAndIndent");
+  };
+})();

+ 28 - 0
web/js/codemirror/lib/util/continuelist.js

@@ -0,0 +1,28 @@
+(function() {
+  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
+    var pos = cm.getCursor(), token = cm.getTokenAt(pos);
+    var space;
+    if (token.className == "string") {
+      var full = cm.getRange({line: pos.line, ch: 0}, {line: pos.line, ch: token.end});
+      var listStart = /\*|\d+\./, listContinue;
+      if (token.string.search(listStart) == 0) {
+        var reg = /^[\W]*(\d+)\./g;
+        var matches = reg.exec(full);
+        if(matches)
+          listContinue = (parseInt(matches[1]) + 1) + ".  ";
+        else
+          listContinue = "*   ";
+        space = full.slice(0, token.start);
+        if (!/^\s*$/.test(space)) {
+          space = "";
+          for (var i = 0; i < token.start; ++i) space += " ";
+        }
+      }
+    }
+
+    if (space != null)
+      cm.replaceSelection("\n" + space + listContinue, "end");
+    else
+      cm.execCommand("newlineAndIndent");
+  };
+})();

+ 32 - 0
web/js/codemirror/lib/util/dialog.css

@@ -0,0 +1,32 @@
+.CodeMirror-dialog {
+  position: absolute;
+  left: 0; right: 0;
+  background: white;
+  z-index: 15;
+  padding: .1em .8em;
+  overflow: hidden;
+  color: #333;
+}
+
+.CodeMirror-dialog-top {
+  border-bottom: 1px solid #eee;
+  top: 0;
+}
+
+.CodeMirror-dialog-bottom {
+  border-top: 1px solid #eee;
+  bottom: 0;
+}
+
+.CodeMirror-dialog input {
+  border: none;
+  outline: none;
+  background: transparent;
+  width: 20em;
+  color: inherit;
+  font-family: monospace;
+}
+
+.CodeMirror-dialog button {
+  font-size: 70%;
+}

+ 75 - 0
web/js/codemirror/lib/util/dialog.js

@@ -0,0 +1,75 @@
+// Open simple dialogs on top of an editor. Relies on dialog.css.
+
+(function() {
+  function dialogDiv(cm, template, bottom) {
+    var wrap = cm.getWrapperElement();
+    var dialog;
+    dialog = wrap.appendChild(document.createElement("div"));
+    if (bottom) {
+      dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
+    } else {
+      dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
+    }
+    dialog.innerHTML = template;
+    return dialog;
+  }
+
+  CodeMirror.defineExtension("openDialog", function(template, callback, options) {
+    var dialog = dialogDiv(this, template, options && options.bottom);
+    var closed = false, me = this;
+    function close() {
+      if (closed) return;
+      closed = true;
+      dialog.parentNode.removeChild(dialog);
+    }
+    var inp = dialog.getElementsByTagName("input")[0], button;
+    if (inp) {
+      CodeMirror.on(inp, "keydown", function(e) {
+        if (e.keyCode == 13 || e.keyCode == 27) {
+          CodeMirror.e_stop(e);
+          close();
+          me.focus();
+          if (e.keyCode == 13) callback(inp.value);
+        }
+      });
+      inp.focus();
+      CodeMirror.on(inp, "blur", close);
+    } else if (button = dialog.getElementsByTagName("button")[0]) {
+      CodeMirror.on(button, "click", function() {
+        close();
+        me.focus();
+      });
+      button.focus();
+      CodeMirror.on(button, "blur", close);
+    }
+    return close;
+  });
+
+  CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
+    var dialog = dialogDiv(this, template, options && options.bottom);
+    var buttons = dialog.getElementsByTagName("button");
+    var closed = false, me = this, blurring = 1;
+    function close() {
+      if (closed) return;
+      closed = true;
+      dialog.parentNode.removeChild(dialog);
+      me.focus();
+    }
+    buttons[0].focus();
+    for (var i = 0; i < buttons.length; ++i) {
+      var b = buttons[i];
+      (function(callback) {
+        CodeMirror.on(b, "click", function(e) {
+          CodeMirror.e_preventDefault(e);
+          close();
+          if (callback) callback(me);
+        });
+      })(callbacks[i]);
+      CodeMirror.on(b, "blur", function() {
+        --blurring;
+        setTimeout(function() { if (blurring <= 0) close(); }, 200);
+      });
+      CodeMirror.on(b, "focus", function() { ++blurring; });
+    }
+  });
+})();

+ 182 - 0
web/js/codemirror/lib/util/foldcode.js

@@ -0,0 +1,182 @@
+// the tagRangeFinder function is
+//   Copyright (C) 2011 by Daniel Glazman <daniel@glazman.org>
+// released under the MIT license (../../LICENSE) like the rest of CodeMirror
+CodeMirror.tagRangeFinder = function(cm, start) {
+  var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
+  var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
+  var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*");
+
+  var lineText = cm.getLine(start.line);
+  var found = false;
+  var tag = null;
+  var pos = start.ch;
+  while (!found) {
+    pos = lineText.indexOf("<", pos);
+    if (-1 == pos) // no tag on line
+      return;
+    if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag
+      pos++;
+      continue;
+    }
+    // ok we seem to have a start tag
+    if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name...
+      pos++;
+      continue;
+    }
+    var gtPos = lineText.indexOf(">", pos + 1);
+    if (-1 == gtPos) { // end of start tag not in line
+      var l = start.line + 1;
+      var foundGt = false;
+      var lastLine = cm.lineCount();
+      while (l < lastLine && !foundGt) {
+        var lt = cm.getLine(l);
+        gtPos = lt.indexOf(">");
+        if (-1 != gtPos) { // found a >
+          foundGt = true;
+          var slash = lt.lastIndexOf("/", gtPos);
+          if (-1 != slash && slash < gtPos) {
+            var str = lineText.substr(slash, gtPos - slash + 1);
+            if (!str.match( /\/\s*\>/ )) // yep, that's the end of empty tag
+              return;
+          }
+        }
+        l++;
+      }
+      found = true;
+    }
+    else {
+      var slashPos = lineText.lastIndexOf("/", gtPos);
+      if (-1 == slashPos) { // cannot be empty tag
+        found = true;
+        // don't continue
+      }
+      else { // empty tag?
+        // check if really empty tag
+        var str = lineText.substr(slashPos, gtPos - slashPos + 1);
+        if (!str.match( /\/\s*\>/ )) { // finally not empty
+          found = true;
+          // don't continue
+        }
+      }
+    }
+    if (found) {
+      var subLine = lineText.substr(pos + 1);
+      tag = subLine.match(xmlNAMERegExp);
+      if (tag) {
+        // we have an element name, wooohooo !
+        tag = tag[0];
+        // do we have the close tag on same line ???
+        if (-1 != lineText.indexOf("</" + tag + ">", pos)) // yep
+        {
+          found = false;
+        }
+        // we don't, so we have a candidate...
+      }
+      else
+        found = false;
+    }
+    if (!found)
+      pos++;
+  }
+
+  if (found) {
+    var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)";
+    var startTagRegExp = new RegExp(startTag);
+    var endTag = "</" + tag + ">";
+    var depth = 1;
+    var l = start.line + 1;
+    var lastLine = cm.lineCount();
+    while (l < lastLine) {
+      lineText = cm.getLine(l);
+      var match = lineText.match(startTagRegExp);
+      if (match) {
+        for (var i = 0; i < match.length; i++) {
+          if (match[i] == endTag)
+            depth--;
+          else
+            depth++;
+          if (!depth) return {from: {line: start.line, ch: gtPos + 1},
+                              to: {line: l, ch: match.index}};
+        }
+      }
+      l++;
+    }
+    return;
+  }
+};
+
+CodeMirror.braceRangeFinder = function(cm, start) {
+  var line = start.line, lineText = cm.getLine(line);
+  var at = lineText.length, startChar, tokenType;
+  for (;;) {
+    var found = lineText.lastIndexOf("{", at);
+    if (found < start.ch) break;
+    tokenType = cm.getTokenAt({line: line, ch: found}).type;
+    if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
+    at = found - 1;
+  }
+  if (startChar == null || lineText.lastIndexOf("}") > startChar) return;
+  var count = 1, lastLine = cm.lineCount(), end, endCh;
+  outer: for (var i = line + 1; i < lastLine; ++i) {
+    var text = cm.getLine(i), pos = 0;
+    for (;;) {
+      var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
+      if (nextOpen < 0) nextOpen = text.length;
+      if (nextClose < 0) nextClose = text.length;
+      pos = Math.min(nextOpen, nextClose);
+      if (pos == text.length) break;
+      if (cm.getTokenAt({line: i, ch: pos + 1}).type == tokenType) {
+        if (pos == nextOpen) ++count;
+        else if (!--count) { end = i; endCh = pos; break outer; }
+      }
+      ++pos;
+    }
+  }
+  if (end == null || end == line + 1) return;
+  return {from: {line: line, ch: startChar + 1},
+          to: {line: end, ch: endCh}};
+};
+
+CodeMirror.indentRangeFinder = function(cm, start) {
+  var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line);
+  var myIndent = CodeMirror.countColumn(firstLine, null, tabSize);
+  for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) {
+    var curLine = cm.getLine(i);
+    if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent)
+      return {from: {line: start.line, ch: firstLine.length},
+              to: {line: i, ch: curLine.length}};
+  }
+};
+
+CodeMirror.newFoldFunction = function(rangeFinder, widget) {
+  if (widget == null) widget = "\u2194";
+  if (typeof widget == "string") {
+    var text = document.createTextNode(widget);
+    widget = document.createElement("span");
+    widget.appendChild(text);
+    widget.className = "CodeMirror-foldmarker";
+  }
+
+  return function(cm, pos) {
+    if (typeof pos == "number") pos = {line: pos, ch: 0};
+    var range = rangeFinder(cm, pos);
+    if (!range) return;
+
+    var present = cm.findMarksAt(range.from), cleared = 0;
+    for (var i = 0; i < present.length; ++i) {
+      if (present[i].__isFold) {
+        ++cleared;
+        present[i].clear();
+      }
+    }
+    if (cleared) return;
+
+    var myWidget = widget.cloneNode(true);
+    CodeMirror.on(myWidget, "mousedown", function() {myRange.clear();});
+    var myRange = cm.markText(range.from, range.to, {
+      replacedWith: myWidget,
+      clearOnEnter: true,
+      __isFold: true
+    });
+  };
+};

+ 108 - 0
web/js/codemirror/lib/util/formatting.js

@@ -0,0 +1,108 @@
+(function() {
+
+  CodeMirror.extendMode("css", {
+    commentStart: "/*",
+    commentEnd: "*/",
+    newlineAfterToken: function(_type, content) {
+      return /^[;{}]$/.test(content);
+    }
+  });
+
+  CodeMirror.extendMode("javascript", {
+    commentStart: "/*",
+    commentEnd: "*/",
+    // FIXME semicolons inside of for
+    newlineAfterToken: function(_type, content, textAfter, state) {
+      if (this.jsonMode) {
+        return /^[\[,{]$/.test(content) || /^}/.test(textAfter);
+      } else {
+        if (content == ";" && state.lexical && state.lexical.type == ")") return false;
+        return /^[;{}]$/.test(content) && !/^;/.test(textAfter);
+      }
+    }
+  });
+
+  CodeMirror.extendMode("xml", {
+    commentStart: "<!--",
+    commentEnd: "-->",
+    newlineAfterToken: function(type, content, textAfter) {
+      return type == "tag" && />$/.test(content) || /^</.test(textAfter);
+    }
+  });
+
+  // Comment/uncomment the specified range
+  CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
+    var cm = this, curMode = CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(from).state).mode;
+    cm.operation(function() {
+      if (isComment) { // Comment range
+        cm.replaceRange(curMode.commentEnd, to);
+        cm.replaceRange(curMode.commentStart, from);
+        if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside
+          cm.setCursor(from.line, from.ch + curMode.commentStart.length);
+      } else { // Uncomment range
+        var selText = cm.getRange(from, to);
+        var startIndex = selText.indexOf(curMode.commentStart);
+        var endIndex = selText.lastIndexOf(curMode.commentEnd);
+        if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
+          // Take string till comment start
+          selText = selText.substr(0, startIndex)
+          // From comment start till comment end
+            + selText.substring(startIndex + curMode.commentStart.length, endIndex)
+          // From comment end till string end
+            + selText.substr(endIndex + curMode.commentEnd.length);
+        }
+        cm.replaceRange(selText, from, to);
+      }
+    });
+  });
+
+  // Applies automatic mode-aware indentation to the specified range
+  CodeMirror.defineExtension("autoIndentRange", function (from, to) {
+    var cmInstance = this;
+    this.operation(function () {
+      for (var i = from.line; i <= to.line; i++) {
+        cmInstance.indentLine(i, "smart");
+      }
+    });
+  });
+
+  // Applies automatic formatting to the specified range
+  CodeMirror.defineExtension("autoFormatRange", function (from, to) {
+    var cm = this;
+    var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");
+    var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
+    var tabSize = cm.getOption("tabSize");
+
+    var out = "", lines = 0, atSol = from.ch == 0;
+    function newline() {
+      out += "\n";
+      atSol = true;
+      ++lines;
+    }
+
+    for (var i = 0; i < text.length; ++i) {
+      var stream = new CodeMirror.StringStream(text[i], tabSize);
+      while (!stream.eol()) {
+        var inner = CodeMirror.innerMode(outer, state);
+        var style = outer.token(stream, state), cur = stream.current();
+        stream.start = stream.pos;
+        if (!atSol || /\S/.test(cur)) {
+          out += cur;
+          atSol = false;
+        }
+        if (!atSol && inner.mode.newlineAfterToken &&
+            inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i+1] || "", inner.state))
+          newline();
+      }
+      if (!stream.pos && outer.blankLine) outer.blankLine(state);
+      if (!atSol) newline();
+    }
+
+    cm.operation(function () {
+      cm.replaceRange(out, from, to);
+      for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
+        cm.indentLine(cur, "smart");
+      cm.setSelection(from, cm.getCursor(false));
+    });
+  });
+})();

+ 137 - 0
web/js/codemirror/lib/util/javascript-hint.js

@@ -0,0 +1,137 @@
+(function () {
+  function forEach(arr, f) {
+    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
+  }
+  
+  function arrayContains(arr, item) {
+    if (!Array.prototype.indexOf) {
+      var i = arr.length;
+      while (i--) {
+        if (arr[i] === item) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return arr.indexOf(item) != -1;
+  }
+
+  function scriptHint(editor, keywords, getToken, options) {
+    // Find the token at the cursor
+    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+    // If it's not a 'word-style' token, ignore the token.
+		if (!/^[\w$_]*$/.test(token.string)) {
+      token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+                       type: token.string == "." ? "property" : null};
+    }
+    // If it is a property, find out what it is a property of.
+    while (tprop.type == "property") {
+      tprop = getToken(editor, {line: cur.line, ch: tprop.start});
+      if (tprop.string != ".") return;
+      tprop = getToken(editor, {line: cur.line, ch: tprop.start});
+      if (tprop.string == ')') {
+        var level = 1;
+        do {
+          tprop = getToken(editor, {line: cur.line, ch: tprop.start});
+          switch (tprop.string) {
+          case ')': level++; break;
+          case '(': level--; break;
+          default: break;
+          }
+        } while (level > 0);
+        tprop = getToken(editor, {line: cur.line, ch: tprop.start});
+	if (tprop.type == 'variable')
+	  tprop.type = 'function';
+	else return; // no clue
+      }
+      if (!context) var context = [];
+      context.push(tprop);
+    }
+    return {list: getCompletions(token, context, keywords, options),
+            from: {line: cur.line, ch: token.start},
+            to: {line: cur.line, ch: token.end}};
+  }
+
+  CodeMirror.javascriptHint = function(editor, options) {
+    return scriptHint(editor, javascriptKeywords,
+                      function (e, cur) {return e.getTokenAt(cur);},
+                      options);
+  };
+
+  function getCoffeeScriptToken(editor, cur) {
+  // This getToken, it is for coffeescript, imitates the behavior of
+  // getTokenAt method in javascript.js, that is, returning "property"
+  // type and treat "." as indepenent token.
+    var token = editor.getTokenAt(cur);
+    if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
+      token.end = token.start;
+      token.string = '.';
+      token.type = "property";
+    }
+    else if (/^\.[\w$_]*$/.test(token.string)) {
+      token.type = "property";
+      token.start++;
+      token.string = token.string.replace(/\./, '');
+    }
+    return token;
+  }
+
+  CodeMirror.coffeescriptHint = function(editor, options) {
+    return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
+  };
+
+  var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
+                     "toUpperCase toLowerCase split concat match replace search").split(" ");
+  var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
+                    "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
+  var funcProps = "prototype apply call bind".split(" ");
+  var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " +
+                  "if in instanceof new null return switch throw true try typeof var void while with").split(" ");
+  var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
+                  "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
+
+  function getCompletions(token, context, keywords, options) {
+    var found = [], start = token.string;
+    function maybeAdd(str) {
+      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
+    }
+    function gatherCompletions(obj) {
+      if (typeof obj == "string") forEach(stringProps, maybeAdd);
+      else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
+      else if (obj instanceof Function) forEach(funcProps, maybeAdd);
+      for (var name in obj) maybeAdd(name);
+    }
+
+    if (context) {
+      // If this is a property, see if it belongs to some object we can
+      // find in the current environment.
+      var obj = context.pop(), base;
+      if (obj.type == "variable") {
+        if (options && options.additionalContext)
+          base = options.additionalContext[obj.string];
+        base = base || window[obj.string];
+      } else if (obj.type == "string") {
+        base = "";
+      } else if (obj.type == "atom") {
+        base = 1;
+      } else if (obj.type == "function") {
+        if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
+            (typeof window.jQuery == 'function'))
+          base = window.jQuery();
+        else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
+          base = window._();
+      }
+      while (base != null && context.length)
+        base = base[context.pop().string];
+      if (base != null) gatherCompletions(base);
+    }
+    else {
+      // If not, just look in the window object and any local scope
+      // (reading into JS mode internals to get at the local variables)
+      for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
+      gatherCompletions(window);
+      forEach(keywords, maybeAdd);
+    }
+    return found;
+  }
+})();

+ 51 - 0
web/js/codemirror/lib/util/loadmode.js

@@ -0,0 +1,51 @@
+(function() {
+  if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js";
+
+  var loading = {};
+  function splitCallback(cont, n) {
+    var countDown = n;
+    return function() { if (--countDown == 0) cont(); };
+  }
+  function ensureDeps(mode, cont) {
+    var deps = CodeMirror.modes[mode].dependencies;
+    if (!deps) return cont();
+    var missing = [];
+    for (var i = 0; i < deps.length; ++i) {
+      if (!CodeMirror.modes.hasOwnProperty(deps[i]))
+        missing.push(deps[i]);
+    }
+    if (!missing.length) return cont();
+    var split = splitCallback(cont, missing.length);
+    for (var i = 0; i < missing.length; ++i)
+      CodeMirror.requireMode(missing[i], split);
+  }
+
+  CodeMirror.requireMode = function(mode, cont) {
+    if (typeof mode != "string") mode = mode.name;
+    if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont);
+    if (loading.hasOwnProperty(mode)) return loading[mode].push(cont);
+
+    var script = document.createElement("script");
+    script.src = CodeMirror.modeURL.replace(/%N/g, mode);
+    var others = document.getElementsByTagName("script")[0];
+    others.parentNode.insertBefore(script, others);
+    var list = loading[mode] = [cont];
+    var count = 0, poll = setInterval(function() {
+      if (++count > 100) return clearInterval(poll);
+      if (CodeMirror.modes.hasOwnProperty(mode)) {
+        clearInterval(poll);
+        loading[mode] = null;
+        ensureDeps(mode, function() {
+          for (var i = 0; i < list.length; ++i) list[i]();
+        });
+      }
+    }, 200);
+  };
+
+  CodeMirror.autoLoadMode = function(instance, mode) {
+    if (!CodeMirror.modes.hasOwnProperty(mode))
+      CodeMirror.requireMode(mode, function() {
+        instance.setOption("mode", instance.getOption("mode"));
+      });
+  };
+}());

+ 46 - 0
web/js/codemirror/lib/util/match-highlighter.js

@@ -0,0 +1,46 @@
+// Define match-highlighter commands. Depends on searchcursor.js
+// Use by attaching the following function call to the cursorActivity event:
+	//myCodeMirror.matchHighlight(minChars);
+// And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html)
+
+(function() {
+  var DEFAULT_MIN_CHARS = 2;
+  
+  function MatchHighlightState() {
+	this.marked = [];
+  }
+  function getMatchHighlightState(cm) {
+	return cm._matchHighlightState || (cm._matchHighlightState = new MatchHighlightState());
+  }
+  
+  function clearMarks(cm) {
+	var state = getMatchHighlightState(cm);
+	for (var i = 0; i < state.marked.length; ++i)
+		state.marked[i].clear();
+	state.marked = [];
+  }
+  
+  function markDocument(cm, className, minChars) {
+    clearMarks(cm);
+	minChars = (typeof minChars !== 'undefined' ? minChars : DEFAULT_MIN_CHARS);
+	if (cm.somethingSelected() && cm.getSelection().replace(/^\s+|\s+$/g, "").length >= minChars) {
+		var state = getMatchHighlightState(cm);
+		var query = cm.getSelection();
+		cm.operation(function() {
+			if (cm.lineCount() < 2000) { // This is too expensive on big documents.
+			  for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
+				//Only apply matchhighlight to the matches other than the one actually selected
+				if (cursor.from().line !== cm.getCursor(true).line ||
+                                    cursor.from().ch !== cm.getCursor(true).ch)
+					state.marked.push(cm.markText(cursor.from(), cursor.to(),
+                                                                      {className: className}));
+			  }
+			}
+		  });
+	}
+  }
+
+  CodeMirror.defineExtension("matchHighlight", function(className, minChars) {
+    markDocument(this, className, minChars);
+  });
+})();

+ 63 - 0
web/js/codemirror/lib/util/matchbrackets.js

@@ -0,0 +1,63 @@
+(function() {
+  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
+  function findMatchingBracket(cm) {
+    var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
+    var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
+    if (!match) return null;
+    var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
+    var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).type;
+
+    var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
+    function scan(line, lineNo, start) {
+      if (!line.text) return;
+      var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
+      if (start != null) pos = start + d;
+      for (; pos != end; pos += d) {
+        var ch = line.text.charAt(pos);
+        if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).type == style) {
+          var match = matching[ch];
+          if (match.charAt(1) == ">" == forward) stack.push(ch);
+          else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
+          else if (!stack.length) return {pos: pos, match: true};
+        }
+      }
+    }
+    for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
+      if (i == cur.line) found = scan(line, i, pos);
+      else found = scan(cm.getLineHandle(i), i);
+      if (found) break;
+    }
+    return {from: {line: cur.line, ch: pos}, to: found && {line: i, ch: found.pos}, match: found && found.match};
+  }
+
+  function matchBrackets(cm, autoclear) {
+    var found = findMatchingBracket(cm);
+    if (!found) return;
+    var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+    var one = cm.markText(found.from, {line: found.from.line, ch: found.from.ch + 1},
+                          {className: style});
+    var two = found.to && cm.markText(found.to, {line: found.to.line, ch: found.to.ch + 1},
+                                      {className: style});
+    var clear = function() {
+      cm.operation(function() { one.clear(); two && two.clear(); });
+    };
+    if (autoclear) setTimeout(clear, 800);
+    else return clear;
+  }
+
+  var currentlyHighlighted = null;
+  function doMatchBrackets(cm) {
+    cm.operation(function() {
+      if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+      if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
+    });
+  }
+
+  CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
+    if (val) cm.on("cursorActivity", doMatchBrackets);
+    else cm.off("cursorActivity", doMatchBrackets);
+  });
+
+  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+  CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
+})();

+ 95 - 0
web/js/codemirror/lib/util/multiplex.js

@@ -0,0 +1,95 @@
+CodeMirror.multiplexingMode = function(outer /*, others */) {
+  // Others should be {open, close, mode [, delimStyle]} objects
+  var others = Array.prototype.slice.call(arguments, 1);
+  var n_others = others.length;
+
+  function indexOf(string, pattern, from) {
+    if (typeof pattern == "string") return string.indexOf(pattern, from);
+    var m = pattern.exec(from ? string.slice(from) : string);
+    return m ? m.index + from : -1;
+  }
+
+  return {
+    startState: function() {
+      return {
+        outer: CodeMirror.startState(outer),
+        innerActive: null,
+        inner: null
+      };
+    },
+
+    copyState: function(state) {
+      return {
+        outer: CodeMirror.copyState(outer, state.outer),
+        innerActive: state.innerActive,
+        inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner)
+      };
+    },
+
+    token: function(stream, state) {
+      if (!state.innerActive) {
+        var cutOff = Infinity, oldContent = stream.string;
+        for (var i = 0; i < n_others; ++i) {
+          var other = others[i];
+          var found = indexOf(oldContent, other.open, stream.pos);
+          if (found == stream.pos) {
+            stream.match(other.open);
+            state.innerActive = other;
+            state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0);
+            return other.delimStyle;
+          } else if (found != -1 && found < cutOff) {
+            cutOff = found;
+          }
+        }
+        if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff);
+        var outerToken = outer.token(stream, state.outer);
+        if (cutOff != Infinity) stream.string = oldContent;
+        return outerToken;
+      } else {
+        var curInner = state.innerActive, oldContent = stream.string;
+        var found = indexOf(oldContent, curInner.close, stream.pos);
+        if (found == stream.pos) {
+          stream.match(curInner.close);
+          state.innerActive = state.inner = null;
+          return curInner.delimStyle;
+        }
+        if (found > -1) stream.string = oldContent.slice(0, found);
+        var innerToken = curInner.mode.token(stream, state.inner);
+        if (found > -1) stream.string = oldContent;
+        var cur = stream.current(), found = cur.indexOf(curInner.close);
+        if (found > -1) stream.backUp(cur.length - found);
+        return innerToken;
+      }
+    },
+    
+    indent: function(state, textAfter) {
+      var mode = state.innerActive ? state.innerActive.mode : outer;
+      if (!mode.indent) return CodeMirror.Pass;
+      return mode.indent(state.innerActive ? state.inner : state.outer, textAfter);
+    },
+
+    blankLine: function(state) {
+      var mode = state.innerActive ? state.innerActive.mode : outer;
+      if (mode.blankLine) {
+        mode.blankLine(state.innerActive ? state.inner : state.outer);
+      }
+      if (!state.innerActive) {
+        for (var i = 0; i < n_others; ++i) {
+          var other = others[i];
+          if (other.open === "\n") {
+            state.innerActive = other;
+            state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0);
+          }
+        }
+      } else if (mode.close === "\n") {
+        state.innerActive = state.inner = null;
+      }
+    },
+
+    electricChars: outer.electricChars,
+
+    innerMode: function(state) {
+      return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer};
+    }
+  };
+};

+ 59 - 0
web/js/codemirror/lib/util/overlay.js

@@ -0,0 +1,59 @@
+// Utility function that allows modes to be combined. The mode given
+// as the base argument takes care of most of the normal mode
+// functionality, but a second (typically simple) mode is used, which
+// can override the style of text. Both modes get to parse all of the
+// text, but when both assign a non-null style to a piece of code, the
+// overlay wins, unless the combine argument was true, in which case
+// the styles are combined.
+
+// overlayParser is the old, deprecated name
+CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, combine) {
+  return {
+    startState: function() {
+      return {
+        base: CodeMirror.startState(base),
+        overlay: CodeMirror.startState(overlay),
+        basePos: 0, baseCur: null,
+        overlayPos: 0, overlayCur: null
+      };
+    },
+    copyState: function(state) {
+      return {
+        base: CodeMirror.copyState(base, state.base),
+        overlay: CodeMirror.copyState(overlay, state.overlay),
+        basePos: state.basePos, baseCur: null,
+        overlayPos: state.overlayPos, overlayCur: null
+      };
+    },
+
+    token: function(stream, state) {
+      if (stream.start == state.basePos) {
+        state.baseCur = base.token(stream, state.base);
+        state.basePos = stream.pos;
+      }
+      if (stream.start == state.overlayPos) {
+        stream.pos = stream.start;
+        state.overlayCur = overlay.token(stream, state.overlay);
+        state.overlayPos = stream.pos;
+      }
+      stream.pos = Math.min(state.basePos, state.overlayPos);
+      if (stream.eol()) state.basePos = state.overlayPos = 0;
+
+      if (state.overlayCur == null) return state.baseCur;
+      if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
+      else return state.overlayCur;
+    },
+    
+    indent: base.indent && function(state, textAfter) {
+      return base.indent(state.base, textAfter);
+    },
+    electricChars: base.electricChars,
+
+    innerMode: function(state) { return {state: state.base, mode: base}; },
+    
+    blankLine: function(state) {
+      if (base.blankLine) base.blankLine(state.base);
+      if (overlay.blankLine) overlay.blankLine(state.overlay);
+    }
+  };
+};

+ 117 - 0
web/js/codemirror/lib/util/pig-hint.js

@@ -0,0 +1,117 @@
+(function () {
+  function forEach(arr, f) {
+    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
+  }
+  
+  function arrayContains(arr, item) {
+    if (!Array.prototype.indexOf) {
+      var i = arr.length;
+      while (i--) {
+        if (arr[i] === item) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return arr.indexOf(item) != -1;
+  }
+
+  function scriptHint(editor, _keywords, getToken) {
+    // Find the token at the cursor
+    var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+    // If it's not a 'word-style' token, ignore the token.
+
+    if (!/^[\w$_]*$/.test(token.string)) {
+        token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+                         className: token.string == ":" ? "pig-type" : null};
+    }
+      
+    if (!context) var context = [];
+    context.push(tprop);
+    
+    var completionList = getCompletions(token, context); 
+    completionList = completionList.sort();
+    //prevent autocomplete for last word, instead show dropdown with one word
+    if(completionList.length == 1) {
+      completionList.push(" ");
+    }
+
+    return {list: completionList,
+              from: {line: cur.line, ch: token.start},
+              to: {line: cur.line, ch: token.end}};
+  }
+  
+  CodeMirror.pigHint = function(editor) {
+    return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);});
+  };
+ 
+  var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP "
+  + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL "
+  + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE "
+  + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " 
+  + "NEQ MATCHES TRUE FALSE";
+  var pigKeywordsU = pigKeywords.split(" ");
+  var pigKeywordsL = pigKeywords.toLowerCase().split(" ");
+  
+  var pigTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP";
+  var pigTypesU = pigTypes.split(" ");
+  var pigTypesL = pigTypes.toLowerCase().split(" ");
+  
+  var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " 
+  + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS "
+  + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG "
+  + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN "
+  + "INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER "
+  + "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS "
+  + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA  "
+  + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE "
+  + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG "
+  + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER";  
+  var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" ");  
+  var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" ");   
+  var pigBuiltinsC = ("BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs "
+  + "DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax "
+  + "FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum "
+  + "InvokeForDouble InvokeForFloat InvokeForInt InvokeForLong InvokeForString Invoker "
+  + "IsEmpty JsonLoader JsonMetadata JsonStorage LongAbs LongAvg LongMax LongMin LongSum MapSize "
+  + "MonitoredUDF Nondeterministic OutputSchema PigStorage PigStreaming StringConcat StringMax "
+  + "StringMin StringSize TextLoader TupleSize Utf8StorageConverter").split(" ").join("() ").split(" ");
+                    
+  function getCompletions(token, context) {
+    var found = [], start = token.string;
+    function maybeAdd(str) {
+      if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str);
+    }
+    
+    function gatherCompletions(obj) {
+      if(obj == ":") {
+        forEach(pigTypesL, maybeAdd);
+      }
+      else {
+        forEach(pigBuiltinsU, maybeAdd);
+        forEach(pigBuiltinsL, maybeAdd);
+        forEach(pigBuiltinsC, maybeAdd);
+        forEach(pigTypesU, maybeAdd);
+        forEach(pigTypesL, maybeAdd);
+        forEach(pigKeywordsU, maybeAdd);
+        forEach(pigKeywordsL, maybeAdd);
+      }
+    }
+
+    if (context) {
+      // If this is a property, see if it belongs to some object we can
+      // find in the current environment.
+      var obj = context.pop(), base;
+
+      if (obj.type == "variable") 
+          base = obj.string;
+      else if(obj.type == "variable-3")
+          base = ":" + obj.string;
+        
+      while (base != null && context.length)
+        base = base[context.pop().string];
+      if (base != null) gatherCompletions(base);
+    }
+    return found;
+  }
+})();

+ 90 - 0
web/js/codemirror/lib/util/runmode-standalone.js

@@ -0,0 +1,90 @@
+/* Just enough of CodeMirror to run runMode under node.js */
+
+function splitLines(string){ return string.split(/\r?\n|\r/); };
+
+function StringStream(string) {
+  this.pos = this.start = 0;
+  this.string = string;
+}
+StringStream.prototype = {
+  eol: function() {return this.pos >= this.string.length;},
+  sol: function() {return this.pos == 0;},
+  peek: function() {return this.string.charAt(this.pos) || null;},
+  next: function() {
+    if (this.pos < this.string.length)
+      return this.string.charAt(this.pos++);
+  },
+  eat: function(match) {
+    var ch = this.string.charAt(this.pos);
+    if (typeof match == "string") var ok = ch == match;
+    else var ok = ch && (match.test ? match.test(ch) : match(ch));
+    if (ok) {++this.pos; return ch;}
+  },
+  eatWhile: function(match) {
+    var start = this.pos;
+    while (this.eat(match)){}
+    return this.pos > start;
+  },
+  eatSpace: function() {
+    var start = this.pos;
+    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+    return this.pos > start;
+  },
+  skipToEnd: function() {this.pos = this.string.length;},
+  skipTo: function(ch) {
+    var found = this.string.indexOf(ch, this.pos);
+    if (found > -1) {this.pos = found; return true;}
+  },
+  backUp: function(n) {this.pos -= n;},
+  column: function() {return this.start;},
+  indentation: function() {return 0;},
+  match: function(pattern, consume, caseInsensitive) {
+    if (typeof pattern == "string") {
+      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
+      if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
+        if (consume !== false) this.pos += pattern.length;
+        return true;
+      }
+    }
+    else {
+      var match = this.string.slice(this.pos).match(pattern);
+      if (match && consume !== false) this.pos += match[0].length;
+      return match;
+    }
+  },
+  current: function(){return this.string.slice(this.start, this.pos);}
+};
+exports.StringStream = StringStream;
+
+exports.startState = function(mode, a1, a2) {
+  return mode.startState ? mode.startState(a1, a2) : true;
+};
+
+var modes = exports.modes = {}, mimeModes = exports.mimeModes = {};
+exports.defineMode = function(name, mode) { modes[name] = mode; };
+exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; };
+exports.getMode = function(options, spec) {
+  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
+    spec = mimeModes[spec];
+  if (typeof spec == "string")
+    var mname = spec, config = {};
+  else if (spec != null)
+    var mname = spec.name, config = spec;
+  var mfactory = modes[mname];
+  if (!mfactory) throw new Error("Unknown mode: " + spec);
+  return mfactory(options, config || {});
+};
+
+exports.runMode = function(string, modespec, callback) {
+  var mode = exports.getMode({indentUnit: 2}, modespec);
+  var lines = splitLines(string), state = exports.startState(mode);
+  for (var i = 0, e = lines.length; i < e; ++i) {
+    if (i) callback("\n");
+    var stream = new exports.StringStream(lines[i]);
+    while (!stream.eol()) {
+      var style = mode.token(stream, state);
+      callback(stream.current(), style, i, stream.start);
+      stream.start = stream.pos;
+    }
+  }
+};

+ 52 - 0
web/js/codemirror/lib/util/runmode.js

@@ -0,0 +1,52 @@
+CodeMirror.runMode = function(string, modespec, callback, options) {
+  var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
+
+  if (callback.nodeType == 1) {
+    var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
+    var node = callback, col = 0;
+    node.innerHTML = "";
+    callback = function(text, style) {
+      if (text == "\n") {
+        node.appendChild(document.createElement("br"));
+        col = 0;
+        return;
+      }
+      var content = "";
+      // replace tabs
+      for (var pos = 0;;) {
+        var idx = text.indexOf("\t", pos);
+        if (idx == -1) {
+          content += text.slice(pos);
+          col += text.length - pos;
+          break;
+        } else {
+          col += idx - pos;
+          content += text.slice(pos, idx);
+          var size = tabSize - col % tabSize;
+          col += size;
+          for (var i = 0; i < size; ++i) content += " ";
+          pos = idx + 1;
+        }
+      }
+
+      if (style) {
+        var sp = node.appendChild(document.createElement("span"));
+        sp.className = "cm-" + style.replace(/ +/g, " cm-");
+        sp.appendChild(document.createTextNode(content));
+      } else {
+        node.appendChild(document.createTextNode(content));
+      }
+    };
+  }
+
+  var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode);
+  for (var i = 0, e = lines.length; i < e; ++i) {
+    if (i) callback("\n");
+    var stream = new CodeMirror.StringStream(lines[i]);
+    while (!stream.eol()) {
+      var style = mode.token(stream, state);
+      callback(stream.current(), style, i, stream.start);
+      stream.start = stream.pos;
+    }
+  }
+};

+ 119 - 0
web/js/codemirror/lib/util/search.js

@@ -0,0 +1,119 @@
+// Define search commands. Depends on dialog.js or another
+// implementation of the openDialog method.
+
+// Replace works a little oddly -- it will do the replace on the next
+// Ctrl-G (or whatever is bound to findNext) press. You prevent a
+// replace by making sure the match is no longer selected when hitting
+// Ctrl-G.
+
+(function() {
+  function SearchState() {
+    this.posFrom = this.posTo = this.query = null;
+    this.marked = [];
+  }
+  function getSearchState(cm) {
+    return cm._searchState || (cm._searchState = new SearchState());
+  }
+  function getSearchCursor(cm, query, pos) {
+    // Heuristic: if the query string is all lowercase, do a case insensitive search.
+    return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase());
+  }
+  function dialog(cm, text, shortText, f) {
+    if (cm.openDialog) cm.openDialog(text, f);
+    else f(prompt(shortText, ""));
+  }
+  function confirmDialog(cm, text, shortText, fs) {
+    if (cm.openConfirm) cm.openConfirm(text, fs);
+    else if (confirm(shortText)) fs[0]();
+  }
+  function parseQuery(query) {
+    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
+    return isRE ? new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i") : query;
+  }
+  var queryDialog =
+    'Search: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
+  function doSearch(cm, rev) {
+    var state = getSearchState(cm);
+    if (state.query) return findNext(cm, rev);
+    dialog(cm, queryDialog, "Search for:", function(query) {
+      cm.operation(function() {
+        if (!query || state.query) return;
+        state.query = parseQuery(query);
+        if (cm.lineCount() < 2000) { // This is too expensive on big documents.
+          for (var cursor = getSearchCursor(cm, state.query); cursor.findNext();)
+            state.marked.push(cm.markText(cursor.from(), cursor.to(),
+                                          {className: "CodeMirror-searching"}));
+        }
+        state.posFrom = state.posTo = cm.getCursor();
+        findNext(cm, rev);
+      });
+    });
+  }
+  function findNext(cm, rev) {cm.operation(function() {
+    var state = getSearchState(cm);
+    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
+    if (!cursor.find(rev)) {
+      cursor = getSearchCursor(cm, state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
+      if (!cursor.find(rev)) return;
+    }
+    cm.setSelection(cursor.from(), cursor.to());
+    state.posFrom = cursor.from(); state.posTo = cursor.to();
+  });}
+  function clearSearch(cm) {cm.operation(function() {
+    var state = getSearchState(cm);
+    if (!state.query) return;
+    state.query = null;
+    for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear();
+    state.marked.length = 0;
+  });}
+
+  var replaceQueryDialog =
+    'Replace: <input type="text" style="width: 10em"/> <span style="color: #888">(Use /re/ syntax for regexp search)</span>';
+  var replacementQueryDialog = 'With: <input type="text" style="width: 10em"/>';
+  var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>";
+  function replace(cm, all) {
+    dialog(cm, replaceQueryDialog, "Replace:", function(query) {
+      if (!query) return;
+      query = parseQuery(query);
+      dialog(cm, replacementQueryDialog, "Replace with:", function(text) {
+        if (all) {
+          cm.operation(function() {
+            for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
+              if (typeof query != "string") {
+                var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+                cursor.replace(text.replace(/\$(\d)/, function(_, i) {return match[i];}));
+              } else cursor.replace(text);
+            }
+          });
+        } else {
+          clearSearch(cm);
+          var cursor = getSearchCursor(cm, query, cm.getCursor());
+          function advance() {
+            var start = cursor.from(), match;
+            if (!(match = cursor.findNext())) {
+              cursor = getSearchCursor(cm, query);
+              if (!(match = cursor.findNext()) ||
+                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
+            }
+            cm.setSelection(cursor.from(), cursor.to());
+            confirmDialog(cm, doReplaceConfirm, "Replace?",
+                          [function() {doReplace(match);}, advance]);
+          }
+          function doReplace(match) {
+            cursor.replace(typeof query == "string" ? text :
+                           text.replace(/\$(\d)/, function(_, i) {return match[i];}));
+            advance();
+          }
+          advance();
+        }
+      });
+    });
+  }
+
+  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
+  CodeMirror.commands.findNext = doSearch;
+  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
+  CodeMirror.commands.clearSearch = clearSearch;
+  CodeMirror.commands.replace = replace;
+  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
+})();

+ 119 - 0
web/js/codemirror/lib/util/searchcursor.js

@@ -0,0 +1,119 @@
+(function(){
+  function SearchCursor(cm, query, pos, caseFold) {
+    this.atOccurrence = false; this.cm = cm;
+    if (caseFold == null && typeof query == "string") caseFold = false;
+
+    pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
+    this.pos = {from: pos, to: pos};
+
+    // The matches method is filled in based on the type of query.
+    // It takes a position and a direction, and returns an object
+    // describing the next occurrence of the query, or null if no
+    // more matches were found.
+    if (typeof query != "string") { // Regexp match
+      if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g");
+      this.matches = function(reverse, pos) {
+        if (reverse) {
+          query.lastIndex = 0;
+          var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0;
+          while (match) {
+            start += match.index + 1;
+            line = line.slice(start);
+            query.lastIndex = 0;
+            var newmatch = query.exec(line);
+            if (newmatch) match = newmatch;
+            else break;
+          }
+          start--;
+        } else {
+          query.lastIndex = pos.ch;
+          var line = cm.getLine(pos.line), match = query.exec(line),
+          start = match && match.index;
+        }
+        if (match)
+          return {from: {line: pos.line, ch: start},
+                  to: {line: pos.line, ch: start + match[0].length},
+                  match: match};
+      };
+    } else { // String query
+      if (caseFold) query = query.toLowerCase();
+      var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
+      var target = query.split("\n");
+      // Different methods for single-line and multi-line queries
+      if (target.length == 1)
+        this.matches = function(reverse, pos) {
+          var line = fold(cm.getLine(pos.line)), len = query.length, match;
+          if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
+              : (match = line.indexOf(query, pos.ch)) != -1)
+            return {from: {line: pos.line, ch: match},
+                    to: {line: pos.line, ch: match + len}};
+        };
+      else
+        this.matches = function(reverse, pos) {
+          var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
+          var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
+          if (reverse ? offsetA >= pos.ch || offsetA != match.length
+              : offsetA <= pos.ch || offsetA != line.length - match.length)
+            return;
+          for (;;) {
+            if (reverse ? !ln : ln == cm.lineCount() - 1) return;
+            line = fold(cm.getLine(ln += reverse ? -1 : 1));
+            match = target[reverse ? --idx : ++idx];
+            if (idx > 0 && idx < target.length - 1) {
+              if (line != match) return;
+              else continue;
+            }
+            var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
+            if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
+              return;
+            var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
+            return {from: reverse ? end : start, to: reverse ? start : end};
+          }
+        };
+    }
+  }
+
+  SearchCursor.prototype = {
+    findNext: function() {return this.find(false);},
+    findPrevious: function() {return this.find(true);},
+
+    find: function(reverse) {
+      var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
+      function savePosAndFail(line) {
+        var pos = {line: line, ch: 0};
+        self.pos = {from: pos, to: pos};
+        self.atOccurrence = false;
+        return false;
+      }
+
+      for (;;) {
+        if (this.pos = this.matches(reverse, pos)) {
+          this.atOccurrence = true;
+          return this.pos.match || true;
+        }
+        if (reverse) {
+          if (!pos.line) return savePosAndFail(0);
+          pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
+        }
+        else {
+          var maxLine = this.cm.lineCount();
+          if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
+          pos = {line: pos.line+1, ch: 0};
+        }
+      }
+    },
+
+    from: function() {if (this.atOccurrence) return this.pos.from;},
+    to: function() {if (this.atOccurrence) return this.pos.to;},
+
+    replace: function(newText) {
+      var self = this;
+      if (this.atOccurrence)
+        self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to);
+    }
+  };
+
+  CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
+    return new SearchCursor(this, query, pos, caseFold);
+  });
+})();

+ 16 - 0
web/js/codemirror/lib/util/simple-hint.css

@@ -0,0 +1,16 @@
+.CodeMirror-completions {
+  position: absolute;
+  z-index: 10;
+  overflow: hidden;
+  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+}
+.CodeMirror-completions select {
+  background: #fafafa;
+  outline: none;
+  border: none;
+  padding: 0;
+  margin: 0;
+  font-family: monospace;
+}

+ 102 - 0
web/js/codemirror/lib/util/simple-hint.js

@@ -0,0 +1,102 @@
+(function() {
+  CodeMirror.simpleHint = function(editor, getHints, givenOptions) {
+    // Determine effective options based on given values and defaults.
+    var options = {}, defaults = CodeMirror.simpleHint.defaults;
+    for (var opt in defaults)
+      if (defaults.hasOwnProperty(opt))
+        options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
+    
+    function collectHints(previousToken) {
+      // We want a single cursor position.
+      if (editor.somethingSelected()) return;
+
+      var tempToken = editor.getTokenAt(editor.getCursor());
+
+      // Don't show completions if token has changed and the option is set.
+      if (options.closeOnTokenChange && previousToken != null &&
+          (tempToken.start != previousToken.start || tempToken.type != previousToken.type)) {
+        return;
+      }
+
+      var result = getHints(editor, givenOptions);
+      if (!result || !result.list.length) return;
+      var completions = result.list;
+      function insert(str) {
+        editor.replaceRange(str, result.from, result.to);
+      }
+      // When there is only one completion, use it directly.
+      if (options.completeSingle && completions.length == 1) {
+        insert(completions[0]);
+        return true;
+      }
+
+      // Build the select widget
+      var complete = document.createElement("div");
+      complete.className = "CodeMirror-completions";
+      var sel = complete.appendChild(document.createElement("select"));
+      // Opera doesn't move the selection when pressing up/down in a
+      // multi-select, but it does properly support the size property on
+      // single-selects, so no multi-select is necessary.
+      if (!window.opera) sel.multiple = true;
+      for (var i = 0; i < completions.length; ++i) {
+        var opt = sel.appendChild(document.createElement("option"));
+        opt.appendChild(document.createTextNode(completions[i]));
+      }
+      sel.firstChild.selected = true;
+      sel.size = Math.min(10, completions.length);
+      var pos = editor.cursorCoords(options.alignWithWord ? result.from : null);
+      complete.style.left = pos.left + "px";
+      complete.style.top = pos.bottom + "px";
+      document.body.appendChild(complete);
+      // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
+      var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
+      if(winW - pos.left < sel.clientWidth)
+        complete.style.left = (pos.left - sel.clientWidth) + "px";
+      // Hack to hide the scrollbar.
+      if (completions.length <= 10)
+        complete.style.width = (sel.clientWidth - 1) + "px";
+
+      var done = false;
+      function close() {
+        if (done) return;
+        done = true;
+        complete.parentNode.removeChild(complete);
+      }
+      function pick() {
+        insert(completions[sel.selectedIndex]);
+        close();
+        setTimeout(function(){editor.focus();}, 50);
+      }
+      CodeMirror.on(sel, "blur", close);
+      CodeMirror.on(sel, "keydown", function(event) {
+        var code = event.keyCode;
+        // Enter
+        if (code == 13) {CodeMirror.e_stop(event); pick();}
+        // Escape
+        else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();}
+        else if (code != 38 && code != 40 && code != 33 && code != 34 && !CodeMirror.isModifierKey(event)) {
+          close(); editor.focus();
+          // Pass the event to the CodeMirror instance so that it can handle things like backspace properly.
+          editor.triggerOnKeyDown(event);
+          // Don't show completions if the code is backspace and the option is set.
+          if (!options.closeOnBackspace || code != 8) {
+            setTimeout(function(){collectHints(tempToken);}, 50);
+          }
+        }
+      });
+      CodeMirror.on(sel, "dblclick", pick);
+
+      sel.focus();
+      // Opera sometimes ignores focusing a freshly created node
+      if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100);
+      return true;
+    }
+    return collectHints();
+  };
+  CodeMirror.simpleHint.defaults = {
+    closeOnBackspace: true,
+    closeOnTokenChange: false,
+    completeSingle: true,
+    alignWithWord: true
+  };
+})();

+ 131 - 0
web/js/codemirror/lib/util/xml-hint.js

@@ -0,0 +1,131 @@
+
+(function() {
+
+    CodeMirror.xmlHints = [];
+
+    CodeMirror.xmlHint = function(cm, simbol) {
+
+        if(simbol.length > 0) {
+            var cursor = cm.getCursor();
+            cm.replaceSelection(simbol);
+            cursor = {line: cursor.line, ch: cursor.ch + 1};
+            cm.setCursor(cursor);
+        }
+
+        CodeMirror.simpleHint(cm, getHint);
+    };
+
+    var getHint = function(cm) {
+
+        var cursor = cm.getCursor();
+
+        if (cursor.ch > 0) {
+
+            var text = cm.getRange({line: 0, ch: 0}, cursor);
+            var typed = '';
+            var simbol = '';
+            for(var i = text.length - 1; i >= 0; i--) {
+                if(text[i] == ' ' || text[i] == '<') {
+                    simbol = text[i];
+                    break;
+                }
+                else {
+                    typed = text[i] + typed;
+                }
+            }
+
+            text = text.slice(0, text.length - typed.length);
+
+            var path = getActiveElement(text) + simbol;
+            var hints = CodeMirror.xmlHints[path];
+
+            if(typeof hints === 'undefined')
+                hints = [''];
+            else {
+                hints = hints.slice(0);
+                for (var i = hints.length - 1; i >= 0; i--) {
+                    if(hints[i].indexOf(typed) != 0)
+                        hints.splice(i, 1);
+                }
+            }
+
+            return {
+                list: hints,
+                from: { line: cursor.line, ch: cursor.ch - typed.length },
+                to: cursor
+            };
+        };
+    };
+
+    var getActiveElement = function(text) {
+
+        var element = '';
+
+        if(text.length >= 0) {
+
+            var regex = new RegExp('<([^!?][^\\s/>]*).*?>', 'g');
+
+            var matches = [];
+            var match;
+            while ((match = regex.exec(text)) != null) {
+                matches.push({
+                    tag: match[1],
+                    selfclose: (match[0].slice(match[0].length - 2) === '/>')
+                });
+            }
+
+            for (var i = matches.length - 1, skip = 0; i >= 0; i--) {
+
+                var item = matches[i];
+
+                if (item.tag[0] == '/')
+                {
+                    skip++;
+                }
+                else if (item.selfclose == false)
+                {
+                    if (skip > 0)
+                    {
+                        skip--;
+                    }
+                    else
+                    {
+                        element = '<' + item.tag + '>' + element;
+                    }
+                }
+            }
+
+            element += getOpenTag(text);
+        }
+
+        return element;
+    };
+
+    var getOpenTag = function(text) {
+
+        var open = text.lastIndexOf('<');
+        var close = text.lastIndexOf('>');
+
+        if (close < open)
+        {
+            text = text.slice(open);
+
+            if(text != '<') {
+
+                var space = text.indexOf(' ');
+                if(space < 0)
+                    space = text.indexOf('\t');
+                if(space < 0)
+                    space = text.indexOf('\n');
+
+                if (space < 0)
+                    space = text.length;
+
+                return text.slice(0, space);
+            }
+        }
+
+        return '';
+    };
+
+})();

+ 465 - 0
web/js/codemirror/mode/css/css.js

@@ -0,0 +1,465 @@
+CodeMirror.defineMode("css", function(config) {
+  var indentUnit = config.indentUnit, type;
+  
+  var atMediaTypes = keySet([
+    "all", "aural", "braille", "handheld", "print", "projection", "screen",
+    "tty", "tv", "embossed"
+  ]);
+  
+  var atMediaFeatures = keySet([
+    "width", "min-width", "max-width", "height", "min-height", "max-height",
+    "device-width", "min-device-width", "max-device-width", "device-height",
+    "min-device-height", "max-device-height", "aspect-ratio",
+    "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
+    "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
+    "max-color", "color-index", "min-color-index", "max-color-index",
+    "monochrome", "min-monochrome", "max-monochrome", "resolution",
+    "min-resolution", "max-resolution", "scan", "grid"
+  ]);
+
+  var propertyKeywords = keySet([
+    "align-content", "align-items", "align-self", "alignment-adjust",
+    "alignment-baseline", "anchor-point", "animation", "animation-delay",
+    "animation-direction", "animation-duration", "animation-iteration-count",
+    "animation-name", "animation-play-state", "animation-timing-function",
+    "appearance", "azimuth", "backface-visibility", "background",
+    "background-attachment", "background-clip", "background-color",
+    "background-image", "background-origin", "background-position",
+    "background-repeat", "background-size", "baseline-shift", "binding",
+    "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
+    "bookmark-target", "border", "border-bottom", "border-bottom-color",
+    "border-bottom-left-radius", "border-bottom-right-radius",
+    "border-bottom-style", "border-bottom-width", "border-collapse",
+    "border-color", "border-image", "border-image-outset",
+    "border-image-repeat", "border-image-slice", "border-image-source",
+    "border-image-width", "border-left", "border-left-color",
+    "border-left-style", "border-left-width", "border-radius", "border-right",
+    "border-right-color", "border-right-style", "border-right-width",
+    "border-spacing", "border-style", "border-top", "border-top-color",
+    "border-top-left-radius", "border-top-right-radius", "border-top-style",
+    "border-top-width", "border-width", "bottom", "box-decoration-break",
+    "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
+    "caption-side", "clear", "clip", "color", "color-profile", "column-count",
+    "column-fill", "column-gap", "column-rule", "column-rule-color",
+    "column-rule-style", "column-rule-width", "column-span", "column-width",
+    "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
+    "cue-after", "cue-before", "cursor", "direction", "display",
+    "dominant-baseline", "drop-initial-after-adjust",
+    "drop-initial-after-align", "drop-initial-before-adjust",
+    "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
+    "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
+    "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
+    "float", "float-offset", "font", "font-feature-settings", "font-family",
+    "font-kerning", "font-language-override", "font-size", "font-size-adjust",
+    "font-stretch", "font-style", "font-synthesis", "font-variant",
+    "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
+    "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
+    "font-weight", "grid-cell", "grid-column", "grid-column-align",
+    "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
+    "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
+    "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
+    "icon", "image-orientation", "image-rendering", "image-resolution",
+    "inline-box-align", "justify-content", "left", "letter-spacing",
+    "line-break", "line-height", "line-stacking", "line-stacking-ruby",
+    "line-stacking-shift", "line-stacking-strategy", "list-style",
+    "list-style-image", "list-style-position", "list-style-type", "margin",
+    "margin-bottom", "margin-left", "margin-right", "margin-top",
+    "marker-offset", "marks", "marquee-direction", "marquee-loop",
+    "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
+    "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
+    "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
+    "outline-color", "outline-offset", "outline-style", "outline-width",
+    "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
+    "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
+    "page", "page-break-after", "page-break-before", "page-break-inside",
+    "page-policy", "pause", "pause-after", "pause-before", "perspective",
+    "perspective-origin", "pitch", "pitch-range", "play-during", "position",
+    "presentation-level", "punctuation-trim", "quotes", "rendering-intent",
+    "resize", "rest", "rest-after", "rest-before", "richness", "right",
+    "rotation", "rotation-point", "ruby-align", "ruby-overhang",
+    "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
+    "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
+    "tab-size", "table-layout", "target", "target-name", "target-new",
+    "target-position", "text-align", "text-align-last", "text-decoration",
+    "text-decoration-color", "text-decoration-line", "text-decoration-skip",
+    "text-decoration-style", "text-emphasis", "text-emphasis-color",
+    "text-emphasis-position", "text-emphasis-style", "text-height",
+    "text-indent", "text-justify", "text-outline", "text-shadow",
+    "text-space-collapse", "text-transform", "text-underline-position",
+    "text-wrap", "top", "transform", "transform-origin", "transform-style",
+    "transition", "transition-delay", "transition-duration",
+    "transition-property", "transition-timing-function", "unicode-bidi",
+    "vertical-align", "visibility", "voice-balance", "voice-duration",
+    "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
+    "voice-volume", "volume", "white-space", "widows", "width", "word-break",
+    "word-spacing", "word-wrap", "z-index"
+  ]);
+
+  var colorKeywords = keySet([
+    "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
+    "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
+  ]);
+  
+  var valueKeywords = keySet([
+    "above", "absolute", "activeborder", "activecaption", "afar",
+    "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
+    "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
+    "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
+    "backwards", "baseline", "below", "bidi-override", "binary", "bengali",
+    "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
+    "both", "bottom", "break-all", "break-word", "button", "button-bevel",
+    "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
+    "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
+    "cell", "center", "checkbox", "circle", "cjk-earthly-branch",
+    "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
+    "col-resize", "collapse", "compact", "condensed", "contain", "content",
+    "content-box", "context-menu", "continuous", "copy", "cover", "crop",
+    "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
+    "decimal-leading-zero", "default", "default-button", "destination-atop",
+    "destination-in", "destination-out", "destination-over", "devanagari",
+    "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
+    "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
+    "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
+    "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
+    "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
+    "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
+    "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
+    "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
+    "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
+    "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
+    "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
+    "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
+    "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
+    "help", "hidden", "hide", "higher", "highlight", "highlighttext",
+    "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
+    "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
+    "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
+    "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
+    "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
+    "landscape", "lao", "large", "larger", "left", "level", "lighter",
+    "line-through", "linear", "lines", "list-item", "listbox", "listitem",
+    "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
+    "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
+    "lower-roman", "lowercase", "ltr", "malayalam", "match",
+    "media-controls-background", "media-current-time-display",
+    "media-fullscreen-button", "media-mute-button", "media-play-button",
+    "media-return-to-realtime-button", "media-rewind-button",
+    "media-seek-back-button", "media-seek-forward-button", "media-slider",
+    "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
+    "media-volume-slider-container", "media-volume-sliderthumb", "medium",
+    "menu", "menulist", "menulist-button", "menulist-text",
+    "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
+    "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
+    "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
+    "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
+    "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
+    "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
+    "outside", "overlay", "overline", "padding", "padding-box", "painted",
+    "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
+    "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
+    "radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
+    "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
+    "ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
+    "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
+    "searchfield-cancel-button", "searchfield-decoration",
+    "searchfield-results-button", "searchfield-results-decoration",
+    "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
+    "single", "skip-white-space", "slide", "slider-horizontal",
+    "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
+    "small", "small-caps", "small-caption", "smaller", "solid", "somali",
+    "source-atop", "source-in", "source-out", "source-over", "space", "square",
+    "square-button", "start", "static", "status-bar", "stretch", "stroke",
+    "sub", "subpixel-antialiased", "super", "sw-resize", "table",
+    "table-caption", "table-cell", "table-column", "table-column-group",
+    "table-footer-group", "table-header-group", "table-row", "table-row-group",
+    "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
+    "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
+    "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
+    "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
+    "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
+    "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
+    "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
+    "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
+    "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
+    "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
+    "xx-large", "xx-small", "yellow"
+  ]);
+
+  function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; }
+  function ret(style, tp) {type = tp; return style;}
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
+    else if (ch == "/" && stream.eat("*")) {
+      state.tokenize = tokenCComment;
+      return tokenCComment(stream, state);
+    }
+    else if (ch == "<" && stream.eat("!")) {
+      state.tokenize = tokenSGMLComment;
+      return tokenSGMLComment(stream, state);
+    }
+    else if (ch == "=") ret(null, "compare");
+    else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
+    else if (ch == "\"" || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    }
+    else if (ch == "#") {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("atom", "hash");
+    }
+    else if (ch == "!") {
+      stream.match(/^\s*\w*/);
+      return ret("keyword", "important");
+    }
+    else if (/\d/.test(ch)) {
+      stream.eatWhile(/[\w.%]/);
+      return ret("number", "unit");
+    }
+    else if (ch === "-") {
+      if (/\d/.test(stream.peek())) {
+        stream.eatWhile(/[\w.%]/);
+        return ret("number", "unit");
+      } else if (stream.match(/^[^-]+-/)) {
+        return ret("meta", type);
+      }
+    }
+    else if (/[,+>*\/]/.test(ch)) {
+      return ret(null, "select-op");
+    }
+    else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
+      return ret("qualifier", type);
+    }
+    else if (ch == ":") {
+      return ret("operator", ch);
+    }
+    else if (/[;{}\[\]\(\)]/.test(ch)) {
+      return ret(null, ch);
+    }
+    else if (ch == "u" && stream.match("rl(")) {
+      stream.backUp(1);
+      state.tokenize = tokenParenthesized;
+      return ret("property", "variable");
+    }
+    else {
+      stream.eatWhile(/[\w\\\-]/);
+      return ret("property", "variable");
+    }
+  }
+
+  function tokenCComment(stream, state) {
+    var maybeEnd = false, ch;
+    while ((ch = stream.next()) != null) {
+      if (maybeEnd && ch == "/") {
+        state.tokenize = tokenBase;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ret("comment", "comment");
+  }
+
+  function tokenSGMLComment(stream, state) {
+    var dashes = 0, ch;
+    while ((ch = stream.next()) != null) {
+      if (dashes >= 2 && ch == ">") {
+        state.tokenize = tokenBase;
+        break;
+      }
+      dashes = (ch == "-") ? dashes + 1 : 0;
+    }
+    return ret("comment", "comment");
+  }
+
+  function tokenString(quote, nonInclusive) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped)
+          break;
+        escaped = !escaped && ch == "\\";
+      }
+      if (!escaped) {
+        if (nonInclusive) stream.backUp(1);
+        state.tokenize = tokenBase;
+      }
+      return ret("string", "string");
+    };
+  }
+
+  function tokenParenthesized(stream, state) {
+    stream.next(); // Must be '('
+    if (!stream.match(/\s*[\"\']/, false))
+      state.tokenize = tokenString(")", true);
+    else
+      state.tokenize = tokenBase;
+    return ret(null, "(");
+  }
+
+  return {
+    startState: function(base) {
+      return {tokenize: tokenBase,
+              baseIndent: base || 0,
+              stack: []};
+    },
+
+    token: function(stream, state) {
+      
+      // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
+      // 
+      // rule** or **ruleset:
+      // A selector + braces combo, or an at-rule.
+      // 
+      // declaration block:
+      // A sequence of declarations.
+      // 
+      // declaration:
+      // A property + colon + value combo.
+      // 
+      // property value:
+      // The entire value of a property.
+      // 
+      // component value:
+      // A single piece of a property value. Like the 5px in
+      // text-shadow: 0 0 5px blue;. Can also refer to things that are
+      // multiple terms, like the 1-4 terms that make up the background-size
+      // portion of the background shorthand.
+      // 
+      // term:
+      // The basic unit of author-facing CSS, like a single number (5),
+      // dimension (5px), string ("foo"), or function. Officially defined
+      //  by the CSS 2.1 grammar (look for the 'term' production)
+      // 
+      // 
+      // simple selector:
+      // A single atomic selector, like a type selector, an attr selector, a
+      // class selector, etc.
+      // 
+      // compound selector:
+      // One or more simple selectors without a combinator. div.example is
+      // compound, div > .example is not.
+      // 
+      // complex selector:
+      // One or more compound selectors chained with combinators.
+      // 
+      // combinator:
+      // The parts of selectors that express relationships. There are four
+      // currently - the space (descendant combinator), the greater-than
+      // bracket (child combinator), the plus sign (next sibling combinator),
+      // and the tilda (following sibling combinator).
+      // 
+      // sequence of selectors:
+      // One or more of the named type of selector chained with commas.
+
+      if (state.tokenize == tokenBase && stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+
+      // Changing style returned based on context
+      var context = state.stack[state.stack.length-1];
+      if (style == "property") {
+        if (context == "propertyValue"){
+          if (valueKeywords[stream.current()]) {
+            style = "string-2";
+          } else if (colorKeywords[stream.current()]) {
+            style = "keyword";
+          } else {
+            style = "variable-2";
+          }
+        } else if (context == "rule") {
+          if (!propertyKeywords[stream.current()]) {
+            style += " error";
+          }
+        } else if (!context || context == "@media{") {
+          style = "tag";
+        } else if (context == "@media") {
+          if (atMediaTypes[stream.current()]) {
+            style = "attribute"; // Known attribute
+          } else if (/^(only|not)$/i.test(stream.current())) {
+            style = "keyword";
+          } else if (stream.current().toLowerCase() == "and") {
+            style = "error"; // "and" is only allowed in @mediaType
+          } else if (atMediaFeatures[stream.current()]) {
+            style = "error"; // Known property, should be in @mediaType(
+          } else {
+            // Unknown, expecting keyword or attribute, assuming attribute
+            style = "attribute error";
+          }
+        } else if (context == "@mediaType") {
+          if (atMediaTypes[stream.current()]) {
+            style = "attribute";
+          } else if (stream.current().toLowerCase() == "and") {
+            style = "operator";
+          } else if (/^(only|not)$/i.test(stream.current())) {
+            style = "error"; // Only allowed in @media
+          } else if (atMediaFeatures[stream.current()]) {
+            style = "error"; // Known property, should be in parentheses
+          } else {
+            // Unknown attribute or property, but expecting property (preceded
+            // by "and"). Should be in parentheses
+            style = "error";
+          }
+        } else if (context == "@mediaType(") {
+          if (propertyKeywords[stream.current()]) {
+            // do nothing, remains "property"
+          } else if (atMediaTypes[stream.current()]) {
+            style = "error"; // Known property, should be in parentheses
+          } else if (stream.current().toLowerCase() == "and") {
+            style = "operator";
+          } else if (/^(only|not)$/i.test(stream.current())) {
+            style = "error"; // Only allowed in @media
+          } else {
+            style += " error";
+          }
+        } else {
+          style = "error";
+        }
+      } else if (style == "atom") {
+        if(!context || context == "@media{") {
+          style = "builtin";
+        } else if (context == "propertyValue") {
+          if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
+            style += " error";
+          }
+        } else {
+          style = "error";
+        }
+      } else if (context == "@media" && type == "{") {
+        style = "error";
+      }
+
+      // Push/pop context stack
+      if (type == "{") {
+        if (context == "@media" || context == "@mediaType") {
+          state.stack.pop();
+          state.stack[state.stack.length-1] = "@media{";
+        }
+        else state.stack.push("rule");
+      }
+      else if (type == "}") {
+        state.stack.pop();
+        if (context == "propertyValue") state.stack.pop();
+      }
+      else if (type == "@media") state.stack.push("@media");
+      else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
+        state.stack.push("@mediaType");
+      else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
+      else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
+      else if (context == "@mediaType(" && type == ")") state.stack.pop();
+      else if (context == "rule" && type == ":") state.stack.push("propertyValue");
+      else if (context == "propertyValue" && type == ";") state.stack.pop();
+      return style;
+    },
+
+    indent: function(state, textAfter) {
+      var n = state.stack.length;
+      if (/^\}/.test(textAfter))
+        n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1;
+      return state.baseIndent + n * indentUnit;
+    },
+
+    electricChars: "}"
+  };
+});
+
+CodeMirror.defineMIME("text/css", "css");

+ 501 - 0
web/js/codemirror/mode/css/test.js

@@ -0,0 +1,501 @@
+// Initiate ModeTest and set defaults
+var MT = ModeTest;
+MT.modeName = 'css';
+MT.modeOptions = {};
+
+// Requires at least one media query
+MT.testMode(
+  'atMediaEmpty',
+  '@media { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'error', '{',
+    null, ' }'
+  ]
+);
+
+MT.testMode(
+  'atMediaMultiple',
+  '@media not screen and (color), not print and (color) { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'keyword', 'not',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' ',
+    'operator', 'and',
+    null, ' (',
+    'property', 'color',
+    null, '), ',
+    'keyword', 'not',
+    null, ' ',
+    'attribute', 'print',
+    null, ' ',
+    'operator', 'and',
+    null, ' (',
+    'property', 'color',
+    null, ') { }'
+  ]
+);
+
+MT.testMode(
+  'atMediaCheckStack',
+  '@media screen { } foo { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' { } ',
+    'tag', 'foo',
+    null, ' { }'
+  ]
+);
+
+MT.testMode(
+  'atMediaCheckStack',
+  '@media screen (color) { } foo { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' (',
+    'property', 'color',
+    null, ') { } ',
+    'tag', 'foo',
+    null, ' { }'
+  ]
+);
+
+MT.testMode(
+  'atMediaCheckStackInvalidAttribute',
+  '@media foobarhello { } foo { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute error', 'foobarhello',
+    null, ' { } ',
+    'tag', 'foo',
+    null, ' { }'
+  ]
+);
+
+// Error, because "and" is only allowed immediately preceding a media expression
+MT.testMode(
+  'atMediaInvalidAttribute',
+  '@media foobarhello { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute error', 'foobarhello',
+    null, ' { }'
+  ]
+);
+
+// Error, because "and" is only allowed immediately preceding a media expression
+MT.testMode(
+  'atMediaInvalidAnd',
+  '@media and screen { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'error', 'and',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' { }'
+  ]
+);
+
+// Error, because "not" is only allowed as the first item in each media query
+MT.testMode(
+  'atMediaInvalidNot',
+  '@media screen not (not) { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' ',
+    'error', 'not',
+    null, ' (',
+    'error', 'not',
+    null, ') { }'
+  ]
+);
+
+// Error, because "only" is only allowed as the first item in each media query
+MT.testMode(
+  'atMediaInvalidOnly',
+  '@media screen only (only) { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' ',
+    'error', 'only',
+    null, ' (',
+    'error', 'only',
+    null, ') { }'
+  ]
+);
+
+// Error, because "foobarhello" is neither a known type or property, but
+// property was expected (after "and"), and it should be in parenthese.
+MT.testMode(
+  'atMediaUnknownType',
+  '@media screen and foobarhello { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' ',
+    'operator', 'and',
+    null, ' ',
+    'error', 'foobarhello',
+    null, ' { }'
+  ]
+);
+
+// Error, because "color" is not a known type, but is a known property, and
+// should be in parentheses.
+MT.testMode(
+  'atMediaInvalidType',
+  '@media screen and color { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' ',
+    'operator', 'and',
+    null, ' ',
+    'error', 'color',
+    null, ' { }'
+  ]
+);
+
+// Error, because "print" is not a known property, but is a known type,
+// and should not be in parenthese.
+MT.testMode(
+  'atMediaInvalidProperty',
+  '@media screen and (print) { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' ',
+    'operator', 'and',
+    null, ' (',
+    'error', 'print',
+    null, ') { }'
+  ]
+);
+
+// Soft error, because "foobarhello" is not a known property or type.
+MT.testMode(
+  'atMediaUnknownProperty',
+  '@media screen and (foobarhello) { }',
+  [
+    'def', '@media',
+    null, ' ',
+    'attribute', 'screen',
+    null, ' ',
+    'operator', 'and',
+    null, ' (',
+    'property error', 'foobarhello',
+    null, ') { }'
+  ]
+);
+
+MT.testMode(
+  'tagSelector',
+  'foo { }',
+  [
+    'tag', 'foo',
+    null, ' { }'
+  ]
+);
+
+MT.testMode(
+  'classSelector',
+  '.foo-bar_hello { }',
+  [
+    'qualifier', '.foo-bar_hello',
+    null, ' { }'
+  ]
+);
+
+MT.testMode(
+  'idSelector',
+  '#foo { #foo }',
+  [
+    'builtin', '#foo',
+    null, ' { ',
+    'error', '#foo',
+    null, ' }'
+  ]
+);
+
+MT.testMode(
+  'tagSelectorUnclosed',
+  'foo { margin: 0 } bar { }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'margin',
+    'operator', ':',
+    null, ' ',
+    'number', '0',
+    null, ' } ',
+    'tag', 'bar',
+    null, ' { }'
+  ]
+);
+
+MT.testMode(
+  'tagStringNoQuotes',
+  'foo { font-family: hello world; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'font-family',
+    'operator', ':',
+    null, ' ',
+    'variable-2', 'hello',
+    null, ' ',
+    'variable-2', 'world',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagStringDouble',
+  'foo { font-family: "hello world"; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'font-family',
+    'operator', ':',
+    null, ' ',
+    'string', '"hello world"',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagStringSingle',
+  'foo { font-family: \'hello world\'; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'font-family',
+    'operator', ':',
+    null, ' ',
+    'string', '\'hello world\'',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagColorKeyword',
+  'foo { color: black; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'color',
+    'operator', ':',
+    null, ' ',
+    'keyword', 'black',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagColorHex3',
+  'foo { background: #fff; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'background',
+    'operator', ':',
+    null, ' ',
+    'atom', '#fff',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagColorHex6',
+  'foo { background: #ffffff; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'background',
+    'operator', ':',
+    null, ' ',
+    'atom', '#ffffff',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagColorHex4',
+  'foo { background: #ffff; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'background',
+    'operator', ':',
+    null, ' ',
+    'atom error', '#ffff',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagColorHexInvalid',
+  'foo { background: #ffg; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'background',
+    'operator', ':',
+    null, ' ',
+    'atom error', '#ffg',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagNegativeNumber',
+  'foo { margin: -5px; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'margin',
+    'operator', ':',
+    null, ' ',
+    'number', '-5px',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagPositiveNumber',
+  'foo { padding: 5px; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'padding',
+    'operator', ':',
+    null, ' ',
+    'number', '5px',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagVendor',
+  'foo { -foo-box-sizing: -foo-border-box; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'meta', '-foo-',
+    'property', 'box-sizing',
+    'operator', ':',
+    null, ' ',
+    'meta', '-foo-',
+    'string-2', 'border-box',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagBogusProperty',
+  'foo { barhelloworld: 0; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property error', 'barhelloworld',
+    'operator', ':',
+    null, ' ',
+    'number', '0',
+    null, '; }'
+  ]
+);
+
+MT.testMode(
+  'tagTwoProperties',
+  'foo { margin: 0; padding: 0; }',
+  [
+    'tag', 'foo',
+    null, ' { ',
+    'property', 'margin',
+    'operator', ':',
+    null, ' ',
+    'number', '0',
+    null, '; ',
+    'property', 'padding',
+    'operator', ':',
+    null, ' ',
+    'number', '0',
+    null, '; }'
+  ]
+);
+//
+//MT.testMode(
+//  'tagClass',
+//  '@media only screen and (min-width: 500px), print {foo.bar#hello { color: black !important; background: #f00; margin: -5px; padding: 5px; -foo-box-sizing: border-box; } /* world */}',
+//  [
+//    'def', '@media',
+//    null, ' ',
+//    'keyword', 'only',
+//    null, ' ',
+//    'attribute', 'screen',
+//    null, ' ',
+//    'operator', 'and',
+//    null, ' ',
+//    'bracket', '(',
+//    'property', 'min-width',
+//    'operator', ':',
+//    null, ' ',
+//    'number', '500px',
+//    'bracket', ')',
+//    null, ', ',
+//    'attribute', 'print',
+//    null, ' {',
+//    'tag', 'foo',
+//    'qualifier', '.bar',
+//    'header', '#hello',
+//    null, ' { ',
+//    'property', 'color',
+//    'operator', ':',
+//    null, ' ',
+//    'keyword', 'black',
+//    null, ' ',
+//    'keyword', '!important',
+//    null, '; ',
+//    'property', 'background',
+//    'operator', ':',
+//    null, ' ',
+//    'atom', '#f00',
+//    null, '; ',
+//    'property', 'padding',
+//    'operator', ':',
+//    null, ' ',
+//    'number', '5px',
+//    null, '; ',
+//    'property', 'margin',
+//    'operator', ':',
+//    null, ' ',
+//    'number', '-5px',
+//    null, '; ',
+//    'meta', '-foo-',
+//    'property', 'box-sizing',
+//    'operator', ':',
+//    null, ' ',
+//    'string-2', 'border-box',
+//    null, '; } ',
+//    'comment', '/* world */',
+//    null, '}'
+//  ]
+//);

+ 210 - 0
web/js/codemirror/mode/groovy/groovy.js

@@ -0,0 +1,210 @@
+CodeMirror.defineMode("groovy", function(config) {
+  function words(str) {
+    var obj = {}, words = str.split(" ");
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+    return obj;
+  }
+  var keywords = words(
+    "abstract as assert boolean break byte case catch char class const continue def default " +
+    "do double else enum extends final finally float for goto if implements import in " +
+    "instanceof int interface long native new package private protected public return " +
+    "short static strictfp super switch synchronized threadsafe throw throws transient " +
+    "try void volatile while");
+  var blockKeywords = words("catch class do else finally for if switch try while enum interface def");
+  var atoms = words("null true false this");
+
+  var curPunc;
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    if (ch == '"' || ch == "'") {
+      return startString(ch, stream, state);
+    }
+    if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
+      curPunc = ch;
+      return null;
+    }
+    if (/\d/.test(ch)) {
+      stream.eatWhile(/[\w\.]/);
+      if (stream.eat(/eE/)) { stream.eat(/\+\-/); stream.eatWhile(/\d/); }
+      return "number";
+    }
+    if (ch == "/") {
+      if (stream.eat("*")) {
+        state.tokenize.push(tokenComment);
+        return tokenComment(stream, state);
+      }
+      if (stream.eat("/")) {
+        stream.skipToEnd();
+        return "comment";
+      }
+      if (expectExpression(state.lastToken)) {
+        return startString(ch, stream, state);
+      }
+    }
+    if (ch == "-" && stream.eat(">")) {
+      curPunc = "->";
+      return null;
+    }
+    if (/[+\-*&%=<>!?|\/~]/.test(ch)) {
+      stream.eatWhile(/[+\-*&%=<>|~]/);
+      return "operator";
+    }
+    stream.eatWhile(/[\w\$_]/);
+    if (ch == "@") { stream.eatWhile(/[\w\$_\.]/); return "meta"; }
+    if (state.lastToken == ".") return "property";
+    if (stream.eat(":")) { curPunc = "proplabel"; return "property"; }
+    var cur = stream.current();
+    if (atoms.propertyIsEnumerable(cur)) { return "atom"; }
+    if (keywords.propertyIsEnumerable(cur)) {
+      if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
+      return "keyword";
+    }
+    return "variable";
+  }
+  tokenBase.isBase = true;
+
+  function startString(quote, stream, state) {
+    var tripleQuoted = false;
+    if (quote != "/" && stream.eat(quote)) {
+      if (stream.eat(quote)) tripleQuoted = true;
+      else return "string";
+    }
+    function t(stream, state) {
+      var escaped = false, next, end = !tripleQuoted;
+      while ((next = stream.next()) != null) {
+        if (next == quote && !escaped) {
+          if (!tripleQuoted) { break; }
+          if (stream.match(quote + quote)) { end = true; break; }
+        }
+        if (quote == '"' && next == "$" && !escaped && stream.eat("{")) {
+          state.tokenize.push(tokenBaseUntilBrace());
+          return "string";
+        }
+        escaped = !escaped && next == "\\";
+      }
+      if (end) state.tokenize.pop();
+      return "string";
+    }
+    state.tokenize.push(t);
+    return t(stream, state);
+  }
+
+  function tokenBaseUntilBrace() {
+    var depth = 1;
+    function t(stream, state) {
+      if (stream.peek() == "}") {
+        depth--;
+        if (depth == 0) {
+          state.tokenize.pop();
+          return state.tokenize[state.tokenize.length-1](stream, state);
+        }
+      } else if (stream.peek() == "{") {
+        depth++;
+      }
+      return tokenBase(stream, state);
+    }
+    t.isBase = true;
+    return t;
+  }
+
+  function tokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize.pop();
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return "comment";
+  }
+
+  function expectExpression(last) {
+    return !last || last == "operator" || last == "->" || /[\.\[\{\(,;:]/.test(last) ||
+      last == "newstatement" || last == "keyword" || last == "proplabel";
+  }
+
+  function Context(indented, column, type, align, prev) {
+    this.indented = indented;
+    this.column = column;
+    this.type = type;
+    this.align = align;
+    this.prev = prev;
+  }
+  function pushContext(state, col, type) {
+    return state.context = new Context(state.indented, col, type, null, state.context);
+  }
+  function popContext(state) {
+    var t = state.context.type;
+    if (t == ")" || t == "]" || t == "}")
+      state.indented = state.context.indented;
+    return state.context = state.context.prev;
+  }
+
+  // Interface
+
+  return {
+    startState: function(basecolumn) {
+      return {
+        tokenize: [tokenBase],
+        context: new Context((basecolumn || 0) - config.indentUnit, 0, "top", false),
+        indented: 0,
+        startOfLine: true,
+        lastToken: null
+      };
+    },
+
+    token: function(stream, state) {
+      var ctx = state.context;
+      if (stream.sol()) {
+        if (ctx.align == null) ctx.align = false;
+        state.indented = stream.indentation();
+        state.startOfLine = true;
+        // Automatic semicolon insertion
+        if (ctx.type == "statement" && !expectExpression(state.lastToken)) {
+          popContext(state); ctx = state.context;
+        }
+      }
+      if (stream.eatSpace()) return null;
+      curPunc = null;
+      var style = state.tokenize[state.tokenize.length-1](stream, state);
+      if (style == "comment") return style;
+      if (ctx.align == null) ctx.align = true;
+
+      if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state);
+      // Handle indentation for {x -> \n ... }
+      else if (curPunc == "->" && ctx.type == "statement" && ctx.prev.type == "}") {
+        popContext(state);
+        state.context.align = false;
+      }
+      else if (curPunc == "{") pushContext(state, stream.column(), "}");
+      else if (curPunc == "[") pushContext(state, stream.column(), "]");
+      else if (curPunc == "(") pushContext(state, stream.column(), ")");
+      else if (curPunc == "}") {
+        while (ctx.type == "statement") ctx = popContext(state);
+        if (ctx.type == "}") ctx = popContext(state);
+        while (ctx.type == "statement") ctx = popContext(state);
+      }
+      else if (curPunc == ctx.type) popContext(state);
+      else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement"))
+        pushContext(state, stream.column(), "statement");
+      state.startOfLine = false;
+      state.lastToken = curPunc || style;
+      return style;
+    },
+
+    indent: function(state, textAfter) {
+      if (!state.tokenize[state.tokenize.length-1].isBase) return 0;
+      var firstChar = textAfter && textAfter.charAt(0), ctx = state.context;
+      if (ctx.type == "statement" && !expectExpression(state.lastToken)) ctx = ctx.prev;
+      var closing = firstChar == ctx.type;
+      if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : config.indentUnit);
+      else if (ctx.align) return ctx.column + (closing ? 0 : 1);
+      else return ctx.indented + (closing ? 0 : config.indentUnit);
+    },
+
+    electricChars: "{}"
+  };
+});
+
+CodeMirror.defineMIME("text/x-groovy", "groovy");

+ 84 - 0
web/js/codemirror/mode/htmlmixed/htmlmixed.js

@@ -0,0 +1,84 @@
+CodeMirror.defineMode("htmlmixed", function(config) {
+  var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
+  var jsMode = CodeMirror.getMode(config, "javascript");
+  var cssMode = CodeMirror.getMode(config, "css");
+
+  function html(stream, state) {
+    var style = htmlMode.token(stream, state.htmlState);
+    if (/(?:^|\s)tag(?:\s|$)/.test(style) && stream.current() == ">" && state.htmlState.context) {
+      if (/^script$/i.test(state.htmlState.context.tagName)) {
+        state.token = javascript;
+        state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
+      }
+      else if (/^style$/i.test(state.htmlState.context.tagName)) {
+        state.token = css;
+        state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
+      }
+    }
+    return style;
+  }
+  function maybeBackup(stream, pat, style) {
+    var cur = stream.current();
+    var close = cur.search(pat), m;
+    if (close > -1) stream.backUp(cur.length - close);
+    else if (m = cur.match(/<\/?$/)) {
+      stream.backUp(cur.length);
+      if (!stream.match(pat, false)) stream.match(cur[0]);
+    }
+    return style;
+  }
+  function javascript(stream, state) {
+    if (stream.match(/^<\/\s*script\s*>/i, false)) {
+      state.token = html;
+      state.localState = null;
+      return html(stream, state);
+    }
+    return maybeBackup(stream, /<\/\s*script\s*>/,
+                       jsMode.token(stream, state.localState));
+  }
+  function css(stream, state) {
+    if (stream.match(/^<\/\s*style\s*>/i, false)) {
+      state.token = html;
+      state.localState = null;
+      return html(stream, state);
+    }
+    return maybeBackup(stream, /<\/\s*style\s*>/,
+                       cssMode.token(stream, state.localState));
+  }
+
+  return {
+    startState: function() {
+      var state = htmlMode.startState();
+      return {token: html, localState: null, mode: "html", htmlState: state};
+    },
+
+    copyState: function(state) {
+      if (state.localState)
+        var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
+      return {token: state.token, localState: local, mode: state.mode,
+              htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
+    },
+
+    token: function(stream, state) {
+      return state.token(stream, state);
+    },
+
+    indent: function(state, textAfter) {
+      if (state.token == html || /^\s*<\//.test(textAfter))
+        return htmlMode.indent(state.htmlState, textAfter);
+      else if (state.token == javascript)
+        return jsMode.indent(state.localState, textAfter);
+      else
+        return cssMode.indent(state.localState, textAfter);
+    },
+
+    electricChars: "/{}:",
+
+    innerMode: function(state) {
+      var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode;
+      return {state: state.localState || state.htmlState, mode: mode};
+    }
+  };
+}, "xml", "javascript", "css");
+
+CodeMirror.defineMIME("text/html", "htmlmixed");

+ 411 - 0
web/js/codemirror/mode/javascript/javascript.js

@@ -0,0 +1,411 @@
+// TODO actually recognize syntax of TypeScript constructs
+
+CodeMirror.defineMode("javascript", function(config, parserConfig) {
+  var indentUnit = config.indentUnit;
+  var jsonMode = parserConfig.json;
+  var isTS = parserConfig.typescript;
+
+  // Tokenizer
+
+  var keywords = function(){
+    function kw(type) {return {type: type, style: "keyword"};}
+    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
+    var operator = kw("operator"), atom = {type: "atom", style: "atom"};
+    
+    var jsKeywords = {
+      "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+      "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
+      "var": kw("var"), "const": kw("var"), "let": kw("var"),
+      "function": kw("function"), "catch": kw("catch"),
+      "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+      "in": operator, "typeof": operator, "instanceof": operator,
+      "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
+    };
+
+    // Extend the 'normal' keywords with the TypeScript language extensions
+    if (isTS) {
+      var type = {type: "variable", style: "variable-3"};
+      var tsKeywords = {
+        // object-like things
+        "interface": kw("interface"),
+        "class": kw("class"),
+        "extends": kw("extends"),
+        "constructor": kw("constructor"),
+
+        // scope modifiers
+        "public": kw("public"),
+        "private": kw("private"),
+        "protected": kw("protected"),
+        "static": kw("static"),
+
+        "super": kw("super"),
+
+        // types
+        "string": type, "number": type, "bool": type, "any": type
+      };
+
+      for (var attr in tsKeywords) {
+        jsKeywords[attr] = tsKeywords[attr];
+      }
+    }
+
+    return jsKeywords;
+  }();
+
+  var isOperatorChar = /[+\-*&%=<>!?|]/;
+
+  function chain(stream, state, f) {
+    state.tokenize = f;
+    return f(stream, state);
+  }
+
+  function nextUntilUnescaped(stream, end) {
+    var escaped = false, next;
+    while ((next = stream.next()) != null) {
+      if (next == end && !escaped)
+        return false;
+      escaped = !escaped && next == "\\";
+    }
+    return escaped;
+  }
+
+  // Used as scratch variables to communicate multiple values without
+  // consing up tons of objects.
+  var type, content;
+  function ret(tp, style, cont) {
+    type = tp; content = cont;
+    return style;
+  }
+
+  function jsTokenBase(stream, state) {
+    var ch = stream.next();
+    if (ch == '"' || ch == "'")
+      return chain(stream, state, jsTokenString(ch));
+    else if (/[\[\]{}\(\),;\:\.]/.test(ch))
+      return ret(ch);
+    else if (ch == "0" && stream.eat(/x/i)) {
+      stream.eatWhile(/[\da-f]/i);
+      return ret("number", "number");
+    }      
+    else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
+      stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
+      return ret("number", "number");
+    }
+    else if (ch == "/") {
+      if (stream.eat("*")) {
+        return chain(stream, state, jsTokenComment);
+      }
+      else if (stream.eat("/")) {
+        stream.skipToEnd();
+        return ret("comment", "comment");
+      }
+      else if (state.lastType == "operator" || state.lastType == "keyword c" ||
+               /^[\[{}\(,;:]$/.test(state.lastType)) {
+        nextUntilUnescaped(stream, "/");
+        stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
+        return ret("regexp", "string-2");
+      }
+      else {
+        stream.eatWhile(isOperatorChar);
+        return ret("operator", null, stream.current());
+      }
+    }
+    else if (ch == "#") {
+        stream.skipToEnd();
+        return ret("error", "error");
+    }
+    else if (isOperatorChar.test(ch)) {
+      stream.eatWhile(isOperatorChar);
+      return ret("operator", null, stream.current());
+    }
+    else {
+      stream.eatWhile(/[\w\$_]/);
+      var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
+      return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
+                     ret("variable", "variable", word);
+    }
+  }
+
+  function jsTokenString(quote) {
+    return function(stream, state) {
+      if (!nextUntilUnescaped(stream, quote))
+        state.tokenize = jsTokenBase;
+      return ret("string", "string");
+    };
+  }
+
+  function jsTokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize = jsTokenBase;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ret("comment", "comment");
+  }
+
+  // Parser
+
+  var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
+
+  function JSLexical(indented, column, type, align, prev, info) {
+    this.indented = indented;
+    this.column = column;
+    this.type = type;
+    this.prev = prev;
+    this.info = info;
+    if (align != null) this.align = align;
+  }
+
+  function inScope(state, varname) {
+    for (var v = state.localVars; v; v = v.next)
+      if (v.name == varname) return true;
+  }
+
+  function parseJS(state, style, type, content, stream) {
+    var cc = state.cc;
+    // Communicate our context to the combinators.
+    // (Less wasteful than consing up a hundred closures on every call.)
+    cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
+  
+    if (!state.lexical.hasOwnProperty("align"))
+      state.lexical.align = true;
+
+    while(true) {
+      var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
+      if (combinator(type, content)) {
+        while(cc.length && cc[cc.length - 1].lex)
+          cc.pop()();
+        if (cx.marked) return cx.marked;
+        if (type == "variable" && inScope(state, content)) return "variable-2";
+        return style;
+      }
+    }
+  }
+
+  // Combinator utils
+
+  var cx = {state: null, column: null, marked: null, cc: null};
+  function pass() {
+    for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
+  }
+  function cont() {
+    pass.apply(null, arguments);
+    return true;
+  }
+  function register(varname) {
+    var state = cx.state;
+    if (state.context) {
+      cx.marked = "def";
+      for (var v = state.localVars; v; v = v.next)
+        if (v.name == varname) return;
+      state.localVars = {name: varname, next: state.localVars};
+    }
+  }
+
+  // Combinators
+
+  var defaultVars = {name: "this", next: {name: "arguments"}};
+  function pushcontext() {
+    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
+    cx.state.localVars = defaultVars;
+  }
+  function popcontext() {
+    cx.state.localVars = cx.state.context.vars;
+    cx.state.context = cx.state.context.prev;
+  }
+  function pushlex(type, info) {
+    var result = function() {
+      var state = cx.state;
+      state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
+    };
+    result.lex = true;
+    return result;
+  }
+  function poplex() {
+    var state = cx.state;
+    if (state.lexical.prev) {
+      if (state.lexical.type == ")")
+        state.indented = state.lexical.indented;
+      state.lexical = state.lexical.prev;
+    }
+  }
+  poplex.lex = true;
+
+  function expect(wanted) {
+    return function expecting(type) {
+      if (type == wanted) return cont();
+      else if (wanted == ";") return pass();
+      else return cont(arguments.callee);
+    };
+  }
+
+  function statement(type) {
+    if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
+    if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
+    if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
+    if (type == "{") return cont(pushlex("}"), block, poplex);
+    if (type == ";") return cont();
+    if (type == "function") return cont(functiondef);
+    if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
+                                      poplex, statement, poplex);
+    if (type == "variable") return cont(pushlex("stat"), maybelabel);
+    if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
+                                         block, poplex, poplex);
+    if (type == "case") return cont(expression, expect(":"));
+    if (type == "default") return cont(expect(":"));
+    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+                                        statement, poplex, popcontext);
+    return pass(pushlex("stat"), expression, expect(";"), poplex);
+  }
+  function expression(type) {
+    if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
+    if (type == "function") return cont(functiondef);
+    if (type == "keyword c") return cont(maybeexpression);
+    if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
+    if (type == "operator") return cont(expression);
+    if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
+    if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
+    return cont();
+  }
+  function maybeexpression(type) {
+    if (type.match(/[;\}\)\],]/)) return pass();
+    return pass(expression);
+  }
+    
+  function maybeoperator(type, value) {
+    if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
+    if (type == "operator" && value == "?") return cont(expression, expect(":"), expression);
+    if (type == ";") return;
+    if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
+    if (type == ".") return cont(property, maybeoperator);
+    if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
+  }
+  function maybelabel(type) {
+    if (type == ":") return cont(poplex, statement);
+    return pass(maybeoperator, expect(";"), poplex);
+  }
+  function property(type) {
+    if (type == "variable") {cx.marked = "property"; return cont();}
+  }
+  function objprop(type) {
+    if (type == "variable") cx.marked = "property";
+    if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
+  }
+  function commasep(what, end) {
+    function proceed(type) {
+      if (type == ",") return cont(what, proceed);
+      if (type == end) return cont();
+      return cont(expect(end));
+    }
+    return function commaSeparated(type) {
+      if (type == end) return cont();
+      else return pass(what, proceed);
+    };
+  }
+  function block(type) {
+    if (type == "}") return cont();
+    return pass(statement, block);
+  }
+  function maybetype(type) {
+    if (type == ":") return cont(typedef);
+    return pass();
+  }
+  function typedef(type) {
+    if (type == "variable"){cx.marked = "variable-3"; return cont();}
+    return pass();
+  }
+  function vardef1(type, value) {
+    if (type == "variable") {
+      register(value);
+      return isTS ? cont(maybetype, vardef2) : cont(vardef2);
+    }
+    return pass();
+  }
+  function vardef2(type, value) {
+    if (value == "=") return cont(expression, vardef2);
+    if (type == ",") return cont(vardef1);
+  }
+  function forspec1(type) {
+    if (type == "var") return cont(vardef1, expect(";"), forspec2);
+    if (type == ";") return cont(forspec2);
+    if (type == "variable") return cont(formaybein);
+    return cont(forspec2);
+  }
+  function formaybein(_type, value) {
+    if (value == "in") return cont(expression);
+    return cont(maybeoperator, forspec2);
+  }
+  function forspec2(type, value) {
+    if (type == ";") return cont(forspec3);
+    if (value == "in") return cont(expression);
+    return cont(expression, expect(";"), forspec3);
+  }
+  function forspec3(type) {
+    if (type != ")") cont(expression);
+  }
+  function functiondef(type, value) {
+    if (type == "variable") {register(value); return cont(functiondef);}
+    if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
+  }
+  function funarg(type, value) {
+    if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
+  }
+
+  // Interface
+
+  return {
+    startState: function(basecolumn) {
+      return {
+        tokenize: jsTokenBase,
+        lastType: null,
+        cc: [],
+        lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
+        localVars: parserConfig.localVars,
+        context: parserConfig.localVars && {vars: parserConfig.localVars},
+        indented: 0
+      };
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        if (!state.lexical.hasOwnProperty("align"))
+          state.lexical.align = false;
+        state.indented = stream.indentation();
+      }
+      if (stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+      if (type == "comment") return style;
+      state.lastType = type;
+      return parseJS(state, style, type, content, stream);
+    },
+
+    indent: function(state, textAfter) {
+      if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
+      if (state.tokenize != jsTokenBase) return 0;
+      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
+      if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
+      var type = lexical.type, closing = firstChar == type;
+      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
+      else if (type == "form" && firstChar == "{") return lexical.indented;
+      else if (type == "form") return lexical.indented + indentUnit;
+      else if (type == "stat")
+        return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
+      else if (lexical.info == "switch" && !closing)
+        return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
+      else if (lexical.align) return lexical.column + (closing ? 0 : 1);
+      else return lexical.indented + (closing ? 0 : indentUnit);
+    },
+
+    electricChars: ":{}",
+
+    jsonMode: jsonMode
+  };
+});
+
+CodeMirror.defineMIME("text/javascript", "javascript");
+CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
+CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
+CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });

+ 87 - 0
web/js/codemirror/mode/meta.js

@@ -0,0 +1,87 @@
+CodeMirror.modeInfo = [
+  {name: 'APL', mime: 'text/apl', mode: 'apl'},
+  {name: 'Asterisk', mime: 'text/x-asterisk', mode: 'asterisk'},
+  {name: 'C', mime: 'text/x-csrc', mode: 'clike'},
+  {name: 'C++', mime: 'text/x-c++src', mode: 'clike'},
+  {name: 'Cobol', mime: 'text/x-cobol', mode: 'cobol'},
+  {name: 'Java', mime: 'text/x-java', mode: 'clike'},
+  {name: 'C#', mime: 'text/x-csharp', mode: 'clike'},
+  {name: 'Scala', mime: 'text/x-scala', mode: 'clike'},
+  {name: 'Clojure', mime: 'text/x-clojure', mode: 'clojure'},
+  {name: 'CoffeeScript', mime: 'text/x-coffeescript', mode: 'coffeescript'},
+  {name: 'Common Lisp', mime: 'text/x-common-lisp', mode: 'commonlisp'},
+  {name: 'CSS', mime: 'text/css', mode: 'css'},
+  {name: 'D', mime: 'text/x-d', mode: 'd'},
+  {name: 'diff', mime: 'text/x-diff', mode: 'diff'},
+  {name: 'DTD', mime: 'application/xml-dtd', mode: 'dtd'},
+  {name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'},
+  {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'},
+  {name: 'Fortran', mime: 'text/x-fortran', mode: 'fortran'},
+  {name: 'Gas', mime: 'text/x-gas', mode: 'gas'},
+  {name: 'GitHub Flavored Markdown', mime: 'text/x-gfm', mode: 'gfm'},
+  {name: 'GO', mime: 'text/x-go', mode: 'go'},
+  {name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'},
+  {name: 'HAML', mime: 'text/x-haml', mode: 'haml'},
+  {name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'},
+  {name: 'Haxe', mime: 'text/x-haxe', mode: 'haxe'},
+  {name: 'ASP.NET', mime: 'application/x-aspx', mode: 'htmlembedded'},
+  {name: 'Embedded Javascript', mime: 'application/x-ejs', mode: 'htmlembedded'},
+  {name: 'JavaServer Pages', mime: 'application/x-jsp', mode: 'htmlembedded'},
+  {name: 'HTML', mime: 'text/html', mode: 'htmlmixed'},
+  {name: 'HTTP', mime: 'message/http', mode: 'http'},
+  {name: 'Jade', mime: 'text/x-jade', mode: 'jade'},
+  {name: 'JavaScript', mime: 'text/javascript', mode: 'javascript'},
+  {name: 'JSON', mime: 'application/x-json', mode: 'javascript'},
+  {name: 'JSON', mime: 'application/json', mode: 'javascript'},
+  {name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'},
+  {name: 'Jinja2', mime: 'jinja2', mode: 'jinja2'},
+  {name: 'LESS', mime: 'text/x-less', mode: 'less'},
+  {name: 'LiveScript', mime: 'text/x-livescript', mode: 'livescript'},
+  {name: 'Lua', mime: 'text/x-lua', mode: 'lua'},
+  {name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'},
+  {name: 'mIRC', mime: 'text/mirc', mode: 'mirc'},
+  {name: 'Nginx', mime: 'text/x-nginx-conf', mode: 'nginx'},
+  {name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'},
+  {name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'},
+  {name: 'Octave', mime: 'text/x-octave', mode: 'octave'},
+  {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'},
+  {name: 'Perl', mime: 'text/x-perl', mode: 'perl'},
+  {name: 'PHP', mime: 'text/x-php', mode: 'php'},
+  {name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'},
+  {name: 'Pig', mime: 'text/x-pig', mode: 'pig'},
+  {name: 'Plain Text', mime: 'text/plain', mode: 'null'},
+  {name: 'Properties files', mime: 'text/x-properties', mode: 'properties'},
+  {name: 'Python', mime: 'text/x-python', mode: 'python'},
+  {name: 'Cython', mime: 'text/x-cython', mode: 'python'},
+  {name: 'R', mime: 'text/x-rsrc', mode: 'r'},
+  {name: 'reStructuredText', mime: 'text/x-rst', mode: 'rst'},
+  {name: 'Ruby', mime: 'text/x-ruby', mode: 'ruby'},
+  {name: 'Rust', mime: 'text/x-rustsrc', mode: 'rust'},
+  {name: 'Sass', mime: 'text/x-sass', mode: 'sass'},
+  {name: 'Scheme', mime: 'text/x-scheme', mode: 'scheme'},
+  {name: 'SCSS', mime: 'text/x-scss', mode: 'css'},
+  {name: 'Shell', mime: 'text/x-sh', mode: 'shell'},
+  {name: 'Sieve', mime: 'application/sieve', mode: 'sieve'},
+  {name: 'Smalltalk', mime: 'text/x-stsrc', mode: 'smalltalk'},
+  {name: 'Smarty', mime: 'text/x-smarty', mode: 'smarty'},
+  {name: 'SmartyMixed', mime: 'text/x-smarty', mode: 'smartymixed'},
+  {name: 'SPARQL', mime: 'application/x-sparql-query', mode: 'sparql'},
+  {name: 'SQL', mime: 'text/x-sql', mode: 'sql'},
+  {name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'},
+  {name: 'sTeX', mime: 'text/x-stex', mode: 'stex'},
+  {name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'},
+  {name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'},
+  {name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'},
+  {name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'},
+  {name: 'TOML', mime: 'text/x-toml', mode: 'toml'},
+  {name: 'Turtle', mime: 'text/turtle', mode: 'turtle'},
+  {name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'},
+  {name: 'VBScript', mime: 'text/vbscript', mode: 'vbscript'},
+  {name: 'Velocity', mime: 'text/velocity', mode: 'velocity'},
+  {name: 'Verilog', mime: 'text/x-verilog', mode: 'verilog'},
+  {name: 'XML', mime: 'application/xml', mode: 'xml'},
+  {name: 'HTML', mime: 'text/html', mode: 'xml'},
+  {name: 'XQuery', mime: 'application/xquery', mode: 'xquery'},
+  {name: 'YAML', mime: 'text/x-yaml', mode: 'yaml'},
+  {name: 'Z80', mime: 'text/x-z80', mode: 'z80'}
+];

+ 203 - 0
web/js/codemirror/mode/mysql/mysql.js

@@ -0,0 +1,203 @@
+/*
+ *	MySQL Mode for CodeMirror 2 by MySQL-Tools
+ *	@author James Thorne (partydroid)
+ *	@link 	http://github.com/partydroid/MySQL-Tools
+ * 	@link 	http://mysqltools.org
+ *	@version 02/Jan/2012
+*/
+CodeMirror.defineMode("mysql", function(config) {
+  var indentUnit = config.indentUnit;
+  var curPunc;
+
+  function wordRegexp(words) {
+    return new RegExp("^(?:" + words.join("|") + ")$", "i");
+  }
+  var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri",
+                        "isblank", "isliteral", "union", "a"]);
+  var keywords = wordRegexp([
+  ('ACCESSIBLE'),('ALTER'),('AS'),('BEFORE'),('BINARY'),('BY'),('CASE'),('CHARACTER'),('COLUMN'),('CONTINUE'),('CROSS'),('CURRENT_TIMESTAMP'),('DATABASE'),('DAY_MICROSECOND'),('DEC'),('DEFAULT'),
+	('DESC'),('DISTINCT'),('DOUBLE'),('EACH'),('ENCLOSED'),('EXIT'),('FETCH'),('FLOAT8'),('FOREIGN'),('GRANT'),('HIGH_PRIORITY'),('HOUR_SECOND'),('IN'),('INNER'),('INSERT'),('INT2'),('INT8'),
+	('INTO'),('JOIN'),('KILL'),('LEFT'),('LINEAR'),('LOCALTIME'),('LONG'),('LOOP'),('MATCH'),('MEDIUMTEXT'),('MINUTE_SECOND'),('NATURAL'),('NULL'),('OPTIMIZE'),('OR'),('OUTER'),('PRIMARY'),
+	('RANGE'),('READ_WRITE'),('REGEXP'),('REPEAT'),('RESTRICT'),('RIGHT'),('SCHEMAS'),('SENSITIVE'),('SHOW'),('SPECIFIC'),('SQLSTATE'),('SQL_CALC_FOUND_ROWS'),('STARTING'),('TERMINATED'),
+	('TINYINT'),('TRAILING'),('UNDO'),('UNLOCK'),('USAGE'),('UTC_DATE'),('VALUES'),('VARCHARACTER'),('WHERE'),('WRITE'),('ZEROFILL'),('ALL'),('AND'),('ASENSITIVE'),('BIGINT'),('BOTH'),('CASCADE'),
+	('CHAR'),('COLLATE'),('CONSTRAINT'),('CREATE'),('CURRENT_TIME'),('CURSOR'),('DAY_HOUR'),('DAY_SECOND'),('DECLARE'),('DELETE'),('DETERMINISTIC'),('DIV'),('DUAL'),('ELSEIF'),('EXISTS'),('FALSE'),
+	('FLOAT4'),('FORCE'),('FULLTEXT'),('HAVING'),('HOUR_MINUTE'),('IGNORE'),('INFILE'),('INSENSITIVE'),('INT1'),('INT4'),('INTERVAL'),('ITERATE'),('KEYS'),('LEAVE'),('LIMIT'),('LOAD'),('LOCK'),
+	('LONGTEXT'),('MASTER_SSL_VERIFY_SERVER_CERT'),('MEDIUMINT'),('MINUTE_MICROSECOND'),('MODIFIES'),('NO_WRITE_TO_BINLOG'),('ON'),('OPTIONALLY'),('OUT'),('PRECISION'),('PURGE'),('READS'),
+	('REFERENCES'),('RENAME'),('REQUIRE'),('REVOKE'),('SCHEMA'),('SELECT'),('SET'),('SPATIAL'),('SQLEXCEPTION'),('SQL_BIG_RESULT'),('SSL'),('TABLE'),('TINYBLOB'),('TO'),('TRUE'),('UNIQUE'),
+	('UPDATE'),('USING'),('UTC_TIMESTAMP'),('VARCHAR'),('WHEN'),('WITH'),('YEAR_MONTH'),('ADD'),('ANALYZE'),('ASC'),('BETWEEN'),('BLOB'),('CALL'),('CHANGE'),('CHECK'),('CONDITION'),('CONVERT'),
+	('CURRENT_DATE'),('CURRENT_USER'),('DATABASES'),('DAY_MINUTE'),('DECIMAL'),('DELAYED'),('DESCRIBE'),('DISTINCTROW'),('DROP'),('ELSE'),('ESCAPED'),('EXPLAIN'),('FLOAT'),('FOR'),('FROM'),
+	('GROUP'),('HOUR_MICROSECOND'),('IF'),('INDEX'),('INOUT'),('INT'),('INT3'),('INTEGER'),('IS'),('KEY'),('LEADING'),('LIKE'),('LINES'),('LOCALTIMESTAMP'),('LONGBLOB'),('LOW_PRIORITY'),
+	('MEDIUMBLOB'),('MIDDLEINT'),('MOD'),('NOT'),('NUMERIC'),('OPTION'),('ORDER'),('OUTFILE'),('PROCEDURE'),('READ'),('REAL'),('RELEASE'),('REPLACE'),('RETURN'),('RLIKE'),('SECOND_MICROSECOND'),
+	('SEPARATOR'),('SMALLINT'),('SQL'),('SQLWARNING'),('SQL_SMALL_RESULT'),('STRAIGHT_JOIN'),('THEN'),('TINYTEXT'),('TRIGGER'),('UNION'),('UNSIGNED'),('USE'),('UTC_TIME'),('VARBINARY'),('VARYING'),
+	('WHILE'),('XOR'),('FULL'),('COLUMNS'),('MIN'),('MAX'),('STDEV'),('COUNT')
+  ]);
+  var operatorChars = /[*+\-<>=&|]/;
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    curPunc = null;
+    if (ch == "$" || ch == "?") {
+      stream.match(/^[\w\d]*/);
+      return "variable-2";
+    }
+    else if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) {
+      stream.match(/^[^\s\u00a0>]*>?/);
+      return "atom";
+    }
+    else if (ch == "\"" || ch == "'") {
+      state.tokenize = tokenLiteral(ch);
+      return state.tokenize(stream, state);
+    }
+    else if (ch == "`") {
+      state.tokenize = tokenOpLiteral(ch);
+      return state.tokenize(stream, state);
+    }
+    else if (/[{}\(\),\.;\[\]]/.test(ch)) {
+      curPunc = ch;
+      return null;
+    }
+    else if (ch == "-" && stream.eat("-")) {
+      stream.skipToEnd();
+      return "comment";
+    }
+    else if (ch == "/" && stream.eat("*")) {
+      state.tokenize = tokenComment;
+      return state.tokenize(stream, state);
+    }
+    else if (operatorChars.test(ch)) {
+      stream.eatWhile(operatorChars);
+      return null;
+    }
+    else if (ch == ":") {
+      stream.eatWhile(/[\w\d\._\-]/);
+      return "atom";
+    }
+    else {
+      stream.eatWhile(/[_\w\d]/);
+      if (stream.eat(":")) {
+        stream.eatWhile(/[\w\d_\-]/);
+        return "atom";
+      }
+      var word = stream.current();
+      if (ops.test(word))
+        return null;
+      else if (keywords.test(word))
+        return "keyword";
+      else
+        return "variable";
+    }
+  }
+
+  function tokenLiteral(quote) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped) {
+          state.tokenize = tokenBase;
+          break;
+        }
+        escaped = !escaped && ch == "\\";
+      }
+      return "string";
+    };
+  }
+
+  function tokenOpLiteral(quote) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped) {
+          state.tokenize = tokenBase;
+          break;
+        }
+        escaped = !escaped && ch == "\\";
+      }
+      return "variable-2";
+    };
+  }
+
+  function tokenComment(stream, state) {
+    for (;;) {
+      if (stream.skipTo("*")) {
+        stream.next();
+        if (stream.eat("/")) {
+          state.tokenize = tokenBase;
+          break;
+        }
+      } else {
+        stream.skipToEnd();
+        break;
+      }
+    }
+    return "comment";
+  }
+
+
+  function pushContext(state, type, col) {
+    state.context = {prev: state.context, indent: state.indent, col: col, type: type};
+  }
+  function popContext(state) {
+    state.indent = state.context.indent;
+    state.context = state.context.prev;
+  }
+
+  return {
+    startState: function() {
+      return {tokenize: tokenBase,
+              context: null,
+              indent: 0,
+              col: 0};
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        if (state.context && state.context.align == null) state.context.align = false;
+        state.indent = stream.indentation();
+      }
+      if (stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+
+      if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") {
+        state.context.align = true;
+      }
+
+      if (curPunc == "(") pushContext(state, ")", stream.column());
+      else if (curPunc == "[") pushContext(state, "]", stream.column());
+      else if (curPunc == "{") pushContext(state, "}", stream.column());
+      else if (/[\]\}\)]/.test(curPunc)) {
+        while (state.context && state.context.type == "pattern") popContext(state);
+        if (state.context && curPunc == state.context.type) popContext(state);
+      }
+      else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state);
+      else if (/atom|string|variable/.test(style) && state.context) {
+        if (/[\}\]]/.test(state.context.type))
+          pushContext(state, "pattern", stream.column());
+        else if (state.context.type == "pattern" && !state.context.align) {
+          state.context.align = true;
+          state.context.col = stream.column();
+        }
+      }
+
+      return style;
+    },
+
+    indent: function(state, textAfter) {
+      var firstChar = textAfter && textAfter.charAt(0);
+      var context = state.context;
+      if (/[\]\}]/.test(firstChar))
+        while (context && context.type == "pattern") context = context.prev;
+
+      var closing = context && firstChar == context.type;
+      if (!context)
+        return 0;
+      else if (context.type == "pattern")
+        return context.col;
+      else if (context.align)
+        return context.col + (closing ? 0 : 1);
+      else
+        return context.indent + (closing ? 0 : indentUnit);
+    }
+  };
+});
+
+CodeMirror.defineMIME("text/x-mysql", "mysql");

+ 216 - 0
web/js/codemirror/mode/plsql/plsql.js

@@ -0,0 +1,216 @@
+CodeMirror.defineMode("plsql", function(_config, parserConfig) {
+  var keywords         = parserConfig.keywords,
+      functions        = parserConfig.functions,
+      types            = parserConfig.types,
+      sqlplus          = parserConfig.sqlplus,
+      multiLineStrings = parserConfig.multiLineStrings;
+  var isOperatorChar   = /[+\-*&%=<>!?:\/|]/;
+  function chain(stream, state, f) {
+    state.tokenize = f;
+    return f(stream, state);
+  }
+
+  var type;
+  function ret(tp, style) {
+    type = tp;
+    return style;
+  }
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    // start of string?
+    if (ch == '"' || ch == "'")
+      return chain(stream, state, tokenString(ch));
+    // is it one of the special signs []{}().,;? Seperator?
+    else if (/[\[\]{}\(\),;\.]/.test(ch))
+      return ret(ch);
+    // start of a number value?
+    else if (/\d/.test(ch)) {
+      stream.eatWhile(/[\w\.]/);
+      return ret("number", "number");
+    }
+    // multi line comment or simple operator?
+    else if (ch == "/") {
+      if (stream.eat("*")) {
+        return chain(stream, state, tokenComment);
+      }
+      else {
+        stream.eatWhile(isOperatorChar);
+        return ret("operator", "operator");
+      }
+    }
+    // single line comment or simple operator?
+    else if (ch == "-") {
+      if (stream.eat("-")) {
+        stream.skipToEnd();
+        return ret("comment", "comment");
+      }
+      else {
+        stream.eatWhile(isOperatorChar);
+        return ret("operator", "operator");
+      }
+    }
+    // pl/sql variable?
+    else if (ch == "@" || ch == "$") {
+      stream.eatWhile(/[\w\d\$_]/);
+      return ret("word", "variable");
+    }
+    // is it a operator?
+    else if (isOperatorChar.test(ch)) {
+      stream.eatWhile(isOperatorChar);
+      return ret("operator", "operator");
+    }
+    else {
+      // get the whole word
+      stream.eatWhile(/[\w\$_]/);
+      // is it one of the listed keywords?
+      if (keywords && keywords.propertyIsEnumerable(stream.current().toLowerCase())) return ret("keyword", "keyword");
+      // is it one of the listed functions?
+      if (functions && functions.propertyIsEnumerable(stream.current().toLowerCase())) return ret("keyword", "builtin");
+      // is it one of the listed types?
+      if (types && types.propertyIsEnumerable(stream.current().toLowerCase())) return ret("keyword", "variable-2");
+      // is it one of the listed sqlplus keywords?
+      if (sqlplus && sqlplus.propertyIsEnumerable(stream.current().toLowerCase())) return ret("keyword", "variable-3");
+      // default: just a "variable"
+      return ret("word", "variable");
+    }
+  }
+
+  function tokenString(quote) {
+    return function(stream, state) {
+      var escaped = false, next, end = false;
+      while ((next = stream.next()) != null) {
+        if (next == quote && !escaped) {end = true; break;}
+        escaped = !escaped && next == "\\";
+      }
+      if (end || !(escaped || multiLineStrings))
+        state.tokenize = tokenBase;
+      return ret("string", "plsql-string");
+    };
+  }
+
+  function tokenComment(stream, state) {
+    var maybeEnd = false, ch;
+    while (ch = stream.next()) {
+      if (ch == "/" && maybeEnd) {
+        state.tokenize = tokenBase;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ret("comment", "plsql-comment");
+  }
+
+  // Interface
+
+  return {
+    startState: function() {
+      return {
+        tokenize: tokenBase,
+        startOfLine: true
+      };
+    },
+
+    token: function(stream, state) {
+      if (stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+      return style;
+    }
+  };
+});
+
+(function() {
+  function keywords(str) {
+    var obj = {}, words = str.split(" ");
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+    return obj;
+  }
+  var cKeywords = "abort accept access add all alter and any array arraylen as asc assert assign at attributes audit " +
+        "authorization avg " +
+        "base_table begin between binary_integer body boolean by " +
+        "case cast char char_base check close cluster clusters colauth column comment commit compress connect " +
+        "connected constant constraint crash create current currval cursor " +
+        "data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete " +
+        "desc digits dispose distinct do drop " +
+        "else elsif enable end entry escape exception exception_init exchange exclusive exists exit external " +
+        "fast fetch file for force form from function " +
+        "generic goto grant group " +
+        "having " +
+        "identified if immediate in increment index indexes indicator initial initrans insert interface intersect " +
+        "into is " +
+        "key " +
+        "level library like limited local lock log logging long loop " +
+        "master maxextents maxtrans member minextents minus mislabel mode modify multiset " +
+        "new next no noaudit nocompress nologging noparallel not nowait number_base " +
+        "object of off offline on online only open option or order out " +
+        "package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior " +
+        "private privileges procedure public " +
+        "raise range raw read rebuild record ref references refresh release rename replace resource restrict return " +
+        "returning reverse revoke rollback row rowid rowlabel rownum rows run " +
+        "savepoint schema segment select separate session set share snapshot some space split sql start statement " +
+        "storage subtype successful synonym " +
+        "tabauth table tables tablespace task terminate then to trigger truncate type " +
+        "union unique unlimited unrecoverable unusable update use using " +
+        "validate value values variable view views " +
+        "when whenever where while with work";
+
+  var cFunctions = "abs acos add_months ascii asin atan atan2 average " +
+        "bfilename " +
+        "ceil chartorowid chr concat convert cos cosh count " +
+        "decode deref dual dump dup_val_on_index " +
+        "empty error exp " +
+        "false floor found " +
+        "glb greatest " +
+        "hextoraw " +
+        "initcap instr instrb isopen " +
+        "last_day least lenght lenghtb ln lower lpad ltrim lub " +
+        "make_ref max min mod months_between " +
+        "new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower " +
+        "nls_sort nls_upper nlssort no_data_found notfound null nvl " +
+        "others " +
+        "power " +
+        "rawtohex reftohex round rowcount rowidtochar rpad rtrim " +
+        "sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate " +
+        "tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc " +
+        "uid upper user userenv " +
+        "variance vsize";
+
+  var cTypes = "bfile blob " +
+        "character clob " +
+        "dec " +
+        "float " +
+        "int integer " +
+        "mlslabel " +
+        "natural naturaln nchar nclob number numeric nvarchar2 " +
+        "real rowtype " +
+        "signtype smallint string " +
+        "varchar varchar2";
+
+  var cSqlplus = "appinfo arraysize autocommit autoprint autorecovery autotrace " +
+        "blockterminator break btitle " +
+        "cmdsep colsep compatibility compute concat copycommit copytypecheck " +
+        "define describe " +
+        "echo editfile embedded escape exec execute " +
+        "feedback flagger flush " +
+        "heading headsep " +
+        "instance " +
+        "linesize lno loboffset logsource long longchunksize " +
+        "markup " +
+        "native newpage numformat numwidth " +
+        "pagesize pause pno " +
+        "recsep recsepchar release repfooter repheader " +
+        "serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber " +
+        "sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix " +
+        "tab term termout time timing trimout trimspool ttitle " +
+        "underline " +
+        "verify version " +
+        "wrap";
+
+  CodeMirror.defineMIME("text/x-plsql", {
+    name: "plsql",
+    keywords: keywords(cKeywords),
+    functions: keywords(cFunctions),
+    types: keywords(cTypes),
+    sqlplus: keywords(cSqlplus)
+  });
+}());

Разница между файлами не показана из-за своего большого размера
+ 352 - 0
web/js/codemirror/mode/sql/sql.js


+ 324 - 0
web/js/codemirror/mode/xml/xml.js

@@ -0,0 +1,324 @@
+CodeMirror.defineMode("xml", function(config, parserConfig) {
+  var indentUnit = config.indentUnit;
+  var Kludges = parserConfig.htmlMode ? {
+    autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
+                      'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
+                      'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
+                      'track': true, 'wbr': true},
+    implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
+                       'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
+                       'th': true, 'tr': true},
+    contextGrabbers: {
+      'dd': {'dd': true, 'dt': true},
+      'dt': {'dd': true, 'dt': true},
+      'li': {'li': true},
+      'option': {'option': true, 'optgroup': true},
+      'optgroup': {'optgroup': true},
+      'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
+            'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
+            'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
+            'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
+            'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
+      'rp': {'rp': true, 'rt': true},
+      'rt': {'rp': true, 'rt': true},
+      'tbody': {'tbody': true, 'tfoot': true},
+      'td': {'td': true, 'th': true},
+      'tfoot': {'tbody': true},
+      'th': {'td': true, 'th': true},
+      'thead': {'tbody': true, 'tfoot': true},
+      'tr': {'tr': true}
+    },
+    doNotIndent: {"pre": true},
+    allowUnquoted: true,
+    allowMissing: true
+  } : {
+    autoSelfClosers: {},
+    implicitlyClosed: {},
+    contextGrabbers: {},
+    doNotIndent: {},
+    allowUnquoted: false,
+    allowMissing: false
+  };
+  var alignCDATA = parserConfig.alignCDATA;
+
+  // Return variables for tokenizers
+  var tagName, type;
+
+  function inText(stream, state) {
+    function chain(parser) {
+      state.tokenize = parser;
+      return parser(stream, state);
+    }
+
+    var ch = stream.next();
+    if (ch == "<") {
+      if (stream.eat("!")) {
+        if (stream.eat("[")) {
+          if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
+          else return null;
+        }
+        else if (stream.match("--")) return chain(inBlock("comment", "-->"));
+        else if (stream.match("DOCTYPE", true, true)) {
+          stream.eatWhile(/[\w\._\-]/);
+          return chain(doctype(1));
+        }
+        else return null;
+      }
+      else if (stream.eat("?")) {
+        stream.eatWhile(/[\w\._\-]/);
+        state.tokenize = inBlock("meta", "?>");
+        return "meta";
+      }
+      else {
+        var isClose = stream.eat("/");
+        tagName = "";
+        var c;
+        while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
+        if (!tagName) return "error";
+        type = isClose ? "closeTag" : "openTag";
+        state.tokenize = inTag;
+        return "tag";
+      }
+    }
+    else if (ch == "&") {
+      var ok;
+      if (stream.eat("#")) {
+        if (stream.eat("x")) {
+          ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");          
+        } else {
+          ok = stream.eatWhile(/[\d]/) && stream.eat(";");
+        }
+      } else {
+        ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
+      }
+      return ok ? "atom" : "error";
+    }
+    else {
+      stream.eatWhile(/[^&<]/);
+      return null;
+    }
+  }
+
+  function inTag(stream, state) {
+    var ch = stream.next();
+    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
+      state.tokenize = inText;
+      type = ch == ">" ? "endTag" : "selfcloseTag";
+      return "tag";
+    }
+    else if (ch == "=") {
+      type = "equals";
+      return null;
+    }
+    else if (/[\'\"]/.test(ch)) {
+      state.tokenize = inAttribute(ch);
+      return state.tokenize(stream, state);
+    }
+    else {
+      stream.eatWhile(/[^\s\u00a0=<>\"\']/);
+      return "word";
+    }
+  }
+
+  function inAttribute(quote) {
+    return function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.next() == quote) {
+          state.tokenize = inTag;
+          break;
+        }
+      }
+      return "string";
+    };
+  }
+
+  function inBlock(style, terminator) {
+    return function(stream, state) {
+      while (!stream.eol()) {
+        if (stream.match(terminator)) {
+          state.tokenize = inText;
+          break;
+        }
+        stream.next();
+      }
+      return style;
+    };
+  }
+  function doctype(depth) {
+    return function(stream, state) {
+      var ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == "<") {
+          state.tokenize = doctype(depth + 1);
+          return state.tokenize(stream, state);
+        } else if (ch == ">") {
+          if (depth == 1) {
+            state.tokenize = inText;
+            break;
+          } else {
+            state.tokenize = doctype(depth - 1);
+            return state.tokenize(stream, state);
+          }
+        }
+      }
+      return "meta";
+    };
+  }
+
+  var curState, setStyle;
+  function pass() {
+    for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
+  }
+  function cont() {
+    pass.apply(null, arguments);
+    return true;
+  }
+
+  function pushContext(tagName, startOfLine) {
+    var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
+    curState.context = {
+      prev: curState.context,
+      tagName: tagName,
+      indent: curState.indented,
+      startOfLine: startOfLine,
+      noIndent: noIndent
+    };
+  }
+  function popContext() {
+    if (curState.context) curState.context = curState.context.prev;
+  }
+
+  function element(type) {
+    if (type == "openTag") {
+      curState.tagName = tagName;
+      return cont(attributes, endtag(curState.startOfLine));
+    } else if (type == "closeTag") {
+      var err = false;
+      if (curState.context) {
+        if (curState.context.tagName != tagName) {
+          if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
+            popContext();
+          }
+          err = !curState.context || curState.context.tagName != tagName;
+        }
+      } else {
+        err = true;
+      }
+      if (err) setStyle = "error";
+      return cont(endclosetag(err));
+    }
+    return cont();
+  }
+  function endtag(startOfLine) {
+    return function(type) {
+      var tagName = curState.tagName;
+      curState.tagName = null;
+      if (type == "selfcloseTag" ||
+          (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
+        maybePopContext(tagName.toLowerCase());
+        return cont();
+      }
+      if (type == "endTag") {
+        maybePopContext(tagName.toLowerCase());
+        pushContext(tagName, startOfLine);
+        return cont();
+      }
+      return cont();
+    };
+  }
+  function endclosetag(err) {
+    return function(type) {
+      if (err) setStyle = "error";
+      if (type == "endTag") { popContext(); return cont(); }
+      setStyle = "error";
+      return cont(arguments.callee);
+    };
+  }
+  function maybePopContext(nextTagName) {
+    var parentTagName;
+    while (true) {
+      if (!curState.context) {
+        return;
+      }
+      parentTagName = curState.context.tagName.toLowerCase();
+      if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
+          !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
+        return;
+      }
+      popContext();
+    }
+  }
+
+  function attributes(type) {
+    if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
+    if (type == "endTag" || type == "selfcloseTag") return pass();
+    setStyle = "error";
+    return cont(attributes);
+  }
+  function attribute(type) {
+    if (type == "equals") return cont(attvalue, attributes);
+    if (!Kludges.allowMissing) setStyle = "error";
+    else if (type == "word") setStyle = "attribute";
+    return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
+  }
+  function attvalue(type) {
+    if (type == "string") return cont(attvaluemaybe);
+    if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
+    setStyle = "error";
+    return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
+  }
+  function attvaluemaybe(type) {
+    if (type == "string") return cont(attvaluemaybe);
+    else return pass();
+  }
+
+  return {
+    startState: function() {
+      return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        state.startOfLine = true;
+        state.indented = stream.indentation();
+      }
+      if (stream.eatSpace()) return null;
+
+      setStyle = type = tagName = null;
+      var style = state.tokenize(stream, state);
+      state.type = type;
+      if ((style || type) && style != "comment") {
+        curState = state;
+        while (true) {
+          var comb = state.cc.pop() || element;
+          if (comb(type || style)) break;
+        }
+      }
+      state.startOfLine = false;
+      return setStyle || style;
+    },
+
+    indent: function(state, textAfter, fullLine) {
+      var context = state.context;
+      if ((state.tokenize != inTag && state.tokenize != inText) ||
+          context && context.noIndent)
+        return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
+      if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
+      if (context && /^<\//.test(textAfter))
+        context = context.prev;
+      while (context && !context.startOfLine)
+        context = context.prev;
+      if (context) return context.indent + indentUnit;
+      else return 0;
+    },
+
+    electricChars: "/",
+
+    configuration: parserConfig.htmlMode ? "html" : "xml"
+  };
+});
+
+CodeMirror.defineMIME("text/xml", "xml");
+CodeMirror.defineMIME("application/xml", "xml");
+if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
+  CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});

+ 784 - 0
web/js/haiya/form/me/lyhtztbg.js

@@ -0,0 +1,784 @@
+$(function(){
+	loadChangeSffs();
+	loadChangeJsgz();
+	validateZujinBiaozhunDates();//处理样式显示
+	HandlKouLvRules(); //处理扣率组样式显示
+	validateWyfDates();//处理物业费
+	changeSfjshyk();
+	
+	needUnbindContextMenuEvent();
+	
+});
+/*租赁时间变化事件**/
+function zlsjChangeEvent(){
+	validateZujinBiaozhunDates();
+	$("[name='s:me_lyhtztbg_klzgz:sjd']").trigger("change");
+	validateWyfDates();
+	validateMysfxm();
+}
+
+/*改变【是否接受会员卡】*/
+function changeSfjshyk(){
+	var sfjshyk = $("[name='m:me_lyhtztbg:sfjshyk']");
+	if(sfjshyk.length ==0){
+		sfjshyk =$("#sfjshyk").text().trim();
+	}else{
+		sfjshyk = sfjshyk.val();
+	} 
+	if(sfjshyk=='是'){
+		$(".sfjshyk_flag").show();
+	} else{ 
+		$(".sfjshyk_flag").hide();
+	}
+}
+
+/*显示租户信息*/
+function showZh(){
+	var zhid = $("[name='m:me_lyhtztbg:zhID']").val();
+	HyUtil.showZh(zhid);
+}
+
+/* 显示品牌信息 */
+function showPP(){
+	var ppId = $("[name='m:me_lyhtztbg:jyppID']").val();
+	HyUtil.showPP(ppId);
+}
+
+/*校验子表填充数据是否重复**/ 
+function checkDataBeforeInsert(data,tableName){
+    if(tableName == 'me_lyhtztbg_qtzkft' || tableName == 'me_lyhtztbg_hykzkft'){
+      var rows =   $(".listRow",$("[tableName='"+tableName+"']"));  //左右子表列
+      for(var i =0,row;row=rows[i++];){
+        var id = $("[name='s:"+tableName+":ppID']",$(row)).val();  //唯一值
+          if(id ==data.PPBM) {                            //对话框,返回数据的那列
+    	   $.ligerDialog.warn(data.PPNAME+"已经存在了!","提示信息");
+            return false;
+         }
+      }
+    }
+    
+    if(tableName == 'me_lyhtztbg_hyklx'){
+        var rows = $(".listRow",$("[tableName='"+tableName+"']"));  //左右子表列
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:"+tableName+":hyklxID']",$(row)).val();  //唯一值
+            if(id == data.HYKTYPE) {                            //对话框,返回数据的那列
+      	   $.ligerDialog.warn(data.HYKNAME+"已经存在了!","提示信息");
+              return false;
+           }
+        }
+      }
+    
+    if(tableName == 'me_lyhtztbg_pwxx'){
+        var rows =   $(".listRow",$("[tableName='me_lyhtztbg_pwxx']"));  // 左右子表列
+        // 校验该铺位是否被租出去
+        var zuLinStartDate  =$("[name='m:me_lyhtztbg:zlyxqq']").val();
+		if(!zuLinStartDate ){  $.ligerDialog.warn("尚未输入租赁开始日期!",'请核查');  return false; }
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:me_lyhtztbg_pwxx:pwID']",$(row)).val();  // 唯一值
+            if(id ==data.WLDPID) {                              // 对话框,返回数据的那列
+      	   $.ligerDialog.warn(data.WLDPDM+"已经存在了!","提示信息");
+              return false;
+           }
+        }
+      }
+    
+     return true;
+  }
+
+/* 【表结算标准信息】 添加行事件*/
+function me_lyhtztbg_jsbzxxAddRowAfterEvent(row){
+	var preRow = $(".listRow:visible",$("[tablename='me_lyhtztbg_jsbzxx']")).not(row).last();
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx)preNx = 0;
+	$("[name$=':xh']",row).val(1+Number(preNx));
+	$("[name$=':sjd']",row).val(1+Number(preNx));
+	validateZujinBiaozhunDates(row);
+	changeJsgz(row,false);
+};
+// 校验租金标准 时间
+function validateZujinBiaozhunDates(curRow){
+	//租赁有效期起,止 
+	var zuLinStart  =$("[name='m:me_lyhtztbg:zlyxqq']");
+	var zuLinStartDate  =zuLinStart.val();
+	var zuLinEndDate  =$("[name='m:me_lyhtztbg:zlyxqz']").val();
+	if((!zuLinEndDate || !zuLinStartDate)&& curRow){  
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		$(curRow).remove();
+		return
+	}
+	
+	var rows = $(".listRow:visible",$("[tablename='me_lyhtztbg_jsbzxx']"));
+	for(var i=0,row;row=rows[i++];){
+		if(i>1){
+			// 处理比较颜色
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"bdxs"); // 保底销售
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"bdkl"); // 保底扣率
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"mbxs"); // 目标销售
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"cmbkl"); // 超目标销售
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zbd"); // 总保底
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zmb"); // 总目标
+			if(zuLinStart.length ==0) continue; // 如果只读状态不再判断其他
+
+			//开始日期等于上个结束日期加1
+			var startDate = HyUtil.getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			if(zuLinStart.length ==0) continue; //只读不判断
+			
+			var differTotalStartDate = FormDate.compareDate($("[name$=':ksrq']",$(row)).val(), zuLinStartDate);
+			if(differTotalEndDate>=0)  {
+				$.ligerDialog.warn("当前开始日期不能早于 租赁开始日期!",'请核查');
+			} 
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",$(row)).val(), zuLinEndDate);
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("当前结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$=':jsrq']",$(row)).val("");
+			return ;
+		}
+	}
+	$("[name='s:me_lyhtztbg_klzgz:sjd']").trigger("change");
+	// 触发单价
+	//$("[name$=':dj']").trigger("change");
+}
+
+/*加载时,改变【结算标准信息】的【结算规则】*/
+function loadChangeJsgz(){
+	var $jsgz = $("[name$=':jsgz']:visible",$("[tablename='me_lyhtztbg_jsbzxx']")); 
+	$jsgz.each(function (i) {
+		changeJsgz(this,true);	//	如果是重新加载了,就不清空值了
+	});
+}
+
+/*改变【结算标准信息】的【结算规则】*/
+function changeJsgz(obj,isLoad){
+	var $tr = $(obj).closest("tr");
+	var jsgz = $("[name$=':jsgz']",$tr).val(); /*结算规则*/
+	
+	if(jsgz != 2){
+		generateKlz(); // 产生扣率组
+	}
+	switch(jsgz){
+		case "1":		/*实销实结,产生一条扣率组,保底销售(只读)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).attr("readonly","readonly");
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly");
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");
+			 if(!isLoad){
+				 $("[name$=':bdkl']",$tr).val("");
+				 $("[name$=':mbxs']",$tr).val("");
+				 $("[name$=':cmbkl']",$tr).val("");
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");
+			 }
+		 break;
+		case "2":		/*固定毛利额,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly");
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");
+			 if(!isLoad){
+				 $("[name$=':bdkl']",$tr).val("");
+				 $("[name$=':mbxs']",$tr).val("");
+				 $("[name$=':cmbkl']",$tr).val("");
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");
+			 }
+		  break;
+		case "3":		/*有保底销售无目标销售,产生一条扣率组,保底销售(可编辑)、保底扣率(可编辑)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).removeAttr("readonly");	
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly");
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");
+			 if(!isLoad){
+				 $("[name$=':mbxs']",$tr).val("");
+				 $("[name$=':cmbkl']",$tr).val("");
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");
+			 }
+		  break;
+		case "4":		/*有保底毛利无目标毛利,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly");
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly");	
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");
+			 if(!isLoad){
+				 $("[name$=':bdkl']",$tr).val("");
+				 $("[name$=':mbxs']",$tr).val("");
+				 $("[name$=':cmbkl']",$tr).val("");	
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");
+			 }
+			break;
+		case "5":		/*有保底销售有目标销售,产生一条扣率组,保底毛利(可编辑)、保底扣率(可编辑)、目标销售(可编辑)、超额目标扣率(可编辑)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");		
+			 $("[name$=':bdkl']",$tr).removeAttr("readonly");
+			 $("[name$=':mbxs']",$tr).removeAttr("readonly");
+			 $("[name$=':cmbkl']",$tr).removeAttr("readonly");	
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");	
+			 if(!isLoad){
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");	
+			 }
+			break;
+		case "6":		/*有保底毛利有目标毛利,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(可编辑)、超额目标扣率(可编辑)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':mbxs']",$tr).removeAttr("readonly");
+			 $("[name$=':cmbkl']",$tr).removeAttr("readonly");
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");
+			 if(!isLoad){
+				 $("[name$=':bdkl']",$tr).val("");
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");
+			 }
+			break;
+		default:
+	}
+	addNeedForKouLv();//	判断是否需要增加扣率组和扣率规则必填
+}
+
+function calBaodiMubiao(obj,targetInput){
+	var curRow = $(obj).closest(".listRow"); 
+	var baodi = FormUtil.commaback($(obj).val());
+	var mianJi = $("[name='m:me_lyhtztbg:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	var ZongJine = HyUtil.calMoneyWithMount(startDate,endDate,baodi);
+	ZongJine = FormMath.tofixed(ZongJine,2);
+	$("[name$=':"+targetInput+"']",curRow).val(ZongJine).trigger("change");
+}
+
+
+/*产生一条扣率组*/
+function generateKlz(){
+	if($(".listRow:visible",$("div[tablename='me_lyhtztbg_klz']")).length ==0){
+		FormUtil.addRow($("div[tablename='me_lyhtztbg_klz']"));  
+		var appendRow = $(".listRow:visible",$("div[tablename='me_lyhtztbg_klz']"))[0];
+		$("[name$=':klzbh']",appendRow).val(1);
+		$("[name$=':ms']",appendRow).val("扣组率1");
+	}
+}
+
+//判断是否需要增加扣率组和扣率规则必填
+function addNeedForKouLv(){
+	var isNeed = false;
+	$("[name='s:me_lyhtztbg_jsbzxx:jsgz']").each(function (i){
+		var val = $(this).val();
+		if(val != 2){
+			isNeed = true;
+			return false; // 终止循环
+		}
+	});
+	if(isNeed){
+		$("div[tablename='me_lyhtztbg_klz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='me_lyhtztbg_klz']")).length ==0){
+			$("div[tablename='me_lyhtztbg_klz']").addClass("validError");
+		}
+		
+		$("div[tablename='me_lyhtztbg_klzgz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='me_lyhtztbg_klzgz']")).length ==0){
+			$("div[tablename='me_lyhtztbg_klzgz']").addClass("validError");
+		}
+	}else{
+		$("div[tablename='me_lyhtztbg_klz']").attr("right","w");
+		$("div[tablename='me_lyhtztbg_klzgz']").attr("right","w");
+		$("div[tablename='me_lyhtztbg_klz']").removeClass("validError");
+		$("div[tablename='me_lyhtztbg_klzgz']").removeClass("validError");
+	}
+}
+
+// 扣率组删除事件
+function me_lyhtztbg_klzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+//扣率组规则删除事件
+function me_lyhtztbg_klzgzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+//扣率组添加事件
+function me_lyhtztbg_klzAddRowAfterEvent(row){
+	autoNumForKlzbm(row);
+}
+
+// 对扣率组编号自动编码
+function autoNumForKlzbm(row){
+	var preRow = $(row).prev();
+	var preBh = $("[name$=':klzbh']",preRow).val();
+	if(!preBh)preBh = 0;
+	$("[name$=':klzbh']",row).val(1+Number(preBh));
+}
+
+
+//单价计算总租金
+function calZongZuJin(obj){
+	var curRow = $(obj).closest(".listRow");
+	var danJia = FormUtil.commaback($("[name$=':dj']",curRow).val()); // 单价
+	
+	var mianJi = $("[name='m:me_lyhtztbg:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn(" 合同建筑面积不能为空!",'请核查');
+		return ;
+	}
+	var baoDiType =$("[name$=':bdxx']",curRow).val();
+	var zuJin = mianJi * danJia;
+	$("[name$=':zj']",curRow).val(zuJin); $("[name$=':zj']",curRow).trigger("change");
+	
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	if(baoDiType =="0"){ //按月
+		var zongZujin = HyUtil.calMoneyWithMount(startDate,endDate,zuJin);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongZujin = zuJin*days;
+	}
+	zongZujin = FormMath.tofixed(zongZujin,2)
+	
+	$("[name$=':zzj']",curRow).val(zongZujin); $("[name$=':zzj']",curRow).trigger("change");
+	validateZujinBiaozhunDates();/*租金发生变化修改样式显示*/
+ }
+
+
+/*处理扣率时间段*/
+function handelKoulvTime(obj){
+	var curRow = $(obj).closest(".listRow");
+	var shijianNo = $("[name$=':sjd']",curRow).val();
+	if(!shijianNo) return ;
+	/*取的时间段*/
+	var shiJianDuanNo =$("[name='s:me_lyhtztbg_jsbzxx:sjd'][value="+shijianNo+"]");
+	if(shiJianDuanNo.length ==0) {
+		$.ligerDialog.warn("该时间段不存在! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	var shiJianDuanRow = shiJianDuanNo.closest(".listRow");
+	var StartDate = $("[name$=':ksrq']",shiJianDuanRow).val();
+	var endDate = $("[name$=':jsrq']",shiJianDuanRow).val();
+	if(!StartDate || !endDate) {
+		$.ligerDialog.warn("该时间段信息不完善! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	
+	$("[name$=':ksrq']",curRow).val(StartDate);
+	$("[name$=':jsrq']",curRow).val(endDate);
+}
+/*处理扣率组大小比较*/
+function HandlKouLvRules(){
+	var kouLvRules = $(".listRow:visible",$("[tablename='me_lyhtztbg_klzgz']"));
+	var combineMessage = [];
+	for(var i=0,rule;rule=kouLvRules[i++];){
+		var sjdInput=$("[name$=':sjd']",rule);
+		if(sjdInput.length==0){ /*只读状态*/
+			var sjd = $("[name='sjd']",rule).text().trim(); /*时间段*/
+			var klz = $("[name='klz']",rule).text().trim();/*扣率组*/
+		}else{
+			var sjd = sjdInput.val(); /*时间段*/
+			var klz = $("[name$=':klz']",rule).val();/*扣率组*/
+		}
+		if(!sjd || !klz) return;
+		 /*第一次设置开始金额为当前金额为第几行*/	
+		var len=-1;
+		for(var j=0,r;r=combineMessage[j++];){
+			if(r.split("-")[0] == sjd+","+klz){
+				len =Number(r.split("-")[1]);
+			}
+		}
+		if(len!=-1){
+			HyUtil.fillColorByCompare(rule,kouLvRules[len],"kl");
+		}
+		combineMessage.push(sjd+","+klz+"-"+(i-1));
+	}
+}
+
+/*处理扣率组*/
+function handelKoulvGroup(obj){
+	var curRow = $(obj).closest(".listRow");
+	var koulvNo = $(obj).val();
+	if(!koulvNo) return ;
+	/*取扣率组*/
+	var KouLv =$("[name='s:me_lyhtztbg_klz:klzbh'][value="+koulvNo+"]");
+	if(KouLv.length ==0) {
+		$.ligerDialog.warn("该扣率组不存在! “"+koulvNo+"”",'请核查');
+		$(obj).val("");
+		return ;
+	}
+	
+	var klzgzMs = KouLv.closest("tr").find("[name='s:me_lyhtztbg_klz:ms']").val();		//	扣率组的【描述】
+	$(obj).closest("tr").find("[name='s:me_lyhtztbg_klzgz:ms']").val(klzgzMs);	
+}
+
+/*【物业费条款添加事件】me_lyhtztbg_wyftk*/
+function me_lyhtztbg_wyftkAddRowAfterEvent(curRow){
+	var preRow = $(".listRow:visible",$("[tablename='me_lyhtztbg_wyftk']")).not(curRow).last();
+
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx)preNx = 0;
+	
+	$("[name$=':nx']",curRow).val(1+Number(preNx));
+	$("[name$=':xh']",curRow).val(1+Number(preNx));
+	if(validateWyfDates(curRow) == false)$(curRow).remove();
+	
+	// 设置公司标准
+	setGsbz(curRow);
+}
+
+/*设置公司标准*/
+function setGsbz(curRow){
+    var fdbh = $("[name='m:me_lyhtztbg:fdID']").val();
+    var conf = {
+        aliasName:'mdwyfbz',      //脚本的别名(唯一的)
+        arg0:fdbh
+    };
+    //执行别名脚本调用方法
+    var json = RunAliasScript(conf); //结果是返回JSON数据
+    if (json.isSuccess == 0) {
+        $("[name='s:me_lyhtztbg_wyftk:gsbz']",curRow).val(json.result[0].BZJE);
+    } else {
+        alert(json.msg);
+    }
+}
+
+/*校验物业费时间**/
+function validateWyfDates(curRow){
+	//租赁有效期起
+	var zuLinStart =$("[name='m:me_lyhtztbg:zlyxqq']");
+	var zuLinStartDate  =zuLinStart.val();
+	var zuLinEndDate  =$("[name='m:me_lyhtztbg:zlyxqz']").val();
+	if((!zuLinStartDate || !zuLinStartDate) && curRow){ 
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		return false;
+	}
+	//循环计算开始日期
+	var rows = $(".listRow:visible",$("[tablename='me_lyhtztbg_wyftk']"));
+	for(var i=0,row;row=rows[i++];){
+		if(i>1){
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"xs");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"dj");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"je");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zje");
+		}
+		if(zuLinStart.length==0) continue;
+		
+		var shiJianDuan = $("[name$=':sjd']",$(row)).val(i); //时间段
+		if(i>1){
+			//开始日期等于上个结束日期加1
+			var startDate = HyUtil.getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		//校验结束日期
+		var jsrq =$("[name$=':jsrq']",row).val();
+		if(!jsrq) continue;
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate);
+		if(differTotalEndDate<0){
+			$.ligerDialog.warn("结束日期不能晚于租赁有效期止!",'请核查!');
+			$("[name$=':jsrq']",curRow).val("")
+		}
+	}
+}
+
+
+/*总物业费**/
+function calZongWuYeFei(obj){
+	var curRow = $(obj).closest(".listRow"); 
+	//校验是否超过总日期
+	var zuLinEndDate =$("[name='m:me_lyhtztbg:zlyxqz']").val();
+	var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",curRow).val(), zuLinEndDate);
+	if(differTotalEndDate<0)  {
+		$.ligerDialog.warn("当前结束日期不能晚于租赁有效期止!",'请核查');
+		$("[name$=':jsrq']",curRow).val("")
+		return ;
+	}
+	
+	var danJia = FormUtil.commaback($("[name$=':dj']",curRow).val()); // 单价
+	
+	var mianJi = $("[name='m:me_lyhtztbg:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var baoDiType =$("[name$=':glfxx']",curRow).val();
+	
+	var xs = $("[name$=':xs']",curRow).val();
+	var jine = FormMath.tofixed(mianJi * danJia * xs,2);
+	
+	$("[name$=':je']",curRow).val(jine); $("[name$=':je']",curRow).trigger("change");
+	
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	if(baoDiType =="0"){ //按月
+		var zongJine = HyUtil.calMoneyWithMount(startDate,endDate,jine);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongJine = jine*days;
+	}
+	zongJine =FormMath.tofixed(zongJine,2);
+	$("[name$=':zje']",curRow).val(zongJine);$("[name$=':zje']",curRow).trigger("change");
+ }
+
+/*加载时,改变【每月收费项目】的【收费方式】*/
+function loadChangeSffs(){
+	var sffs = $("[name$=':sffs']:visible",$("[tablename='me_zlmysfxm']"));
+	sffs.each(function (i) {
+		changeSffs(this);
+	});
+}
+
+/*改变【每月收费项目】的【收费方式】*/
+function changeSffs(obj){
+	var $tr = $(obj).closest("tr");
+	var sffs = $("[name$=':sffs']",$tr).val(); /*收费方式*/
+	$("[name$=':dj']",$tr).off();
+	 $("[name='shouFeiGuiZe']",$tr).show();
+	switch(sffs){
+		case "0":		/*固定金额类型 : 只能录入收费金额*/
+		case "4":		
+			 $("[name$=':dj']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':sfje']",$tr).removeAttr("readonly");
+			 $("[name$=':kl']",$tr).attr("readonly","readonly").val("");
+		  break;
+		case "1":		/*比率类型:只能录入比率*/
+		case "2":
+		case "5":
+			 $("[name$=':sfgz']",$tr).val("");
+			 $("[name$=':sfgzID']",$tr).val("");
+			 $("[name='shouFeiGuiZe']",$tr).hide().parent().css("width","170px");
+			$("[name$=':dj']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':kl']",$tr).removeAttr("readonly");
+		  break;
+		case "3":		/*每平方米单价类型:只能录入单价*/
+			 $("[name$=':dj']",$tr).removeAttr("readonly");
+			$("[name$=':sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':kl']",$tr).attr("readonly","readonly").val("");
+			//计算价格
+			 $("[name$=':dj']",$tr).on("blur",function(){
+				 var dj =  FormUtil.commaback($(this).val()); 
+				 var curRow = $(this).closest(".listRow"); 
+				 var mianJi = $("[name='m:me_lyhtztbg:jzmj']").val();
+				 if(!mianJi) $.ligerDialog.warn("尚未生成面积!",'提示');
+				 $("[name$=':sfje']",curRow).val(FormMath.tofixed(dj*mianJi,2));
+			 });
+		  break;
+		default:
+	}
+}
+
+/*校验每月收费项目*/
+function validateMysfxm (){
+	var zuLinStartDate  =$("[name='m:me_lyhtztbg:zlyxqq']").val();
+	var zuLinEndDate  =$("[name='m:me_lyhtztbg:zlyxqz']").val();
+	
+	var sfxm = $(".listRow:visible",$("[tablename='me_lyhtztbg_mysfxm']"));
+	var sfxmArray =[];
+	
+	for(var i=0,row;row=sfxm[i++];){
+		var ksrq = $("[name$=':ksrq']",$(row)).val();
+		var jsrq = $("[name$=':jsrq']",$(row)).val();
+		if(!ksrq) continue;
+		var differTotalStartDate = FormDate.compareDate(ksrq, zuLinStartDate);
+		if(differTotalStartDate>0){
+			$.ligerDialog.warn("每月收费项目开始日期不能早于 租赁开始日期!",'请核查');
+			$("[name$=':ksrq']",$(row)).val("")
+		}
+		
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate);
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("每月收费项目结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$=':jsrq']",$(row)).val("")
+		}
+		var curSfxmID =$("[name$=':sfxmID']",$(row)).val();
+		if(curSfxmID){
+			for(var j=0,prevSfxm;prevSfxm=sfxmArray[j++];){
+				var thisSfxmId = prevSfxm.split("$")[0];
+				/*当前收费项目第二次出现*/
+				if(thisSfxmId == curSfxmID){
+					var differToPrevDate = FormDate.compareDate(ksrq,prevSfxm.split("$")[1]);
+					if(differToPrevDate>=0){
+						$.ligerDialog.warn("同一收费项目日期不得重复!",'请核查');
+						$("[name$=':ksrq']",$(row)).val("");
+					}
+				}
+			  }
+			
+			sfxmArray.push(curSfxmID+"$"+jsrq);
+			}
+		}
+}
+
+
+
+
+/*结算标准信息   删除行事件*/
+function me_lyhtztbg_jsbzxxDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:me_lyhtztbg:xh'][value=" + xh + "]").closest("tr").remove();/*清除旧的记录*/
+}
+
+/*分解所有结算标准信息  */
+function decomposeAllJs(tableName,fenjieTable){
+	var $jsbzxxTrs = $(".listRow:visible",$("[tablename='"+tableName+"']"));   
+	$jsbzxxTrs.each(function (i) {
+		decomposeSingle($(this),fenjieTable);
+	});
+}
+function decomposeAllWyf(){
+	decomposeAllJs('me_lyhtztbg_wyftk','me_lyhtztbg_wyffj');
+}
+function decomposeSingleWyf(){
+	decomposeSingleJs('me_lyhtztbg_wyftk','me_lyhtztbg_wyffj');
+}
+function decomposeAllBaodi(){
+	decomposeAllJs('me_lyhtztbg_jsbzxx','me_lyhtztbg_bdxx');
+}
+function decomposeSingleBaodi(){
+	decomposeSingleJs('me_lyhtztbg_jsbzxx','me_lyhtztbg_bdxx');
+}
+
+/*单个分解*/
+function decomposeSingleJs(tableName,fenjieTable){
+	$jsbzxxTrs = $("input[name$=':fjsj']:checked",$("div[tablename='"+tableName+"']")).closest("tr");
+	if($jsbzxxTrs.length ==0){
+		$.ligerDialog.warn("请选择要分解的数据!","提示信息");
+		return;
+	}
+	//分解
+	decomposeSingle($jsbzxxTrs,fenjieTable);
+} 
+
+/*【通过分解目标行来分解信息,将结果输出至分解表】 
+ * 被选中的行:selectRow,分解表的表明fenJieTable*/
+function decomposeSingle(selectRow,fenJieTable){
+	var xh = selectRow.find("[name$=':xh']").val();	
+	if(!xh){
+		$.ligerDialog.warn("请选择要分解的时间段","提示");
+		return ;
+	}
+	//分解表
+	var fenJieTableDiv = $("div[tablename='"+fenJieTable+"']");
+	/*将序号与当前选中行所有分解信息删除*/
+	$("[name$=':xh'][value=" + xh + "]",fenJieTableDiv).closest("tr").remove(); /*清除旧的记录*/
+	
+	var sjd =$("[name$=':xh']",selectRow).val(); //序号
+	var ksrq =$("[name$=':ksrq']",selectRow).val();
+	var jsrq =$("[name$=':jsrq']",selectRow).val();
+	var yzj,mbxs,type;
+	//物业费
+	if(fenJieTable == 'me_lyhtztbg_wyffj'){
+		yzj =FormUtil.commaback($("[name$=':je']",selectRow).val());
+		type =$("[name$=':glfxx']",selectRow).val();
+	//标准租金
+	}else{
+		yzj=FormUtil.commaback($("[name$=':bdxs']",selectRow).val()); 
+		 mbxs =FormUtil.commaback($("[name$=':mbxs']",selectRow).val()); 
+	}
+	var jsonData = HyUtil.decompose(sjd,ksrq,jsrq,yzj,"",mbxs,type);
+	
+	/*填充数据*/
+	for (var i = 0, c; c = jsonData[i++];) {
+		FormUtil.addRow(fenJieTableDiv);
+		var rowcount=$("input[name$=':ny']",fenJieTableDiv).length;
+		$($("input[name$=':xh']",fenJieTableDiv).get(rowcount-1)).val(xh);		
+		$($("input[name$=':ny']",fenJieTableDiv).get(rowcount-1)).val(c.ZQY);				
+		$($("input[name$=':ksrq']",fenJieTableDiv).get(rowcount-1)).val(c.KSRQ);			
+		$($("input[name$=':jsrq']",fenJieTableDiv).get(rowcount-1)).val(c.JSRQ);			
+		if(fenJieTable == 'me_lyhtztbg_wyffj'){
+			$($("input[name$=':je']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);	
+		}else{
+			$($("input[name$=':bdxsml']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);	
+			$($("input[name$=':mbxsml']",fenJieTableDiv).get(rowcount-1)).val(c.MBXS);
+		}
+	}
+}
+
+/*显示分解信息列表 */
+function showFjsj(obj,fenJieTable){
+	var fenJieTableDiv =$("div[tablename='"+fenJieTable+"']");
+	var tableDiv = $(obj).closest("[tablename]");
+	var selectRow =$("input[name=':fjsj']:checked",tableDiv).closest("tr");
+	var xhVal = selectRow.find("[name$=':xh']").val();/*选择行*/
+	
+	HyUtil.showDecompose(fenJieTable,'xh',xhVal);
+}
+
+
+/**分解代码 end**/
+
+/*格式化填充的日期*/
+$().ready(function (){
+	$("[issameas]").change(function(){
+		var me = $(this),curVal = me.val();
+		var curName = me.attr("name");
+		if(!curName) return;
+		curName = curName.split(":");
+		var targetName = me.attr("isSameAs");
+		if(targetName=='true' && curName.length == 3){
+			targetName =curName[0] +":"+curName[1] +":y"+curName[2]
+		}
+		 
+		var targetValue = $("[name='"+targetName+"']").val();
+		if(targetValue != curVal){
+			me.css("color","red");
+		}else{
+			me.css("color","");
+		}
+	});
+	
+	$("div[tablename='me_lyhtztbg_bdxx']").hide();	/* 【月度租金分解】不能直接控制隐藏,否则权限控制不了,先隐藏全部,在显示个体 */
+	$("div[tablename='me_lyhtztbg_wyffj']").hide();	/* 【物业费分解】不能直接控制隐藏,否则权限控制不了,先隐藏全部,在显示个体 */
+	
+});
+
+/*根据【付款天数】获取【联营结算日】*/
+function getJsrByFkts(){
+	var fkts = $("[name='m:me_lyhtztbg:fkts']").val();
+	lyjsr = HyUtil.getJsrByFkts(fkts);
+	$("[name='m:me_lyhtztbg:lyjsr']").val(lyjsr);
+}
+
+function CustomQueryAftEvent__(){
+	validateZujinBiaozhunDates();//处理样式显示
+	validateWyfDates();//物业费样式显示
+	
+	needUnbindContextMenuEvent();
+	HyUtil.unbindContextMenuEvent("me_lyhtztbg_yjsbzxx");	//	原租金标准信息
+	HyUtil.unbindContextMenuEvent("me_lyhtztbg_yklzgz");	//	原扣率组规则
+	HyUtil.unbindContextMenuEvent("me_lyhtztbg_ywyftk");	//	原物业费条款
+	
+}
+
+//需要特殊处理右键事件
+function needUnbindContextMenuEvent(){
+	var pwxxRows = $(".listRow:visible",$("[tablename='me_lyhtztbg_pwxx']"));
+	pwxxRows.each(function (i){
+		var sfypw = $(this).find("[name='s:me_lyhtztbg_pwxx:sfypw']").val();
+		if(sfypw == "true"){
+			$(this).unbind("contextmenu");
+			$(this).find(".extend").remove();
+			$(this).find("[name='s:me_lyhtztbg_pwxx:zywz']").attr("readonly","readonly");
+		}
+	});
+}
+
+
+
+

+ 656 - 0
web/js/haiya/form/me/lytksb.js

@@ -0,0 +1,656 @@
+$().ready(function (){
+	
+	changeSftstk();	/*【是否特殊条款】*/
+	changeSblx();	/*【申报类型】*/
+	
+	loadChangeSffs();	/*加载时,改变【每月收费项目】的【收费方式】*/
+	
+	loadChangeJsgz(); /*加载时,改变【结算标准信息】的【结算规则】*/
+});
+
+
+/*校验子表填充数据是否重复**/ 
+function checkDataBeforeInsert(data,tableName){
+	if(tableName == 'me_lypwxx'){
+	      var rows =   $(".listRow",$("[tableName='me_lypwxx']"));  //左右子表列
+	      for(var i =0,row;row=rows[i++];){
+	        var id = $("[name='s:me_lypwxx:pwID']",$(row)).val();  //唯一值
+	          if(id ==data.WLDPID) {                              //对话框,返回数据的那列
+	    	   $.ligerDialog.warn(data.WLDPDM+"已经存在了!","提示信息");
+	            return false
+	         }
+	      }
+	    }
+    
+     return true;
+  }
+
+/*租赁时间变化事件**/
+function zlsjChangeEvent(){
+	validateJsbzxxDates();
+	$("[name='s:me_lyklzgz:sjd']").trigger("change");
+	validateWyfDates();
+	validateMysfxm();
+}
+
+/*==============主表事件=================*/
+
+/*改变【是否特殊条款】*/
+function changeSftstk(){
+	var sftstk = $("[name='m:me_lytksbb:sftstk']").val();
+	if(typeof sftstk == "undefined"){
+		sftstk = $.trim($("#sftstk").text());
+	}
+	if(sftstk == "1" || sftstk == "是"){
+		$("#tstk_tr").show();
+	}else{
+		$("#tstk_tr").hide();
+	}
+}
+
+/*改变【申报类型】*/
+function changeSblx(){
+	var sblx = $("[name='m:me_lytksbb:sblx']").val();
+	if(typeof sblx == "undefined"){
+		sblx = $.trim($("#sblx").text());
+	}
+	if(sblx == "2" || sblx == "重新申报"){
+		$("#sbyy_tr").show();
+	}else{
+		$("#sbyy_tr").hide();
+	}
+}
+
+/*计算所有铺位*/
+function calAllPuwei(obj){
+	var puweiTrs = $(".listRow:visible",$("[tablename='me_lypwxx']")); 
+	var shangPuNums = "";
+	puweiTrs.each(function (i) {
+	  var pwId = $("[name$=':dph']",$(this)).val();
+	  shangPuNums = shangPuNums + pwId;
+	  if(i != puweiTrs.length-1)shangPuNums = shangPuNums+"-";
+	});
+	$("[name='m:me_lytksbb:sph']").val(shangPuNums); 
+}
+
+/*============每月收费项目=================*/
+
+/*加载时,改变【每月收费项目】的【收费方式】*/
+function loadChangeSffs(){
+	var $sffs = $("[name$=':sffs']:visible",$("[tablename='me_lymysfxm']")); 
+	$sffs.each(function (i) {
+		changeSffs(this);
+	});
+}
+
+/*每月收费项目  添加行事件*/
+function me_lymysfxmAddRowAfterEvent(row){
+	loadChangeSffs();
+}
+
+/*改变【每月收费项目】的【收费方式】*/
+function changeSffs(obj){
+	var $tr = $(obj).closest("tr");
+	var sffs = $("[name$='sffs']",$tr).val(); /*收费方式*/
+	$("[name$='dj']",$tr).off();
+	 $("[name='shouFeiGuiZe']",$tr).show();
+	switch(sffs){
+		case "0":		/*固定金额类型 : 只能录入收费金额*/
+		case "4":		
+			 $("[name$='dj']",$tr).attr("readonly","readonly").val("");
+			 $("[name$='sfje']",$tr).removeAttr("readonly");
+			 $("[name$='kl']",$tr).attr("readonly","readonly").val("");
+		  break;
+		case "1":		/*比率类型:只能录入比率*/
+		case "2":
+		case "5":
+			 $("[name$=':sfgz']",$tr).val("");
+			 $("[name$=':sfgzID']",$tr).val("");
+			 $("[name='shouFeiGuiZe']",$tr).hide().parent().css("width","170px");
+			$("[name$='dj']",$tr).attr("readonly","readonly").val("");
+			$("[name$='sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$='kl']",$tr).removeAttr("readonly");
+		  break;
+		case "3":		/*每平方米单价类型:只能录入单价*/
+			 $("[name$='dj']",$tr).removeAttr("readonly");
+			$("[name$='sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$='kl']",$tr).attr("readonly","readonly").val("");
+			//计算价格
+			 $("[name$='dj']",$tr).on("blur",function(){
+				 var dj =  FormUtil.commaback($(this).val()); 
+				 var curRow = $(this).closest(".listRow"); 
+				 var mianJi = $("[name='m:me_lytksbb:jzmj']").val();
+				 if(!mianJi) alert("尚未生成面积");
+				 $("[name$='sfje']",curRow).val(dj*mianJi);
+			 });
+		  break;
+		default:
+	}
+}
+/*校验每月收费项目*/
+function validateMysfxm (){
+	var zuLinStartDate  =$("[name='m:me_lytksbb:zlyxqq']").val();
+	var zuLinEndDate  =$("[name='m:me_lytksbb:zlyxqz']").val();
+	
+	var sfxm = $(".listRow:visible",$("[tablename='me_lymysfxm']"));
+	var sfxmArray =[];
+	
+	for(var i=0,row;row=sfxm[i++];){
+		var ksrq = $("[name$='ksrq']",$(row)).val();
+		var jsrq = $("[name$='jsrq']",$(row)).val();
+		if(!ksrq) continue;
+		var differTotalStartDate = FormDate.dateVal(ksrq, zuLinStartDate, "day");
+		if(differTotalStartDate>0){
+			$.ligerDialog.warn("每月收费项目开始日期不能早于 租赁开始日期!",'请核查');
+			$("[name$='ksrq']",$(row)).val("")
+		}
+		
+		var differTotalEndDate = FormDate.dateVal(jsrq, zuLinEndDate, "day");
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("每月收费项目结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$='jsrq']",$(row)).val("")
+		}
+		var curSfxmID =$("[name$='sfxmID']",$(row)).val();
+		if(curSfxmID){
+			for(var j=0,prevSfxm;prevSfxm=sfxmArray[j++];){
+				var thisSfxmId = prevSfxm.split("$")[0];
+				/*当前收费项目第二次出现*/
+				if(thisSfxmId == curSfxmID){
+					var differToPrevDate = FormDate.dateVal(ksrq,prevSfxm.split("$")[1], "day");
+					if(differToPrevDate>=0){
+						$.ligerDialog.warn("同一收费项目日期不得重复!",'请核查');
+						$("[name$='ksrq']",$(row)).val("");
+					}
+				}
+			  }
+			
+			sfxmArray.push(curSfxmID+"$"+jsrq);
+			}
+		}
+}
+
+
+/*显示租户信息*/
+function showZh(){
+	var zhid = $("[name$='zhid']").val();
+	if(zhid == null || zhid == ""){
+		$.ligerDialog.warn("请先选择租户!","提示");
+		return ;
+	}
+	var url=__ctx + "/platform/form/bpmDataTemplate/detailData_shxxwh.ht?__pk__="+zhid;
+	DialogUtil.open({
+		height:600,
+		width: 800,
+		title : "查看租户信息",
+		url: url, 
+		isResize: true
+	});
+}
+
+
+/*========== 结算标准信息 start =============*/
+
+/*结算标准信息  添加行事件*/
+function me_lyjsbzxxAddRowAfterEvent(row){
+	var preRow = $(row).prev();
+	if(preRow.attr("style") == 'display: none;')
+		$("[name$=':xh']",row).val(1);
+	else {
+		var preNx = $("[name$=':xh']",preRow).val();
+		$("[name$=':xh']",row).val(1+Number(preNx));
+	}
+	if(validateJsbzxxDates() ==false) $(row).remove();
+	
+	loadChangeJsgz();
+}
+
+/*结算标准信息   删除行事件*/
+function me_lyjsbzxxDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:me_lybdxx:xh'][value=" + xh + "]").closest("tr").remove();/*清除旧的记录*/
+}
+
+/*分解所有结算标准信息*/
+function decomposeAllJs(){
+	var $jsbzxxTrs = $(".listRow:visible",$("[tablename='me_lyjsbzxx']")); 
+	$jsbzxxTrs.each(function (i) {
+		decomposeSingleJs(this);
+	});
+	
+	var $jsbzxxTrs = $("input[name='jsfj']:checked").closest("tr");
+	showJsFj($jsbzxxTrs);
+}
+
+/*分解单个结算标准信息*/
+function decomposeSingleJs(obj){
+	var selectRow = $(obj);
+	var isSingle = false;
+	if(typeof obj == "undefined"){
+		selectRow = $("input[name='fjsj']:checked").closest("tr");
+		isSingle = true;
+	}
+	var xh = selectRow.find("[name$=':xh']").val();	/*obj 是结算标准对象*/
+	if(!xh){
+		$.ligerDialog.warn("请选择要分解的时间段","提示");
+		return ;
+	}
+	
+	$("[name='s:me_lybdxx:xh'][value=" + xh + "]").closest("tr").remove(); /*清除旧的记录*/
+	
+	var sjd =$("[name$='xh']",selectRow).val(); //序号
+	var ksrq =$("[name$='ksrq']",selectRow).val();
+	var jsrq =$("[name$='jsrq']",selectRow).val();
+	var bdxs =FormUtil.commaback($("[name$='bdxs']",selectRow).val()); 
+	var mbxs =FormUtil.commaback($("[name$='mbxs']",selectRow).val()); //目标销售
+	
+	var jsonData = decompose(sjd,ksrq,jsrq,bdxs,"",mbxs);
+	/*填充数据*/
+	var fenJieTableDiv =$('div[tablename="me_lybdxx"]');
+	for (var i = 0, c; c = jsonData[i++];) {
+		FormUtil.addRow(fenJieTableDiv);
+		var rowcount=$("input[name$=':ny']",fenJieTableDiv).length;
+		$($("input[name$=':xh']",fenJieTableDiv).get(rowcount-1)).val(xh);		
+		$($("input[name$=':ny']",fenJieTableDiv).get(rowcount-1)).val(c.ZQY);				
+		$($("input[name$=':ksrq']",fenJieTableDiv).get(rowcount-1)).val(c.KSRQ);			
+		$($("input[name$=':jsrq']",fenJieTableDiv).get(rowcount-1)).val(c.JSRQ);			
+		$($("input[name$=':bdxs']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);//保底销售/毛利
+		$($("input[name$=':mbxs']",fenJieTableDiv).get(rowcount-1)).val(c.MBXS);//目标销售/毛利
+	}
+	
+	if(isSingle){
+		showJsFj(selectRow);
+	}
+}
+
+/*分解数据*/
+function decompose(sjd,ksrq,jsrq,zj,gzid,mbxs){
+	if(!zj) zj =0;
+	var paramJson = {jlbh:sjd,ksrq:ksrq,jsrq:jsrq,yzj:zj};
+	if(mbxs) paramJson.mbxs =mbxs;
+	if(gzid) paramJson.gzid =gzid;
+	var jsonParams = [];
+	jsonParams.push(paramJson);
+	var conf = {aliasName:'decompose',paramJson:JSON.stringify(jsonParams)};
+	var json = RunAliasScript(conf); 
+    if(json.isSuccess==0){
+    	return JSON.parse(json.result);
+	 }else{
+		 $.ligerDialog.error("分解失败:"+json.result,"提示信息");
+		 return [];
+	 }
+}
+
+
+
+/*显示保底信息列表*/
+function showJsFj(obj){
+	var $jsbzxxTrs = $(obj);
+	if(typeof obj == "undefined"){
+		$jsbzxxTrs = $("input[name='jsfj']:checked").closest("tr");
+	}
+	var xh = $jsbzxxTrs.find("[name$=':xh']").val();
+	$(".listRow:visible",$("[tablename='me_lybdxx']")).hide();	/*先隐藏全部,在显示个体*/
+	$("[name='s:me_lybdxx:xh'][value=" + xh + "]").closest("tr").show(); 
+	
+	$("td.tdNo", $(".listRow:visible",$("[tablename='me_lybdxx']"))).each(function(i) {
+		$(this).text(i + 1);
+	});
+	
+	$("[name='s:me_lybdxx:bdxs']").trigger("blur");
+	$("[name='s:me_lybdxx:mbxs']").trigger("blur");
+}
+
+// 校验租金标准 时间
+function validateJsbzxxDates(){
+	//租赁有效期起,止
+	var zuLinStartDate  =$("[name='m:me_lytksbb:zlyxqq']").val();
+	var zuLinEndDate  =$("[name='m:me_lytksbb:zlyxqz']").val();
+	if(!zuLinStartDate || !zuLinStartDate){ 
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		return false
+	}
+	
+	var rows = $(".listRow:visible",$("[tablename='me_lyjsbzxx']"));
+	for(var i=0,row;row=rows[i++];){
+		var shiJianDuan = $("[name$='sjd']",$(row)).val(i); //时间段
+		
+		if(i>1){
+			//开始日期等于上个结束日期加1
+			var startDate = getNewDataStr($("[name$='jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$='ksrq']",$(row)).val(startDate);
+		}else{
+			var differTotalStartDate = FormDate.dateVal($("[name$='ksrq']",$(rows)).val(), zuLinStartDate, "day");
+			if(differTotalEndDate>=0)  {
+				$.ligerDialog.warn("当前开始日期不能早于 租赁开始日期!",'请核查');
+			} 
+			$("[name$='ksrq']",$(row)).val(zuLinStartDate);
+		}
+		var differTotalEndDate = FormDate.dateVal($("[name$='jsrq']",$(rows)).val(), zuLinEndDate, "day");
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("当前结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$='jsrq']",$(rows)).val("")
+			return ;
+		}
+	}
+	$("[name='s:me_lyklzgz:sjd']").trigger("change");
+}
+
+//type: m=2/d=1/y=3   number:几天
+function getNewDataStr(curDateStr,number,type){
+	if(!curDateStr) return "";
+	curDateStr = curDateStr.replace(/\-/g, "/");
+	var curDate =  new Date(curDateStr);
+	if(type==1) curDate.setDate(curDate.getDate()+number); 
+	if(type==2) curDate.setMonth(curDate.getMonth()+number);
+	if(type==3) curDate.setFullYear(curDate.getFullYear()+number); 
+	
+	return curDate.Format("yyyy-MM-dd");
+}
+
+/*加载时,改变【结算标准信息】的【结算规则】*/
+function loadChangeJsgz(){
+	var $jsgz = $("[name$=':jsgz']:visible",$("[tablename='me_lyjsbzxx']")); 
+	$jsgz.each(function (i) {
+		changeJsgz(this);
+	});
+}
+
+/*改变【结算标准信息】的【结算规则】*/
+function changeJsgz(obj){
+	var $tr = $(obj).closest("tr");
+	var jsgz = $("[name$=':jsgz']",$tr).val(); /*结算规则*/
+	 generateKlz();
+	switch(jsgz){
+		case "1":		/*实销实结,产生一条扣率组,保底销售(只读)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).attr("readonly","readonly").val("");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly").val("");
+			 
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+		 break;
+		case "2":		/*固定毛利额,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly").val("");
+			 
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+			
+		  break;
+		case "3":		/*有保底销售无目标销售,产生一条扣率组,保底销售(可编辑)、保底扣率(可编辑)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");
+			 $("[name$=':bdkl']",$tr).removeAttr("readonly");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly").val("");
+			 
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+		  break;
+		case "4":		/*有保底毛利无目标毛利,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly").val("");
+			 
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly").val("");	
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+			break;
+		case "5":		/*有保底销售有目标销售,产生一条扣率组,保底毛利(可编辑)、保底扣率(可编辑)、目标销售(可编辑)、超额目标扣率(可编辑)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");		
+			 $("[name$=':bdkl']",$tr).removeAttr("readonly");
+			 $("[name$=':mbxs']",$tr).removeAttr("readonly");
+			 
+			 $("[name$=':cmbkl']",$tr).removeAttr("readonly");	
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");	
+			 
+			break;
+		case "6":		/*有保底毛利有目标毛利,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(可编辑)、超额目标扣率(可编辑)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':mbxs']",$tr).removeAttr("readonly");
+			 
+			 $("[name$=':cmbkl']",$tr).removeAttr("readonly");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+			 
+			break;
+		default:
+	}
+}
+
+/*产生一条扣率组*/
+function generateKlz(){
+	var $kouLv = $(".listRow:visible",$("[tablename='me_lyklz']"));
+	if($kouLv.size() == 0){
+		FormUtil.addRow($('div[tablename$="me_lyklz"]'));  
+		var appendRow = $(".listRow:visible",$("[tablename$='me_lyklz']"))[0];
+		$("[name$='klzbh']",appendRow).val(1);
+		$("[name$='ms']",appendRow).val("扣组率1");
+	}
+}
+
+/*===============扣率规则==================*/
+
+/*处理扣率时间段*/
+function handelKoulvTime(obj){
+	var curRow = $(obj).closest(".listRow");
+	var shijianNo = $("[name$='sjd']",curRow).val();
+	if(!shijianNo) return ;
+	/*取的时间段*/
+	var shiJianDuanNo =$("[name='s:me_lyjsbzxx:sjd'][value="+shijianNo+"]");
+	if(shiJianDuanNo.length ==0) {
+		$.ligerDialog.warn("该时间段不存在! “"+shijianNo+"”",'请核查');
+		$("[name$='sjd']",curRow).val("");
+		return ;
+	}
+	var shiJianDuanRow = shiJianDuanNo.closest(".listRow");
+	var startDate = $("[name$='ksrq']",shiJianDuanRow).val();
+	var endDate = $("[name$='jsrq']",shiJianDuanRow).val();
+	if(!startDate || !endDate) {
+		$.ligerDialog.warn("改时间段信息不完善! “"+shijianNo+"”",'请核查');
+		$("[name$='sjd']",curRow).val("");
+		return ;
+	}
+	
+	$("[name$='ksrq']",curRow).val(startDate);
+	$("[name$='jsrq']",curRow).val(endDate);
+	
+	initXSJEQvalidateRule();
+	
+	$("[name='s:me_lyklzgz:zqxsjeq']").trigger("blur");
+	$("[name='s:me_lyklzgz:zqxsjez']").trigger("blur");
+}
+
+/*处理扣率组*/
+function handelKoulvGroup(obj){
+	var curRow = $(obj).closest(".listRow");
+	var koulvNo = $(obj).val();
+	if(!koulvNo) return ;
+	/*取扣率组*/
+	var KouLv =$("[name='s:me_lyklz:klzbh'][value="+koulvNo+"]");
+	if(KouLv.length ==0) {
+		$.ligerDialog.warn("该扣率组不存在! “"+koulvNo+"”",'请核查');
+		$(obj).val("");
+		return ;
+	}
+	initXSJEQvalidateRule();
+	
+	$("[name='s:me_lyklzgz:zqxsjeq']").trigger("blur");
+	$("[name='s:me_lyklzgz:zqxsjez']").trigger("blur");
+}
+
+/*校验周期销售金额止*/
+function checkZqxsjez(obj){
+	var curRow = $(obj).closest(".listRow");
+	 var JeQi = $("[name$='zqxsjeq']",curRow).val();
+	 var jeZhi =$(obj).val();
+	 if(!JeQi || !jeZhi)
+	 if( FormUtil.commaback(JeQi) > FormUtil.commaback(jeZhi)){
+		 $.ligerDialog.warn("周期销售金额止:"+jeZhi+  " 不应该小于 周期销售金额起 :"+JeQi+"",'请核查!');
+			$(obj).val("");
+			return ;
+	 }
+	 initXSJEQvalidateRule();
+	 
+	 $("[name='s:me_lyklzgz:zqxsjeq']").trigger("blur");
+	 $("[name='s:me_lyklzgz:zqxsjez']").trigger("blur");
+}
+
+/*循环判断扣率组,初始化销售金额起,校验扣率组金额*/
+function initXSJEQvalidateRule(){ 
+	var kouLvRules = $(".listRow:visible",$("[tablename='me_lyklzgz']"));
+	var combineMessage = [];
+	for(var i=0,rule;rule=kouLvRules[i++];){
+		var sjd = $("[name$=':sjd']",rule).val(); /*时间段*/
+		var klz = $("[name$=':klz']",rule).val();/*扣率组*/
+		if(!sjd || !klz){
+			return;
+		}
+		 /*第一次设置开始金额为当前金额为第几行*/	
+		var len=-1;
+		for(var j=0,r;r=combineMessage[j++];){
+			if(r.split("-")[0] == sjd+","+klz){
+				len =Number(r.split("-")[1]);
+			}
+		}
+		if(len==-1){
+			$("[name$=':zqxsjeq']",rule).val("0");
+		}else{
+			var JJJine = $("[name$=':zqxsjez']",$(kouLvRules[len])).val(); /*上一截止金额*/
+			$("[name$='zqxsjeq']",rule).val(JJJine);
+		}
+		combineMessage.push(sjd+","+klz+"-"+(i-1));
+	}
+	
+} 
+
+
+/*=================物业费条款====================*/
+
+/*添加物业费条款事件*/
+function me_lywyftkAddRowAfterEvent(curRow){
+	/*计算序号*/
+	var preRow = $(curRow).prev();
+	if(preRow.attr("style") == 'display: none;')
+		$("[name$=':nx']",curRow).val(1);
+	else {
+		var preNx = $("[name$='nx']",preRow).val();
+		$("[name$=':nx']",curRow).val(1+Number(preNx));
+	}
+	if(validateWyfDates() == false)$(curRow).remove();
+}
+
+/*校验物业费时间**/
+function validateWyfDates(){
+	//租赁有效期起 
+	var zuLinStartDate  =$("[name='m:me_lytksbb:zlyxqq']").val();
+	var zuLinEndDate  =$("[name='m:me_lytksbb:zlyxqz']").val();
+	if(!zuLinStartDate || !zuLinStartDate){ 
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		return false;
+	}
+	//循环计算开始日期
+	var rows = $(".listRow:visible",$("[tablename='me_lywyftk']"));
+	for(var i=0,row;row=rows[i++];){
+		var shiJianDuan = $("[name$='sjd']",$(row)).val(i); //时间段
+		if(i>1){
+			//开始日期等于上个结束日期加1
+			var startDate = getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		//校验结束日期
+		var jsrq =$("[name$=':jsrq']",row).val();
+		if(!jsrq) continue;
+		var differTotalEndDate = FormDate.dateVal(jsrq, zuLinEndDate, "day");
+		if(differTotalEndDate<0){
+			$.ligerDialog.warn("结束日期不能晚于租赁有效期止!",'请核查!');
+			$("[name$='jsrq']",curRow).val("")
+		}
+	}
+}
+
+
+/*总物业费**/
+function calZongWuYeFei(obj){
+	var curRow = $(obj).closest(".listRow"); 
+	//校验是否超过总日期
+	var zuLinEndDate =$("[name='m:me_lytksbb:zlyxqz']").val();
+	var differTotalEndDate = FormDate.dateVal($("[name$=':jsrq']",curRow).val(), zuLinEndDate, "day");
+	if(differTotalEndDate<0)  {
+		$.ligerDialog.warn("当前结束日期不能晚于租赁有效期止!",'请核查');
+		$("[name$=':jsrq']",curRow).val("");
+		return ;
+	}
+	
+	var danJia = FormUtil.commaback($("[name$=':dj']",curRow).val()); // 单价
+	if(! danJia>0) return; 
+	
+	var mianJi = $("[name='m:me_lytksbb:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var baoDiType =$("[name$=':glfxx']",curRow).val();
+	var jine = mianJi * danJia;
+	$("[name$=':je']",curRow).val(jine);
+	$("[name$=':je']",curRow).trigger("blur");
+	
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	if(baoDiType =="0"){ //按月
+		var mounth = FormDate.dateVal(startDate, endDate, "day")+1;
+		var zongJine = calMountZujin(startDate,endDate,jine);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day")+1;
+		var zongJine = jine*days;
+	}
+	zongJine =FormMath.tofixed(zongJine,2);
+	$("[name$=':zje']",curRow).val(zongJine); 
+	$("[name$=':zje']",curRow).trigger("blur");
+}
+
+function calBaodiMubiao(obj,targetInput){
+	var curRow = $(obj).closest(".listRow"); 
+	var baodi = FormUtil.commaback($(obj).val());
+	if(!baodi) return;
+	var mianJi = $("[name='m:me_lytksbb:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	var ZongJine = FormMath.tofixed(calMountZujin(startDate,endDate,baodi),2);
+	$("[name$='"+targetInput+"']",curRow).val(ZongJine).trigger("change");
+}
+
+/*总租金。月度计算法*/
+function calMountZujin(startTime,endTime,zuJin){
+	startTime = startTime.replace(/\-/g, "/");
+	endTime = endTime.replace(/\-/g, "/");
+	var startDate = new Date(startTime); //开始时间
+	var endDate = new Date(endTime); //结束时间
+	
+	var num=0;
+	var year=endDate.getFullYear()-startDate.getFullYear();
+		num+=year*12;
+	var month=endDate.getMonth()-startDate.getMonth();
+		num+=month;
+		
+	var amount = zuJin * num; //月租金
+	var day=endDate.getDate()-startDate.getDate()+1;   //
+	amount = amount + day*zuJin/30  // 少于一月 减相差金额,多于一月 加多的金额
+	return amount;
+}
+

+ 709 - 0
web/js/haiya/form/me/lyzzlbg.js

@@ -0,0 +1,709 @@
+/*【租赁变更】**/
+$(function(){
+	loadChangeSffs();
+	validateZujinBiaozhunDates();// 处理样式显示
+	validateWyfDates();// 物业费样式显示
+	initXSJEQvalidateRule();
+	
+	changeSfjshyk();
+	
+	needUnbindContextMenuEvent();
+	
+});
+
+
+/* 校验子表填充数据是否重复* */ 
+function checkDataBeforeInsert(data,tableName){
+    if(tableName == 'me_lyzzlbg_hykzkft'){
+      var rows =   $(".listRow",$("[tableName='"+tableName+"']"));  // 左右子表列
+      for(var i =0,row;row=rows[i++];){
+        var id = $("[name='s:"+tableName+":ppID']",$(row)).val();  // 唯一值
+          if(id ==data.PPBM) {                            // 对话框,返回数据的那列
+    	   $.ligerDialog.warn(data.PPNAME+"已经存在了!","提示信息");
+            return false;
+         }
+      }
+    }
+    
+    if(tableName == 'me_lyzzlbg_hyklx'){
+        var rows = $(".listRow",$("[tableName='"+tableName+"']"));  // 左右子表列
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:"+tableName+":hyklxID']",$(row)).val();  // 唯一值
+            if(id == data.HYKTYPE) {                            // 对话框,返回数据的那列
+      	   $.ligerDialog.warn(data.HYKNAME+"已经存在了!","提示信息");
+              return false;
+           }
+        }
+      }
+    
+    if(tableName == 'me_lyzzlbg_pwxx'){
+        var rows =   $(".listRow",$("[tableName='me_lyzzlbg_pwxx']"));  // 左右子表列
+        // 校验该铺位是否被租出去
+        var zuLinStartDate  =$("[name='m:me_lyzzlbg:zlyxqq']").val();
+		if(!zuLinStartDate ){  $.ligerDialog.warn("尚未输入租赁开始日期!",'请核查');  return false; }
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:me_lyzzlbg_pwxx:pwID']",$(row)).val();  // 唯一值
+            if(id ==data.WLDPID) {                              // 对话框,返回数据的那列
+      	   $.ligerDialog.warn(data.WLDPDM+"已经存在了!","提示信息");
+              return false;
+           }
+        }
+      }
+     return true;
+  }
+
+/* 租赁时间变化事件* */
+function zlsjChangeEvent(){
+	validateZujinBiaozhunDates();
+	$("[name='s:me_lyzzlbg_klzgz:sjd']").trigger("change");
+	validateWyfDates();
+	validateMysfxm();
+	FormDate.doDateCalculate();
+}
+
+/* 表租金标准信息 添加行事件 */
+function me_lyzzlbg_zjbzxxAddRowAfterEvent(row){
+	// 取出最后一行序号、再次基础上加
+	var preRow = $(".listRow:visible",$("[tablename='me_lyzzlbg_zjbzxx']")).not(row).last();
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx)preNx = 0;
+	$("[name$=':xh']",row).val(1+Number(preNx));
+	$("[name$=':sjd']",row).val(1+Number(preNx));
+	validateZujinBiaozhunDates(row);
+};
+
+// 校验租金标准 时间
+function validateZujinBiaozhunDates(curRow){
+	// 租赁有效期起,止
+	var zuLinStart  =$("[name='m:me_lyzzlbg:zlyxqq']");
+	var zuLinStartDate  =zuLinStart.val();
+	var zuLinEndDate  =$("[name='m:me_lyzzlbg:zlyxqz']").val();
+	if((!zuLinStartDate || !zuLinEndDate)&& curRow){    
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		$(curRow).remove();
+		return ;
+	}
+	var rows = $(".listRow:visible",$("[tablename='me_lyzzlbg_zjbzxx']"));
+	for(var i=0,row;row=rows[i++];){
+		if(i>1){
+			// 处理比较颜色
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"dj"); // 单价
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zj"); // 租金
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zzj"); // 总租金
+			if(zuLinStart.length ==0) continue; // 如果只读状态不再判断其他
+		}
+		if(zuLinStart.length ==0) continue; 
+		
+		if(i>1){
+			// 开始日期等于上个结束日期加1
+			var startDate = HyUtil.getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			var differTotalStartDate = FormDate.compareDate($("[name$=':ksrq']",$(row)).val(), zuLinStartDate);
+			if(differTotalEndDate>=0)  {
+				$.ligerDialog.warn("当前开始日期不能早于 租赁开始日期!",'请核查');
+			}
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",$(row)).val(), zuLinEndDate);
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("【合同租金标准信息】结束日期不能晚于租赁有效期止!",'请核查');
+			$("[name$=':jsrq']",$(row)).val("");
+			return ;
+		}
+	}
+	// 修改日期后,触发时间段chang事件
+	$("[name='s:me_lyzzlbg_klzgz:sjd']").trigger("change");
+	// 触发单价
+	$("[name$=':dj']").trigger("change");
+}
+
+/* 修改租金规则 */
+function changeZujinGz(obj){
+	var curRow = $(obj).closest(".listRow");
+	var danJia = $("[name$=':dj']",curRow);
+	var zuJin = $("[name$=':zj']",curRow);
+	var rule = $(obj).val();
+	
+	if(rule == "2" || rule=="4"){ // 纯扣
+		danJia.val(0);zuJin.val(0);
+		danJia.attr("readonly","readonly");
+		zuJin.attr("readonly","readonly");
+	}else{
+		danJia.removeAttr("readonly");
+		zuJin.removeAttr("readonly"); 
+	}
+	danJia.trigger("change");
+	
+	if(rule != "1"){	// 固定金额 不加扣率组
+		createKoulvzu();
+	}
+	
+	addNeedForKouLv();// 判断是否需要增加扣率组和扣率规则必填
+}
+
+function createKoulvzu(){
+	if($(".listRow:visible",$("div[tablename='me_lyzzlbg_klz']")).length ==0){
+		FormUtil.addRow($("div[tablename='me_lyzzlbg_klz']"));  
+		var appendRow = $(".listRow:visible",$("div[tablename='me_lyzzlbg_klz']"))[0];
+		$("[name$=':klzbh']",appendRow).val(1);
+		$("[name$=':ms']",appendRow).val("扣组率1");
+	}
+}
+
+// 判断是否需要增加扣率组和扣率规则必填
+function addNeedForKouLv(){
+	var isNeed = false;
+	$("[name='s:me_lyzzlbg_zjbzxx:zjgzID']").each(function (i){
+		var val = $(this).val();
+		if(val != 1){
+			isNeed = true;
+			return false; // 终止循环
+		}
+	});
+	if(isNeed){
+		$("div[tablename='me_lyzzlbg_klz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='me_lyzzlbg_klz']")).length ==0){
+			$("div[tablename='me_lyzzlbg_klz']").addClass("validError");
+		}
+		
+		$("div[tablename='me_lyzzlbg_klzgz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='me_lyzzlbg_klzgz']")).length ==0){
+			$("div[tablename='me_lyzzlbg_klzgz']").addClass("validError");
+		}
+	}else{
+		$("div[tablename='me_lyzzlbg_klz']").attr("right","w");
+		$("div[tablename='me_lyzzlbg_klzgz']").attr("right","w");
+		$("div[tablename='me_lyzzlbg_klz']").removeClass("validError");
+		$("div[tablename='me_lyzzlbg_klzgz']").removeClass("validError");
+	}
+}
+
+// 扣率组删除事件
+function me_lyzzlbg_klzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+// 扣率组规则删除事件
+function me_lyzzlbg_klzgzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+// 扣率组添加事件
+function me_lyzzlbg_klzAddRowAfterEvent(row){
+	autoNumForKlzbm(row);
+}
+
+// 对扣率组编号自动编码
+function autoNumForKlzbm(row){
+	var preRow = $(row).prev();
+	var preBh = $("[name$=':klzbh']",preRow).val();
+	if(!preBh)preBh = 0;
+	$("[name$=':klzbh']",row).val(1+Number(preBh));
+}
+
+/**
+ * 单价计算总租金 trigger 'dj'/'zj' 触发者
+ */
+function calZongZuJin(obj){
+	var trigger = $(obj).attr("name").split(":")[2];
+	var curRow = $(obj).closest(".listRow");
+	var val = FormUtil.commaback($(obj).val()); // 单价
+	
+	var mianJi = $("[name='m:me_lyzzlbg:jzmj']").val();
+	if(!mianJi){ $.ligerDialog.warn(" 合同建筑面积不能为空!",'请核查'); return ; }
+	
+	var zuJin,danJia;
+	/* 如果是单价 */
+	if('dj'== trigger){
+		danJia = val;
+		zuJin = mianJi * danJia;
+		$("[name$=':zj']",curRow).val(FormMath.tofixed(zuJin,2));
+	}else if('zj' == trigger){
+		zuJin = val;
+		danJia =zuJin/mianJi;
+		$("[name$=':dj']",curRow).val(FormMath.tofixed(danJia,2));
+	}else return;
+	
+	var baoDiType =$("[name$=':bdxx']",curRow).val();
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	if(baoDiType =="0"){ // 按月
+		var zongZujin = HyUtil.calMoneyWithMount(startDate,endDate,zuJin);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongZujin = zuJin*days;
+	}
+	zongZujin = FormMath.tofixed(zongZujin,2);
+	$("[name$=':zzj']",curRow).val(zongZujin).trigger("change");
+ }
+
+/* 处理扣率时间段 */
+function handelKoulvTime(obj){
+	var curRow = $(obj).closest(".listRow");
+	var shijianNo = $("[name$=':sjd']",curRow).val();
+	if(!shijianNo) return ;
+	/* 取的时间段 */
+	var shiJianDuanNo =$("[name='s:me_lyzzlbg_zjbzxx:sjd'][value="+shijianNo+"]");
+	if(shiJianDuanNo.length ==0) {
+		$.ligerDialog.warn("该时间段不存在! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	var shiJianDuanRow = shiJianDuanNo.closest(".listRow");
+	var StartDate = $("[name$=':ksrq']",shiJianDuanRow).val();
+	var endDate = $("[name$=':jsrq']",shiJianDuanRow).val();
+	if(!StartDate || !endDate) {
+		$.ligerDialog.warn("改时间段信息不完善! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	
+	$("[name$=':ksrq']",curRow).val(StartDate);
+	$("[name$=':jsrq']",curRow).val(endDate);
+	
+	initXSJEQvalidateRule();
+}
+
+/* 处理扣率组 */
+function handelKoulvGroup(obj){
+	var curRow = $(obj).closest(".listRow");
+	var koulvNo = $(obj).val();
+	if(!koulvNo) return ;
+	/* 取扣率组 */
+	var KouLv =$("[name='s:me_lyzzlbg_klz:klzbh'][value="+koulvNo+"]");
+	if(KouLv.length ==0) {
+		$.ligerDialog.warn("该扣率组不存在! “"+koulvNo+"”",'请核查');
+		$(obj).val("");
+		return ;
+	}
+	initXSJEQvalidateRule();
+	
+	var klzgzMs = KouLv.closest("tr").find("[name='s:me_lyzzlbg_klz:ms']").val();		// 扣率组的【描述】
+	$(obj).closest("tr").find("[name='s:me_lyzzlbg_klzgz:ms']").val(klzgzMs);					// 当前扣率组规则【描述】
+}
+/* 循环判断扣率组,初始化销售金额起,校验扣率组金额 */
+function initXSJEQvalidateRule(){ 
+	var kouLvRules = $(".listRow:visible",$("[tablename='me_lyzzlbg_klzgz']"));
+	var combineMessage = [];
+	for(var i=0,rule;rule=kouLvRules[i++];){
+		var sjdInput=$("[name$=':sjd']",rule);
+		if(sjdInput.length==0){ /* 只读状态 */
+			var sjd = $("[name='sjd_td']",rule).text().trim(); /* 时间段 */
+			var klz = $("[name='klz_td']",rule).text().trim();/* 扣率组 */
+		}else{
+			var sjd = sjdInput.val(); /* 时间段 */
+			var klz = $("[name$=':klz']",rule).val();/* 扣率组 */
+		}
+		if(!sjd || !klz) return;
+		 /* 第一次设置开始金额为当前金额为第几行 */	
+		var len=-1;
+		for(var j=0,r;r=combineMessage[j++];){
+			if(r.split("-")[0] == sjd+","+klz){
+				len =Number(r.split("-")[1]);
+			}
+		}
+		if(len==-1){
+			$("[name$=':zqxsjeq']",rule).val("0");$("[name$=':zqxsjeq']",rule).trigger("change");
+		}else{
+			var JJJine = $("[name$=':zqxsjez']",$(kouLvRules[len])).val(); /* 上一截止金额 */
+			$("[name$=':zqxsjeq']",rule).val(JJJine);
+			HyUtil.fillColorByCompare(rule,kouLvRules[len],"kl");
+		}
+		combineMessage.push(sjd+","+klz+"-"+(i-1));
+	}
+} 
+/* 校验周期销售金额止 */
+function checkZqxsjez(obj){
+	var curRow = $(obj).closest(".listRow");
+	 var JeQi = $("[name$=':zqxsjeq']",curRow).val();
+	 var jeZhi =$(obj).val();
+	 if(JeQi&&jeZhi){
+		 if( FormUtil.commaback(JeQi) > FormUtil.commaback(jeZhi)){
+			 $.ligerDialog.warn("周期销售金额止:"+jeZhi+  " 不应该小于 周期销售金额起 :"+JeQi+"",'请核查!');
+			 $(obj).val("");
+			 return ;
+		 }
+	 }
+	 initXSJEQvalidateRule();
+}
+/* 【物业费条款添加事件】me_lyzzlbg_wyftk */
+function me_lyzzlbg_wyftkAddRowAfterEvent(curRow){
+
+	var preRow = $(".listRow:visible",$("[tablename='me_lyzzlbg_wyftk']")).not(curRow).last();
+
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx)preNx = 0;
+	
+	$("[name$=':nx']",curRow).val(1+Number(preNx));
+	$("[name$=':xh']",curRow).val(1+Number(preNx));
+	
+	validateWyfDates(curRow);
+	
+}
+
+/* 校验物业费时间* */
+function validateWyfDates(curRow){
+	// 租赁有效期起
+	var zuLinStart = $("[name='m:me_lyzzlbg:zlyxqq']");
+	var zuLinStartDate  = zuLinStart.val();
+	var zuLinEndDate  =$("[name='m:me_lyzzlbg:zlyxqz']").val();
+	if((!zuLinStartDate || !zuLinEndDate) && curRow){ 
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		$(curRow).remove();
+		return ;
+	}
+	// 循环计算开始日期
+	var rows = $(".listRow:visible",$("[tablename='me_lyzzlbg_wyftk']"));
+	for(var i=0,row;row=rows[i++];){
+		if(i>1){ // 填充字段颜色
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"xs");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"dj");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"je");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zje");
+		}
+		if(zuLinStart.length==0) continue;
+		
+		var shiJianDuan = $("[name$=':sjd']",$(row)).val(i); // 时间段
+		if(i>1){
+			// 开始日期等于上个结束日期加1
+			var startDate = HyUtil.getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		// 校验结束日期
+		var jsrq =$("[name$=':jsrq']",$(row)).val();
+		if(!jsrq) continue;
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate, "day");
+		if(differTotalEndDate<0){
+			$.ligerDialog.warn("【物业费条款】结束日期不能晚于租赁有效期止!",'请核查!');
+			$("[name$=':jsrq']",$(row)).val("");
+		}
+	}
+}
+
+/* 总物业费* */
+function calZongWuYeFei(obj){
+	var curRow = $(obj).closest(".listRow"); 
+	// 校验是否超过总日期
+	var zuLinEndDate =$("[name='m:me_lyzzlbg:zlyxqz']").val();
+	var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",curRow).val(), zuLinEndDate);
+	if(differTotalEndDate<0)  {
+		$.ligerDialog.warn("当前结束日期不能晚于租赁有效期止!",'请核查');
+		$("[name$=':jsrq']",curRow).val("");
+		return ;
+	}
+	
+	var danJia = FormUtil.commaback($("[name$=':dj']",curRow).val()); // 单价
+	
+	var mianJi = $("[name='m:me_lyzzlbg:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var baoDiType =$("[name$=':glfxx']",curRow).val();
+	var xs = $("[name$=':xs']",curRow).val();
+	var jine = FormMath.tofixed(mianJi * danJia * xs,2);
+
+	$("[name$=':je']",curRow).val(jine); $("[name$=':je']",curRow).trigger("change");
+	
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	if(baoDiType =="0"){ // 按月
+		var zongJine = HyUtil.calMoneyWithMount(startDate,endDate,jine);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongJine = jine*days;
+	}
+	zongJine =FormMath.tofixed(zongJine,2);
+	$("[name$=':zje']",curRow).val(zongJine);$("[name$=':zje']",curRow).trigger("change");
+ }
+
+/* 加载时,改变【每月收费项目】的【收费方式】 */
+function loadChangeSffs(){
+	var sffs = $("[name$=':sffs']:visible",$("[tablename='me_lyzzlbg_mysfxm']"));
+	sffs.each(function (i) {
+		changeSffs(this);
+	});
+}
+
+/* 改变【每月收费项目】的【收费方式】 */
+function changeSffs(obj){
+	var $tr = $(obj).closest("tr");
+	var sffs = $("[name$=':sffs']",$tr).val(); /* 收费方式 */
+	$("[name$=':dj']",$tr).off();
+	 $("[name='shouFeiGuiZe']",$tr).show();
+	switch(sffs){
+		case "0":		/* 固定金额类型 : 只能录入收费金额 */
+		case "4":		
+			 $("[name$=':dj']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':sfje']",$tr).removeAttr("readonly");
+			 $("[name$=':kl']",$tr).attr("readonly","readonly").val("");
+		  break;
+		case "1":		/* 比率类型:只能录入比率 */
+		case "2":
+		case "5":
+			 $("[name$=':sfgz']",$tr).val("");
+			 $("[name$=':sfgzID']",$tr).val("");
+			 $("[name='shouFeiGuiZe']",$tr).hide().parent().css("width","170px");
+			$("[name$=':dj']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':kl']",$tr).removeAttr("readonly");
+		  break;
+		case "3":		/* 每平方米单价类型:只能录入单价 */
+			 $("[name$=':dj']",$tr).removeAttr("readonly");
+			$("[name$=':sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':kl']",$tr).attr("readonly","readonly").val("");
+			// 计算价格
+			 $("[name$=':dj']",$tr).on("blur",function(){
+				 var dj =  FormUtil.commaback($(this).val()); 
+				 var curRow = $(this).closest(".listRow"); 
+				 var mianJi = $("[name='m:me_lyzzlbg:jzmj']").val();
+				 if(!mianJi) $.ligerDialog.warn("尚未生成面积");
+				 $("[name$=':sfje']",curRow).val(FormMath.tofixed(dj*mianJi,2));
+			 });
+		  break;
+		default:
+	}
+}
+/* 校验每月收费项目 */
+function validateMysfxm (){
+	var zuLinStartDate  =$("[name='m:me_lyzzlbg:zlyxqq']").val();
+	var zuLinEndDate  =$("[name='m:me_lyzzlbg:zlyxqz']").val();
+	
+	var sfxm = $(".listRow:visible",$("[tablename='me_lyzzlbg_mysfxm']"));
+	var sfxmArray =[];
+	
+	for(var i=0,row;row=sfxm[i++];){
+		var ksrq = $("[name$=':ksrq']",$(row)).val();
+		var jsrq = $("[name$=':jsrq']",$(row)).val();
+		if(!ksrq) continue;
+		var differTotalStartDate = FormDate.compareDate(ksrq, zuLinStartDate);
+		if(differTotalStartDate>0){
+			$.ligerDialog.warn("每月收费项目开始日期不能早于 租赁开始日期!",'请核查');
+			$("[name$=':ksrq']",$(row)).val("")
+		}
+		
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate);
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("每月收费项目结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$=':jsrq']",$(row)).val("");
+		}
+		var curSfxmID =$("[name$=':sfxmID']",$(row)).val();
+		if(curSfxmID){
+			for(var j=0,prevSfxm;prevSfxm=sfxmArray[j++];){
+				var thisSfxmId = prevSfxm.split("$")[0];
+				/* 当前收费项目第二次出现 */
+				if(thisSfxmId == curSfxmID){
+					var differToPrevDate = FormDate.compareDate(ksrq,prevSfxm.split("$")[1]);
+					if(differToPrevDate>=0){
+						$.ligerDialog.warn("同一收费项目日期不得重复!",'请核查');
+						$("[name$=':ksrq']",$(row)).val("");
+					}
+				}
+			  }
+			
+			sfxmArray.push(curSfxmID+"$"+jsrq);
+			}
+		}
+}
+
+/* 结算标准信息 删除行事件 */
+function me_lyzzlbg_zjbzxxDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:me_lyzzlbg_ydzjfj:xh'][value=" + xh + "]").closest("tr").remove();/* 清除旧的记录 */
+}
+function me_lyzzlbg_wyftkDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:me_lyzzlbg_wyffj:xh'][value=" + xh + "]").closest("tr").remove();/* 清除旧的记录 */
+}
+/**
+ * 修改收费规则的时候删除所有分解 wyffj /ydzjfj
+ */ 
+function delAllFenJie(target){
+	$("[name='s:me_lyzzlbg_"+target+":xh'][value!='']").closest("tr").remove();
+}
+
+/* 分解所有结算标准信息 */
+function decomposeAllJs(tableName,fenjieTable){
+	var $jsbzxxTrs = $(".listRow:visible",$("[tablename='"+tableName+"']"));   
+	$jsbzxxTrs.each(function (i) {
+		decomposeSingle($(this),fenjieTable);
+	});
+}
+function decomposeAllWyf(){
+	decomposeAllJs('me_lyzzlbg_wyftk','me_lyzzlbg_wyffj');
+}
+function decomposeSingleWyf(){
+	decomposeSingleJs('me_lyzzlbg_wyftk','me_lyzzlbg_wyffj');
+}
+function decomposeAllYzj(){
+	decomposeAllJs('me_lyzzlbg_zjbzxx','me_lyzzlbg_ydzjfj');
+}
+function decomposeSingleYzj(){
+	decomposeSingleJs('me_lyzzlbg_zjbzxx','me_lyzzlbg_ydzjfj');
+}
+
+/* 单个分解 */
+function decomposeSingleJs(tableName,fenjieTable){
+	$jsbzxxTrs = $("input[name$=':fjsj']:checked",$("div[tablename='"+tableName+"']")).closest("tr");
+	if($jsbzxxTrs.length ==0){
+		$.ligerDialog.warn("请选择要分解的数据!","提示信息");
+		return;
+	}
+	
+	// 分解
+	decomposeSingle($jsbzxxTrs,fenjieTable);
+} 
+
+/*
+ * 【通过分解目标行来分解信息,将结果输出至分解表】 被选中的行:selectRow,分解表的表明fenJieTable
+ */
+function decomposeSingle(selectRow,fenJieTable){
+	var xh = selectRow.find("[name$=':xh']").val();	
+	if(!xh){
+		$.ligerDialog.warn("请选择要分解的时间段","提示");
+		return ;
+	}
+	// 分解表
+	var fenJieTableDiv = $("div[tablename='"+fenJieTable+"']");
+	/* 将序号与当前选中行所有分解信息删除 */
+	$("[name$=':xh'][value=" + xh + "]",fenJieTableDiv).closest("tr").remove(); /* 清除旧的记录 */
+	
+	var sjd =$("[name$=':xh']",selectRow).val(); // 序号
+	var ksrq =$("[name$=':ksrq']",selectRow).val();
+	var jsrq =$("[name$=':jsrq']",selectRow).val();
+	
+	// 物业费
+	if(fenJieTable == 'me_lyzzlbg_wyffj'){
+		var zj =FormUtil.commaback($("[name$=':je']",selectRow).val());
+		var sfgz =  $("[name='m:me_lyzzlbg:wyfsfgzID']").val();
+		var type = $("[name$=':glfxx']",selectRow).val();
+	// 标准租金
+	}else{
+		var type = $("[name$=':bdxx']",selectRow).val();
+		var zj =FormUtil.commaback($("[name$=':zj']",selectRow).val()); 
+		var sfgz =$("[name='m:me_lyzzlbg:zjsfgzID']").val();
+		
+	}
+	var jsonData =  HyUtil.decompose(sjd,ksrq,jsrq,zj,sfgz,"",type);
+	
+	/* 填充数据 */
+	for (var i = 0, c; c = jsonData[i++];) {
+		FormUtil.addRow(fenJieTableDiv);
+		var rowcount=$("input[name$=':ny']",fenJieTableDiv).length;
+		$($("input[name$=':xh']",fenJieTableDiv).get(rowcount-1)).val(xh);		
+		$($("input[name$=':ny']",fenJieTableDiv).get(rowcount-1)).val(c.ZQY);				
+		$($("input[name$=':ksrq']",fenJieTableDiv).get(rowcount-1)).val(c.KSRQ);			
+		$($("input[name$=':jsrq']",fenJieTableDiv).get(rowcount-1)).val(c.JSRQ);			
+		$($("input[name$=':je']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);	
+		$($("input[name$=':scrq']",fenJieTableDiv).get(rowcount-1)).val(c.CDRQ);	
+	}
+}
+
+/* 显示分解信息列表 */
+function showFjsj(obj,fenJieTable){
+	var fenJieTableDiv =$("div[tablename='"+fenJieTable+"']");
+	var tableDiv = $(obj).closest("[tablename]");
+	var selectRow =$("input[name=':fjsj']:checked",tableDiv).closest("tr");
+	var xhVal = selectRow.find("[name$=':xh']").val();/* 选择行 */
+	
+	HyUtil.showDecompose(fenJieTable,'xh',xhVal);
+}
+
+/** 分解代码 end* */
+
+/* 格式化填充的日期 */
+$().ready(function (){
+	
+	$("[issameas]").change(function(){
+		var me = $(this),curVal = me.val();
+		var curNameStr = me.attr("name");
+		if(!curNameStr) return; 
+		var curName = curNameStr.split(":");
+		var targetName = me.attr("isSameAs");
+		if(targetName=='true' && curName.length == 3){
+			targetName =curName[0] +":"+curName[1] +":y"+curName[2]
+		}
+		if(!window.isSameToCheck_)window.isSameToCheck_ = [];
+		window.isSameToCheck_.push(curNameStr+";"+targetName);
+		
+		window.setTimeout(function(){checkIsSame()},50); 
+	});
+	
+	function checkIsSame(){
+		if(!window.isSameToCheck_) return ;
+		for(var checkStr;checkStr=window.isSameToCheck_.pop();){
+			var targetValue = $("[name='"+checkStr.split(";")[1]+"']").val();
+			var curInput = $("[name='"+checkStr.split(";")[0]+"']");
+			 
+			if(targetValue != curInput.val()){
+				curInput.css("color","red");
+			}else{
+				curInput.css("color","");
+			}
+		}
+	}
+	
+	$("div[tablename='me_lyzzlbg_ybdxx']").hide();	// 原保底信息   
+	$("div[tablename='me_lyzzlbg_ydzjfj']").hide();	// 月度租金分解
+	$("div[tablename='me_lyzzlbg_ywyffj']").hide();	// 原物业费分解
+	$("div[tablename='me_lyzzlbg_wyffj']").hide();	// 物业费分解
+});
+
+function CustomQueryAftEvent__(){
+	$("div[tablename='me_lyzzlbg_ybdxx']").hide();	// 原保底信息   
+	$("div[tablename='me_lyzzlbg_ydzjfj']").hide();	// 月度租金分解
+	$("div[tablename='me_lyzzlbg_ywyffj']").hide();	// 原物业费分解
+	$("div[tablename='me_lyzzlbg_wyffj']").hide();	// 物业费分解
+	validateZujinBiaozhunDates();// 处理样式显示
+	validateWyfDates();// 物业费样式显示
+	initXSJEQvalidateRule();
+	
+	needUnbindContextMenuEvent();
+	
+	HyUtil.unbindContextMenuEvent("me_lyzzlbg_yjsbzxx");	//	原结算标准信息
+	HyUtil.unbindContextMenuEvent("me_lyzzlbg_yklz");		//	原扣率组
+	HyUtil.unbindContextMenuEvent("me_lyzzlbg_yklzgz");		//	原扣率规则
+	HyUtil.unbindContextMenuEvent("me_lyzzlbg_ywyftk");		//	原物业费条款
+	HyUtil.unbindContextMenuEvent("me_lyzzlbg_ybzj");		//	原保证金
+	HyUtil.unbindContextMenuEvent("me_lyzzlbg_ymysfxm");	//	原每月收费项目
+	HyUtil.unbindContextMenuEvent("me_lyzzlbg_ysffsxm");	//	原收款方式项目
+	HyUtil.unbindContextMenuEvent("me_lyzzlbg_yhykzkft");	//	原折扣分摊标准
+	HyUtil.unbindContextMenuEvent("me_lyzzlbg_yhyklx");		//	原会员卡类型
+	
+}
+
+// 需要特殊处理右键事件
+function needUnbindContextMenuEvent(){
+	var pwxxRows = $(".listRow:visible",$("[tablename='me_lyzzlbg_pwxx']"));
+	pwxxRows.each(function (i){
+		var sfypw = $(this).find("[name='s:me_lyzzlbg_pwxx:sfypw']").val();
+		if(sfypw == "true"){
+			$(this).unbind("contextmenu");
+			$(this).find(".extend").remove();
+			$(this).find("[name='s:me_lyzzlbg_pwxx:zywz']").attr("readonly","readonly");
+		}
+	});
+}
+
+/*改变【是否接受会员卡】*/
+function changeSfjshyk(){
+	var sfjshyk = $("[name='m:me_lyzzlbg:sfjshyk']");
+	if(sfjshyk.length ==0){
+		sfjshyk = $.trim($("#sfjshyk").text());
+	}else{
+		sfjshyk = sfjshyk.val();
+	} 
+	if(sfjshyk=='是'){
+		$(".sfjshyk_flag").show();
+	} else{ 
+		$(".sfjshyk_flag").hide();
+	}
+}
+
+
+
+
+

+ 768 - 0
web/js/haiya/form/me/zlhtqd.js

@@ -0,0 +1,768 @@
+/*【租赁合同签订】**/
+$(function(){
+	changeTYShouYin();
+	changeSblx();
+	changeSftstk();
+	changeSfjshyk();
+	changeZllx();
+	
+	validateZujinBiaozhunDates();// 样式
+	validateWyfDates();// 样式
+	
+	$("div[tablename='me_zlhtqd_ydzjfj']").hide();	/* 【月度租金分解】不能直接控制隐藏,否则权限控制不了,先隐藏全部,在显示个体 */
+	$("div[tablename='me_zlhtqd_wyffj']").hide();	/* 【物业费分解】不能直接控制隐藏,否则权限控制不了,先隐藏全部,在显示个体 */
+});
+/* 显示租户信息 */
+function showZh(){
+	var zhid = $("[name='m:me_zlhtqd:zhID']").val();
+	HyUtil.showZh(zhid);
+}
+
+/* 显示品牌信息 */
+function showPP(){
+	var ppId = $("[name='m:me_zlhtqd:jyppID']").val();
+	HyUtil.showPP(ppId);
+}
+
+/* 校验子表填充数据是否重复* */ 
+function checkDataBeforeInsert(data,tableName){
+    if(tableName == 'me_zlhtqd_hykzkft'){
+      var rows =   $(".listRow",$("[tableName='"+tableName+"']"));  // 左右子表列
+      for(var i =0,row;row=rows[i++];){
+        var id = $("[name='s:"+tableName+":ppID']",$(row)).val();  // 唯一值
+          if(id ==data.PPBM) {                            // 对话框,返回数据的那列
+    	   $.ligerDialog.warn(data.PPNAME+"已经存在了!","提示信息");
+            return false;
+         }
+      }
+    }
+    
+    if(tableName == 'me_zlhtqd_hyklx'){
+        var rows = $(".listRow",$("[tableName='"+tableName+"']"));  // 左右子表列
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:"+tableName+":hyklxID']",$(row)).val();  // 唯一值
+            if(id == data.HYKTYPE) {                            // 对话框,返回数据的那列
+      	   $.ligerDialog.warn(data.HYKNAME+"已经存在了!","提示信息");
+              return false;
+           }
+        }
+    }
+    
+    if(tableName == 'me_zlhtqd_pwxx'){
+        var rows =   $(".listRow",$("[tableName='me_zlhtqd_pwxx']"));  // 左右子表列
+        // 校验该铺位是否被租出去
+        var zuLinStartDate  =$("[name='m:me_zlhtqd:zlyxqq']").val();
+		if(!zuLinStartDate ){  $.ligerDialog.warn("尚未输入租赁开始日期!",'请核查');  return false }
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:me_zlhtqd_pwxx:pwID']",$(row)).val();  // 唯一值
+            if(id ==data.WLDPID) {                              // 对话框,返回数据的那列
+      	   $.ligerDialog.warn(data.WLDPDM+"已经存在了!","提示信息");
+              return false;
+           }
+        }
+        $("[name='m:me_zlhtqd:splx']").val(data.DYLX);
+      }
+    
+     return true;
+  }
+
+/* 校验铺位是否到期 */
+function validatePuweiIsUsed(){
+	 var zuLinStartDate = $.trim($("#zlyxqq").text());
+	 var errMsg = "";
+	 var warnMsg = "";
+	 $("[name='s:me_zlhtqd_pwxx:pwID']").each(function(){	
+		var pwID = $(this).val();
+		var conf ={aliasName:'validatePuweiIsUsed',pwid:pwID,sDate:zuLinStartDate};
+		var json = RunAliasScript(conf);
+		if(json.isSuccess ==1){
+			errMsg = json.msg;
+			return false;	// 跳出循环
+		}else if(json.result){
+			var pwh = $.trim($(this).closest("tr").find("#pwh").text());
+			warnMsg = warnMsg + "铺位【"+pwh+"】到期日期为:"+json.result+",请核查!<br/>";
+		}
+	});
+	
+	if(errMsg != ""){
+		 $.ligerDialog.error(errMsg,"错误信息");
+		 return false;
+	}
+	if(warnMsg != ""){
+		 $.ligerDialog.warn(warnMsg,"提示信息");
+		 return false;
+	}
+	return true;
+}
+
+
+/* 结算标准信息 删除行事件 */
+function me_zlhtqd_zjbzxxDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:me_zlhtqd_ydzjfj:xh'][value=" + xh + "]").closest("tr").remove();/* 清除旧的记录 */
+}
+function me_zlhtqd_wyftkDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:me_zlhtqd_wyffj:xh'][value=" + xh + "]").closest("tr").remove();/* 清除旧的记录 */
+}
+
+// 计算所有铺位,与面积
+function calAllPuwei(obj){
+	var pws = $("[tablename='me_zlhtqd_pwxx']").find("[name$=':pwh']").not("[value='']");
+	if(pws.size() == 0)
+		pws = $("[tablename='me_zlhtqd_pwxx']").find("[name$=':pwh']");
+	var shangPuNums = "";
+	pws.each(function (i) {
+	  var pwId = $(this).val();
+	  shangPuNums = shangPuNums + pwId;
+	  if(i != pws.length-1){
+		  if(shangPuNums){
+			  shangPuNums = shangPuNums+"-";
+		  }
+	  }
+	});
+	$("[name='m:me_zlhtqd:sp']").val(shangPuNums); 
+};
+
+function me_zlhtqd_zjbzxxAddRowAfterEvent(row){
+	var preRow = $(".listRow:visible",$("[tablename='me_zlhtqd_zjbzxx']")).not(row).last();
+
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx)preNx = 0;
+	
+	$("[name$=':xh']",row).val(1+Number(preNx));
+	$("[name$=':sjd']",row).val(1+Number(preNx));
+	validateZujinBiaozhunDates(row);
+};
+// 校验租金标准 时间
+function validateZujinBiaozhunDates(curRow){
+	// 租赁有效期起,止
+	var zuLinStart =$("[name='m:me_zlhtqd:zlyxqq']");
+	var zuLinStartDate  =zuLinStart.val();
+	var zuLinEndDate  =$("[name='m:me_zlhtqd:zlyxqz']").val();
+	if((!zuLinEndDate || !zuLinStartDate)&&curRow){    
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		$(curRow).remove();
+		return;
+	}
+	
+	var rows = $(".listRow:visible",$("[tablename='me_zlhtqd_zjbzxx']"));
+	for(var i=0,row;row=rows[i++];){
+		if(i>1){
+			// 处理比较颜色
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"dj"); // 单价
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zj"); // 租金
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zzj"); // 总租金
+		}
+		if(zuLinStart.length ==0) continue; // 如果只读状态不再判断其他
+		
+		if(i>1){
+			// 开始日期等于上个结束日期加1
+			var startDate = HyUtil.getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			var differTotalStartDate = FormDate.compareDate($("[name$=':ksrq']",$(row)).val(), zuLinStartDate);
+			if(differTotalEndDate>=0)  {
+				$.ligerDialog.warn("当前开始日期不能早于 租赁开始日期!",'请核查');
+			}
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",$(row)).val(), zuLinEndDate);
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("当前结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$=':jsrq']",$(row)).val("");
+			return ;
+		}
+	}
+	// ai 修改日期后,触发时间段chang事件
+	$("[name='s:me_zlhtqd_klzgz:sjd']").trigger("change");
+	// 触发单价
+	//$("[name$=':dj']").trigger("change");
+}
+
+/* 修改租金规则 */
+function changeZujinGz(obj){
+	var curRow = $(obj).closest(".listRow");
+	var danJia = $("[name$=':dj']",curRow);
+	var zuJin = $("[name$=':zj']",curRow);
+	var rule = $(obj).val();
+	
+	if(rule == "2" || rule=="4"){ // 纯扣
+		danJia.val(0);zuJin.val(0);
+		danJia.attr("readonly","readonly");
+		zuJin.attr("readonly","readonly");
+	}else{
+		danJia.removeAttr("readonly");
+		zuJin.removeAttr("readonly"); 
+	}
+	danJia.trigger("change");
+	
+	if(rule != "1"){	//	固定金额 不加扣率组
+		createKoulvzu();
+	}
+	addNeedForKouLv();//	判断是否需要增加扣率组和扣率规则必填
+}
+
+function createKoulvzu(){
+	if($(".listRow:visible",$("div[tablename='me_zlhtqd_klz']")).length ==0){
+		FormUtil.addRow($("div[tablename='me_zlhtqd_klz']"));  
+		var appendRow = $(".listRow:visible",$("div[tablename='me_zlhtqd_klz']"))[0];
+		$("[name$=':klzbh']",appendRow).val(1);
+		$("[name$=':jckl']",appendRow).val("0");
+		$("[name$=':ms']",appendRow).val("扣组率1");
+	}
+}
+
+// 判断是否需要增加扣率组和扣率规则必填
+function addNeedForKouLv(){
+	var isNeed = false;
+	$("[name='s:me_zlhtqd_zjbzxx:zjgzID']").each(function (i){
+		var val = $(this).val();
+		if(val != 1){
+			isNeed = true;
+			return false; // 终止循环
+		}
+	});
+	if(isNeed){
+		$("div[tablename='me_zlhtqd_klz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='me_zlhtqd_klz']")).length ==0){
+			$("div[tablename='me_zlhtqd_klz']").addClass("validError");
+		}
+		
+		$("div[tablename='me_zlhtqd_klzgz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='me_zlhtqd_klzgz']")).length ==0){
+			$("div[tablename='me_zlhtqd_klzgz']").addClass("validError");
+		}
+	}else{
+		$("div[tablename='me_zlhtqd_klz']").attr("right","w");
+		$("div[tablename='me_zlhtqd_klzgz']").attr("right","w");
+		$("div[tablename='me_zlhtqd_klz']").removeClass("validError");
+		$("div[tablename='me_zlhtqd_klzgz']").removeClass("validError");
+	}
+}
+
+// 扣率组删除事件
+function me_zlhtqd_klzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+//扣率组规则删除事件
+function me_zlhtqd_klzgzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+//扣率组添加事件
+function me_zlhtqd_klzAddRowAfterEvent(row){
+	autoNumForKlzbm(row);
+}
+
+// 对扣率组编号自动编码
+function autoNumForKlzbm(row){
+	var preRow = $(row).prev();
+	var preBh = $("[name$=':klzbh']",preRow).val();
+	if(!preBh)preBh = 0;
+	$("[name$=':klzbh']",row).val(1+Number(preBh));
+}
+
+/**
+ * 单价计算总租金 trigger 'dj'/'zj' 触发者
+ */
+function calZongZuJin(obj){
+	var trigger = $(obj).attr("name").split(":")[2];
+	var curRow = $(obj).closest(".listRow");
+	var val = FormUtil.commaback($(obj).val()); 
+	
+	var mianJi = $("[name='m:me_zlhtqd:jzmj']").val();
+	if(!mianJi){ $.ligerDialog.warn(" 合同建筑面积不能为空!",'请核查'); return ; }
+	
+	var zuJin,danJia;
+	/* 如果是单价 */
+	if('dj'== trigger){
+		danJia = val;
+		zuJin = mianJi * danJia;
+		$("[name$=':zj']",curRow).val(FormMath.tofixed(zuJin,2));
+	}else if('zj' == trigger){
+		zuJin = val;
+		danJia =zuJin/mianJi;
+		$("[name$=':dj']",curRow).val(FormMath.tofixed(danJia,2));
+	}
+	
+	var baoDiType =$("[name$=':bdxx']",curRow).val();
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	if(baoDiType =="0"){ // 按月
+		var zongZujin = HyUtil.calMoneyWithMount(startDate,endDate,zuJin);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongZujin = zuJin*days;
+	}
+	zongZujin = FormMath.tofixed(zongZujin,2);
+	$("[name$=':zzj']",curRow).val(zongZujin).trigger("change");
+ }
+
+/* 处理扣率时间段 */
+function handelKoulvTime(obj){
+	var curRow = $(obj).closest(".listRow");
+	var shijianNo = $("[name$=':sjd']",curRow).val();
+	if(!shijianNo) return ;
+	/* 取的时间段 */
+	var shiJianDuanNo =$("[name='s:me_zlhtqd_zjbzxx:sjd'][value="+shijianNo+"]");
+	if(shiJianDuanNo.length ==0) {
+		$.ligerDialog.warn("该时间段不存在! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	var shiJianDuanRow = shiJianDuanNo.closest(".listRow");
+	var StartDate = $("[name$=':ksrq']",shiJianDuanRow).val();
+	var endDate = $("[name$=':jsrq']",shiJianDuanRow).val();
+	if(!StartDate || !endDate) {
+		$.ligerDialog.warn("改时间段信息不完善! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	
+	$("[name$=':ksrq']",curRow).val(StartDate);
+	$("[name$=':jsrq']",curRow).val(endDate);
+	
+	initXSJEQvalidateRule();
+}
+
+/* 处理扣率组 */
+function handelKoulvGroup(obj){
+	var koulvNo = $(obj).val();
+	if(!koulvNo) return ;
+	/* 取扣率组 */
+	var KouLv =$("[name='s:me_zlhtqd_klz:klzbh'][value="+koulvNo+"]");
+	if(KouLv.length ==0) {
+		$.ligerDialog.warn("该扣率组不存在! “"+koulvNo+"”",'请核查');
+		$(obj).val("");
+		return ;
+	}
+	initXSJEQvalidateRule();
+	
+	var klzgzMs = KouLv.closest("tr").find("[name='s:me_zlhtqd_klz:ms']").val();		//	扣率组的【描述】
+	$(obj).closest("tr").find("[name='s:me_zlhtqd_klzgz:ms']").val(klzgzMs);					// 当前扣率组规则【描述】
+}
+
+/* 循环判断扣率组,初始化销售金额起,校验扣率组金额 */
+function initXSJEQvalidateRule(){ 
+	var kouLvRules = $(".listRow:visible",$("[tablename='me_zlhtqd_klzgz']"));
+	var combineMessage = [];
+	for(var i=0,rule;rule=kouLvRules[i++];){
+		var sjdInput=$("[name$=':sjd']",rule);
+		if(sjdInput.length==0){ /* 只读状态 */
+			var sjd = $.trim($("[name=':sjd_td']",rule).text()); /* 时间段 */
+			var klz = $.trim($("[name=':klz_td']",rule).text());/* 扣率组 */
+		}else{
+			var sjd = sjdInput.val(); /* 时间段 */
+			var klz = $("[name$=':klz']",rule).val();/* 扣率组 */
+		}
+		if(!sjd || !klz) return;
+		 /* 第一次设置开始金额为当前金额为第几行 */	
+		var len=-1;
+		for(var j=0,r;r=combineMessage[j++];){
+			if(r.split("-")[0] == sjd+","+klz){
+				len =Number(r.split("-")[1]);
+			}
+		}
+		if(len==-1){
+			$("[name$=':zqxsjeq']",rule).val("0");$("[name$=':zqxsjeq']",rule).trigger("change");
+		}else{
+			var JJJine = $("[name$=':zqxsjez']",$(kouLvRules[len])).val(); /* 上一截止金额 */
+			$("[name$=':zqxsjeq']",rule).val(JJJine);
+			HyUtil.fillColorByCompare(rule,kouLvRules[len],"kl");
+		}
+		combineMessage.push(sjd+","+klz+"-"+(i-1));
+	}
+} 
+/* 校验周期销售金额止 */
+function checkZqxsjez(obj){
+	var curRow = $(obj).closest(".listRow");
+	 var JeQi = $("[name$=':zqxsjeq']",curRow).val();
+	 var jeZhi =$(obj).val();
+	 if(JeQi&&jeZhi){
+		 if( FormUtil.commaback(JeQi) > FormUtil.commaback(jeZhi)){
+			 $.ligerDialog.warn("周期销售金额止:"+jeZhi+  " 不应该小于 周期销售金额起 :"+JeQi+"",'请核查!');
+			 $(obj).val("");
+			 return ;
+		 }
+	 }
+	 initXSJEQvalidateRule();
+}
+/* 【物业费条款添加事件】me_zlhtqd_wyftk */
+function me_zlhtqd_wyftkAddRowAfterEvent(curRow){
+	var preRow = $(".listRow:visible",$("[tablename='me_zlhtqd_wyftk']")).not(curRow).last();
+
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx)preNx = 0;
+	
+	$("[name$=':nx']",curRow).val(1+Number(preNx));
+	$("[name$=':xh']",curRow).val(1+Number(preNx));
+	
+	validateWyfDates(curRow);
+	
+	// 设置公司标准
+	setGsbz(curRow);
+	
+}
+
+/*设置公司标准*/
+function setGsbz(curRow){
+    var fdbh = $("[name='m:me_zlhtqd:fdID']").val();
+    var conf = {
+        aliasName:'mdwyfbz',      //脚本的别名(唯一的)
+        arg0:fdbh
+    };
+    //执行别名脚本调用方法
+    var json = RunAliasScript(conf); //结果是返回JSON数据
+    if (json.isSuccess == 0) {
+        $("[name='s:me_zlhtqd_wyftk:gsbz']",curRow).val(json.result[0].BZJE);
+    } else {
+        alert(json.msg);
+    }
+}
+
+/* 校验物业费时间* */
+function validateWyfDates(row){
+	// 租赁有效期起
+	var zuLinStart  =$("[name='m:me_zlhtqd:zlyxqq']");
+	var zuLinStartDate  = zuLinStart.val();
+	var zuLinEndDate  =$("[name='m:me_zlhtqd:zlyxqz']").val();
+	if((!zuLinStartDate || !zuLinStartDate) && row){ 
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		$(row).remove();
+		return ;
+	}
+	// 循环计算开始日期
+	var rows = $(".listRow:visible",$("[tablename='me_zlhtqd_wyftk']"));
+	for(var i=0,row;row=rows[i++];){
+		if(i>1){ // 填充字段颜色
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"xs");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"dj");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"je");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zje");
+		}
+		if(zuLinStart.length==0) continue;
+		
+		var shiJianDuan = $("[name$=':sjd']",$(row)).val(i); // 时间段
+		if(i>1){
+			// 开始日期等于上个结束日期加1
+			var startDate = HyUtil.getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		// 校验结束日期
+		var jsrq =$("[name$=':jsrq']",row).val();
+		if(!jsrq) continue;
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate);
+		if(differTotalEndDate<0){
+			$.ligerDialog.warn("结束日期不能晚于租赁有效期止!",'请核查!');
+			$("[name$=':jsrq']",curRow).val("");
+		}
+	}
+}
+
+/* 总物业费* */
+function calZongWuYeFei(obj){
+	var curRow = $(obj).closest(".listRow"); 
+	// 校验是否超过总日期
+	var zuLinEndDate =$("[name='m:me_zlhtqd:zlyxqz']").val();
+	var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",curRow).val(), zuLinEndDate);
+	if(differTotalEndDate<0)  {
+		$.ligerDialog.warn("当前结束日期不能晚于租赁有效期止!",'请核查');
+		$("[name$=':jsrq']",curRow).val("")
+		return ;
+	}
+	
+	var danJia = FormUtil.commaback($("[name$=':dj']",curRow).val()); // 单价
+	
+	var mianJi = $("[name='m:me_zlhtqd:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var baoDiType =$("[name$=':glfxx']",curRow).val();
+	
+	var xs = $("[name$=':xs']",curRow).val();
+	var jine = FormMath.tofixed(mianJi * danJia * xs,2);
+
+	$("[name$=':je']",curRow).val(jine); $("[name$=':je']",curRow).trigger("change");
+	
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	if(baoDiType =="0"){ // 按月
+		var zongJine = HyUtil.calMoneyWithMount(startDate,endDate,jine);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongJine = jine*days;
+	}
+	zongJine =FormMath.tofixed(zongJine,2);
+	$("[name$=':zje']",curRow).val(zongJine);$("[name$=':zje']",curRow).trigger("change");
+ }
+
+/* 切换收银方式 */
+function changeTYShouYin(){
+	var type = $("select[name='m:me_zlhtqd:tysy']");
+	if(type.length==0){
+		type = $.trim($("#changeTYShouYin").text());
+	} else {
+		type = type.val();
+	} 
+	if(type==0 || type =='自收银') {
+		$(".tongyishouyin").hide();
+		$("input",$(".tongyishouyin")).val("");
+	}
+	else{
+		$(".tongyishouyin").show();
+	}
+}
+
+/* 改变【是否特殊条款】 */
+function changeSftstk(){
+	var sftstk = $("[name='m:me_zlhtqd:sftstk']");
+	if(sftstk.length ==0){
+		sftstk = $.trim($("#sftstk").text());
+	}else{
+		sftstk = sftstk.val();
+	} 
+	
+	if(sftstk == "1" || sftstk=='是'){
+		$(".tstk_tr").show();
+	} else{ 
+		$(".tstk_tr").hide();
+		$("textarea",$(".tstk_tr")).val("");
+	}
+}
+
+/*改变【是否接受会员卡】*/
+function changeSfjshyk(){
+	var sfjshyk = $("[name='m:me_zlhtqd:sfjshyk']");
+	if(sfjshyk.length ==0){
+		sfjshyk = $.trim($("#sfjshyk").text());
+	}else{
+		sfjshyk = sfjshyk.val();
+	} 
+	if(sfjshyk=='是'){
+		$(".sfjshyk_flag").show();
+	} else{ 
+		$(".sfjshyk_flag").hide();
+	}
+}
+
+/*改变【租赁类型】*/
+function changeZllx(){
+	var zllx = $("[name='m:me_zlhtqd:zllx']");
+	if(zllx.length ==0){
+		zllx = $.trim($("#zllx").text());
+	}else{
+		zllx = zllx.val();
+	} 
+	if(zllx=="1" || zllx=='新租'){
+		$(".zllx_flag").hide();
+		$("#zllx_td").attr("colspan","3");
+	} else{ 
+		$(".zllx_flag").show();
+		$("#zllx_td").removeAttr("colspan");
+	}
+}
+
+/* 改变【申报类型】 */
+function changeSblx(){
+	var sblx = $("[name='m:me_zlhtqd:sblx']");
+	if(sblx.length ==0){
+		sblx = $.trim($("#sblx").text());
+	}else{
+		sblx = sblx.val();
+	}
+	if(sblx == "2" || sblx == '重新申报'){
+		$("#sbyy_tr").show();
+	} else {
+		$("#sbyy_tr").hide();
+		$("textarea",$("#sbyy_tr")).val("");
+	}
+}
+
+/* 加载时,改变【每月收费项目】的【收费方式】 */
+function loadChangeSffs(){
+	var sffs = $("[name$=':sffs']:visible",$("[tablename='me_zlhtqd_mysfxm']"));
+	sffs.each(function (i) {
+		changeSffs(this);
+	});
+}
+
+/* 改变【每月收费项目】的【收费方式】 */
+function changeSffs(obj){
+	var $tr = $(obj).closest("tr");
+	var sffs = $("[name$=':sffs']",$tr).val(); /* 收费方式 */
+	$("[name$=':dj']",$tr).off();
+	$("[name='shouFeiGuiZe']",$tr).show();
+	switch(sffs){
+		case "0":		/* 固定金额类型 : 只能录入收费金额 */
+		case "4":		
+			$("[name$=':dj']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':sfje']",$tr).removeAttr("readonly");
+			$("[name$=':kl']",$tr).attr("readonly","readonly").val("");
+			break;
+		case "1":		/* 比率类型:只能录入比率 */
+		case "2":
+		case "5":
+			$("[name$=':sfgz']",$tr).val("");
+			$("[name$=':sfgzID']",$tr).val("");
+			$("[name='shouFeiGuiZe']",$tr).hide().parent().css("width","130px");
+			$("[name$=':dj']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':kl']",$tr).removeAttr("readonly");
+		  break;
+		case "3":		/* 每平方米单价类型:只能录入单价 */
+			$("[name$=':dj']",$tr).removeAttr("readonly");
+			$("[name$=':sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':kl']",$tr).attr("readonly","readonly").val("");
+		// 计算价格
+		 $("[name$=':dj']",$tr).on("blur",function(){
+			 var dj =  FormUtil.commaback($(this).val()); 
+			 var curRow = $(this).closest(".listRow"); 
+			 var mianJi = $("[name='m:me_zlhtqd:jzmj']").val();
+			 if(!mianJi) {
+				 $.ligerDialog.warn("尚未生成面积");
+			 }
+		 		$("[name$=':sfje']",curRow).val(FormMath.tofixed(dj*mianJi,2));
+			 });
+		  break;
+		default:
+	}
+}
+/* 校验每月收费项目 */
+function validateMysfxm (){
+	var zuLinStartDate  =$("[name='m:me_zlhtqd:zlyxqq']").val();
+	var zuLinEndDate  =$("[name='m:me_zlhtqd:zlyxqz']").val();
+	var sfxm = $(".listRow:visible",$("[tablename='me_zlhtqd_mysfxm']"));
+	var sfxmArray =[];
+
+	for(var i=0,row;row=sfxm[i++];){
+		var ksrq = $("[name$=':ksrq']",$(row)).val();
+		var jsrq = $("[name$=':jsrq']",$(row)).val();
+		if(!ksrq) continue;
+		var differTotalStartDate = FormDate.compareDate(ksrq, zuLinStartDate);
+		if(differTotalStartDate>0){
+			$.ligerDialog.warn("每月收费项目开始日期不能早于 租赁开始日期!",'请核查');
+			$("[name$=':ksrq']",$(row)).val("");
+		}
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate);
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("每月收费项目结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$=':jsrq']",$(row)).val("");
+		}
+		var curSfxmID =$("[name$=':sfxmID']",$(row)).val();
+		if(curSfxmID){
+			for(var j=0,prevSfxm;prevSfxm=sfxmArray[j++];){
+				var thisSfxmId = prevSfxm.split("$")[0];
+				/* 当前收费项目第二次出现 */
+				if(thisSfxmId == curSfxmID){
+					var differToPrevDate = FormDate.compareDate(ksrq,prevSfxm.split("$")[1]);
+					if(differToPrevDate>=0){
+						$.ligerDialog.warn("同一收费项目日期不得重复!",'请核查');
+						$("[name$=':ksrq']",$(row)).val("");
+					}
+				}
+			  }
+			sfxmArray.push(curSfxmID+"$"+jsrq);
+		}
+	}
+}
+
+/* 分解所有结算标准信息 */
+function decomposeAllJs(tableName,fenjieTable){
+	var $jsbzxxTrs = $(".listRow:visible",$("[tablename='"+tableName+"']"));   
+	$jsbzxxTrs.each(function (i) {
+		decomposeSingle($(this),fenjieTable);
+	});
+}
+function decomposeAllWyf(){
+	decomposeAllJs('me_zlhtqd_wyftk','me_zlhtqd_wyffj');
+}
+function decomposeSingleWyf(){
+	decomposeSingleJs('me_zlhtqd_wyftk','me_zlhtqd_wyffj');
+}
+function decomposeAllYzj(){
+	decomposeAllJs('me_zlhtqd_zjbzxx','me_zlhtqd_ydzjfj');
+}
+function decomposeSingleYzj(){
+	decomposeSingleJs('me_zlhtqd_zjbzxx','me_zlhtqd_ydzjfj');
+}
+
+/* 单个分解 */
+function decomposeSingleJs(tableName,fenjieTable){
+	var $jsbzxxTrs = $("input[name$=':fjsj']:checked",$("div[tablename='"+tableName+"']")).closest("tr");
+	if($jsbzxxTrs.length ==0){
+		$.ligerDialog.warn("请选择要分解的数据!","提示信息");
+		return;
+	}
+	// 分解
+	decomposeSingle($jsbzxxTrs,fenjieTable);
+} 
+
+/*
+ * 【通过分解目标行来分解信息,将结果输出至分解表】 被选中的行:selectRow,分解表的表明fenJieTable
+ */
+function decomposeSingle(selectRow,fenJieTable){
+	var xh = selectRow.find("[name$=':xh']").val();	
+	if(!xh){
+		$.ligerDialog.warn("请选择要分解的租金标准信息","提示");
+		return ;
+	}
+	// 分解表
+	var fenJieTableDiv = $("div[tablename='"+fenJieTable+"']");
+	/* 将序号与当前选中行所有分解信息删除 */
+	$("[name$=':xh'][value=" + xh + "]",fenJieTableDiv).closest("tr").remove(); /* 清除旧的记录 */
+
+	var sjd =$("[name$=':xh']",selectRow).val(); // 序号
+	var ksrq =$("[name$=':ksrq']",selectRow).val();
+	var jsrq =$("[name$=':jsrq']",selectRow).val();
+	var jsrq =$("[name$=':jsrq']",selectRow).val();
+
+	// 物业费
+	if(fenJieTable == 'me_zlhtqd_wyffj'){
+		var zj =FormUtil.commaback($("[name$=':je']",selectRow).val());
+		var sfgz =  $("[name='m:me_zlhtqd:wyfsfgzID']").val();
+		var type = $("[name$=':glfxx']",selectRow).val()
+	// 标准租金
+	}else{
+		var type = $("[name$=':bdxx']",selectRow).val()
+		var zj =FormUtil.commaback($("[name$=':zj']",selectRow).val()); 
+		var sfgz =$("[name='m:me_zlhtqd:zjsfgzID']").val();
+	}
+
+	var jsonData = HyUtil.decompose(sjd,ksrq,jsrq,zj,sfgz,"",type);
+	/* 填充数据 */
+	for (var i = 0, c; c = jsonData[i++];) {
+		FormUtil.addRow(fenJieTableDiv);
+		var rowcount=$("input[name$=':ny']",fenJieTableDiv).length;
+		$($("input[name$=':xh']",fenJieTableDiv).get(rowcount-1)).val(xh);		
+		$($("input[name$=':ny']",fenJieTableDiv).get(rowcount-1)).val(c.ZQY);				
+		$($("input[name$=':ksrq']",fenJieTableDiv).get(rowcount-1)).val(c.KSRQ);			
+		$($("input[name$=':jsrq']",fenJieTableDiv).get(rowcount-1)).val(c.JSRQ);			
+		$($("input[name$=':je']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);	
+		$($("input[name$=':scrq']",fenJieTableDiv).get(rowcount-1)).val(c.CDRQ);
+	}
+}
+
+/* 显示分解信息列表 */
+function showFjsj(obj,fenJieTable){
+	var fenJieTableDiv =$("div[tablename='"+fenJieTable+"']");
+	var tableDiv = $(obj).closest("[tablename]");
+	var selectRow =$("input[name=':fjsj']:checked",tableDiv).closest("tr");
+	var xhVal = selectRow.find("[name$=':xh']").val();/* 选择行 */
+	
+	HyUtil.showDecompose(fenJieTable,'xh',xhVal);
+}
+
+/* 显示店铺明细 */
+function showDpmx(obj){
+	var pwID = $(obj).closest("tr").find("[name='s:me_zlhtqd_pwxx:pwID']").val();
+	HyUtil.showDpmx(pwID);
+}
+
+
+
+

+ 772 - 0
web/js/haiya/form/me/zlzlybg.js

@@ -0,0 +1,772 @@
+/*【租赁变更】**/
+
+$(function(){
+	loadChangeSffs();
+	loadChangeJsgz();
+	validateZujinBiaozhunDates();//处理样式显示
+	HandlKouLvRules(); //处理扣率组样式显示
+	validateWyfDates();//物业费样式显示
+	changeSfjshyk();
+	
+	needUnbindContextMenuEvent();
+	
+});
+
+
+/*校验子表填充数据是否重复**/ 
+function checkDataBeforeInsert(data,tableName){
+    if(tableName == 'me_zlzlybg_hykzkft'){
+      var rows =   $(".listRow",$("[tableName='"+tableName+"']"));  //左右子表列
+      for(var i =0,row;row=rows[i++];){
+        var id = $("[name='s:"+tableName+":ppID']",$(row)).val();  //唯一值
+          if(id ==data.PPBM) {                            //对话框,返回数据的那列
+    	   $.ligerDialog.warn(data.PPNAME+"已经存在了!","提示信息");
+            return false;
+         }
+      }
+    }
+    
+    if(tableName == 'me_zlzlybg_hyklx'){
+        var rows = $(".listRow",$("[tableName='"+tableName+"']"));  //左右子表列
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:"+tableName+":hyklxID']",$(row)).val();  //唯一值
+            if(id == data.HYKTYPE) {                            //对话框,返回数据的那列
+            	$.ligerDialog.warn(data.HYKNAME+"已经存在了!","提示信息");
+            	return false;
+           }
+        }
+      }
+    
+    if(tableName == 'me_zlzlybg_pwxx'){
+        var rows =   $(".listRow",$("[tableName='me_zlzlybg_pwxx']"));  // 左右子表列
+        // 校验该铺位是否被租出去
+        var zuLinStartDate  =$("[name='m:me_zlzlybg:zlyxqq']").val();
+		if(!zuLinStartDate ){  $.ligerDialog.warn("尚未输入租赁开始日期!",'请核查');  return false; }
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:me_zlzlybg_pwxx:pwID']",$(row)).val();  // 唯一值
+            if(id ==data.WLDPID) {                              // 对话框,返回数据的那列
+            	$.ligerDialog.warn(data.WLDPDM+"已经存在了!","提示信息");
+            	return false;
+           }
+        }
+      }
+     return true;
+  }
+
+/*租赁时间变化事件**/
+function zlsjChangeEvent(){
+	FormDate.doDateCalculate();
+	validateZujinBiaozhunDates();
+	$("[name='s:me_zlzlybg_klzgz:sjd']").trigger("change");
+	validateWyfDates();
+	validateMysfxm();
+}
+
+/* 【表结算标准信息】 添加行事件*/
+function me_zlzlybg_jsbzxxAddRowAfterEvent(row){
+	var preRow = $(".listRow:visible",$("[tablename='me_zlzlybg_jsbzxx']")).not(row).last();
+	
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx)preNx = 0;
+	$("[name$=':xh']",row).val(1+Number(preNx));
+	$("[name$=':sjd']",row).val(1+Number(preNx));
+	validateZujinBiaozhunDates(row); 
+	changeJsgz(row,false);
+};
+// 校验租金标准 时间
+function validateZujinBiaozhunDates(curRow){
+	//租赁有效期起,止 
+	var zuLinStart  =$("[name='m:me_zlzlybg:zlyxqq']");
+	var zuLinStartDate  =zuLinStart.val();
+	var zuLinEndDate  =$("[name='m:me_zlzlybg:zlyxqz']").val();
+	//length!=0 只读判断
+	if((!zuLinEndDate || !zuLinStartDate)&& curRow ){
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		$(curRow).remove();
+		return;
+	}
+	
+	var rows = $(".listRow:visible",$("[tablename='me_zlzlybg_jsbzxx']"));
+	for(var i=0,row;row=rows[i++];){
+		if(i>1){
+			// 处理比较颜色
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"bdxs"); // 保底销售
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"bdkl"); // 保底扣率
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"mbxs"); // 目标销售
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"cmbkl"); // 超目标销售
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zbd"); // 总保底
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zmb"); // 总目标
+			if(zuLinStart.length ==0) continue; // 如果只读状态不再判断其他
+			
+			//开始日期等于上个结束日期加1
+			var startDate = HyUtil.getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			if(zuLinStart.length ==0) continue; //只读不判断
+			
+			var differTotalStartDate = FormDate.compareDate($("[name$=':ksrq']",$(row)).val(), zuLinStartDate);
+			if(differTotalEndDate>=0)  {
+				$.ligerDialog.warn("当前开始日期不能早于租赁开始日期!",'请核查');
+			} 
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",$(row)).val(), zuLinEndDate);
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("当前结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$=':jsrq']",$(row)).val("")
+			return ;
+		}
+	}
+	$("[name='s:me_zlzlybg_klzgz:sjd']").trigger("change");
+	// 触发单价
+	$("[name$=':dj']").trigger("change");
+}
+
+/*加载时,改变【结算标准信息】的【结算规则】*/
+function loadChangeJsgz(){
+	var $jsgz = $("[name$=':jsgz']:visible",$("[tablename='me_zlzlybg_jsbzxx']")); 
+	$jsgz.each(function (i) {
+		changeJsgz(this,true);
+	});
+}
+
+/*改变【结算标准信息】的【结算规则】*/
+function changeJsgz(obj,isLoad){
+	var $tr = $(obj).closest("tr");
+	var jsgz = $("[name$=':jsgz']",$tr).val(); /*结算规则*/
+		
+	if(jsgz != 2){
+		generateKlz(); // 产生扣率组
+	}
+	 
+	switch(jsgz){
+		case "1":		/*实销实结,产生一条扣率组,保底销售(只读)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).attr("readonly","readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly");
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");
+			 
+			 if(!isLoad){
+				 $("[name$=':bdxs']",$tr).val("");	
+				 $("[name$=':bdkl']",$tr).val("");
+				 $("[name$=':mbxs']",$tr).val("");
+				 $("[name$=':cmbkl']",$tr).val("");
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");
+			 }
+			 
+			 //$("#xs_span").show();	/*保底销售*/
+			// $("#ml_span").hide();	/*保底毛利*/
+			 
+		 break;
+		case "2":		/*固定毛利额,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly");
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");
+			 
+			 if(!isLoad){
+				 $("[name$=':bdkl']",$tr).val("");
+				 $("[name$=':mbxs']",$tr).val("");
+				 $("[name$=':cmbkl']",$tr).val("");
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");
+			 }
+			 
+			 //$("#xs_span").show();	/*保底销售*/
+			// $("#ml_span").hide();	/*保底毛利*/
+			 
+		  break;
+		case "3":		/*有保底销售无目标销售,产生一条扣率组,保底销售(可编辑)、保底扣率(可编辑)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).removeAttr("readonly");	
+			 
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly");
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly");
+			 
+			 if(!isLoad){
+				 $("[name$=':mbxs']",$tr).val("");
+				 $("[name$=':cmbkl']",$tr).val("");
+			 }
+			 
+			 //$("#xs_span").show();	/*保底销售*/
+			 //$("#ml_span").hide();	/*保底毛利*/
+		  break;
+		case "4":		/*有保底毛利无目标毛利,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly");
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly");	
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");
+			 
+			 if(!isLoad){
+				 $("[name$=':bdkl']",$tr).val("");
+				 $("[name$=':mbxs']",$tr).val("");
+				 $("[name$=':cmbkl']",$tr).val("");	
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");
+			 }
+			 
+			 //$("#xs_span").hide();	/*保底销售*/
+			 //$("#ml_span").show();	/*保底毛利*/
+			break;
+		case "5":		/*有保底销售有目标销售,产生一条扣率组,保底毛利(可编辑)、保底扣率(可编辑)、目标销售(可编辑)、超额目标扣率(可编辑)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");		
+			 $("[name$=':bdkl']",$tr).removeAttr("readonly");
+			 $("[name$=':mbxs']",$tr).removeAttr("readonly");
+			 $("[name$=':cmbkl']",$tr).removeAttr("readonly");	
+			 
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");	
+			 
+			 if(!isLoad){
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");	
+			 }
+			 //$("#xs_span").show();	/*保底销售*/
+			 //$("#ml_span").hide();	/*保底毛利*/
+			break;
+		case "6":		/*有保底毛利有目标毛利,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(可编辑)、超额目标扣率(可编辑)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':mbxs']",$tr).removeAttr("readonly");
+			 $("[name$=':cmbkl']",$tr).removeAttr("readonly");
+			 
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly");
+			 
+			 if(!isLoad){
+				 $("[name$=':bdkl']",$tr).val("");
+				 $("[name$=':zbd']",$tr).val("");
+				 $("[name$=':zmb']",$tr).val("");	
+			 }
+			 
+			 //$("#xs_span").hide();	/*保底销售*/
+			 //$("#ml_span").show();	/*保底毛利*/
+			 
+			break;
+		default:
+	}
+	
+	addNeedForKouLv();//	判断是否需要增加扣率组和扣率规则必填
+	
+}
+/*产生一条扣率组*/
+function generateKlz(){
+	if($(".listRow:visible",$("div[tablename='me_zlzlybg_klz']")).length ==0){
+		FormUtil.addRow($("div[tablename='me_zlzlybg_klz']"));  
+		var appendRow = $(".listRow:visible",$("div[tablename='me_zlzlybg_klz']"))[0];
+		$("[name$=':klzbh']",appendRow).val(1);
+		$("[name$=':ms']",appendRow).val("扣组率1");
+	}
+}
+
+//判断是否需要增加扣率组和扣率规则必填
+function addNeedForKouLv(){
+	var isNeed = false;
+	$("[name='s:me_zlzlybg_jsbzxx:jsgz']").each(function (i){
+		var val = $(this).val();
+		if(val != 2){
+			isNeed = true;
+			return false; // 终止循环
+		}
+	});
+	if(isNeed){
+		$("div[tablename='me_zlzlybg_klz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='me_zlzlybg_klz']")).length ==0){
+			$("div[tablename='me_zlzlybg_klz']").addClass("validError");
+		}
+		
+		$("div[tablename='me_zlzlybg_klzgz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='me_zlzlybg_klzgz']")).length ==0){
+			$("div[tablename='me_zlzlybg_klzgz']").addClass("validError");
+		}
+	}else{
+		$("div[tablename='me_zlzlybg_klz']").attr("right","w");
+		$("div[tablename='me_zlzlybg_klzgz']").attr("right","w");
+		$("div[tablename='me_zlzlybg_klz']").removeClass("validError");
+		$("div[tablename='me_zlzlybg_klzgz']").removeClass("validError");
+	}
+}
+
+// 扣率组删除事件
+function me_zlzlybg_klzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+//扣率组规则删除事件
+function me_zlzlybg_klzgzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+//扣率组添加事件
+function me_zlzlybg_klzAddRowAfterEvent(row){
+	autoNumForKlzbm(row);
+}
+
+// 对扣率组编号自动编码
+function autoNumForKlzbm(row){
+	var preRow = $(row).prev();
+	var preBh = $("[name$=':klzbh']",preRow).val();
+	if(!preBh)preBh = 0;
+	$("[name$=':klzbh']",row).val(1+Number(preBh));
+}
+
+/*处理扣率组大小比较*/
+function HandlKouLvRules(){
+	var kouLvRules = $(".listRow:visible",$("[tablename='me_zlzlybg_klzgz']"));
+	var combineMessage = [];
+	for(var i=0,rule;rule=kouLvRules[i++];){
+		var sjdInput=$("[name$=':sjd']",rule);
+		if(sjdInput.length==0){ /*只读状态*/
+			var sjd = $("[name='sjd_td']",rule).text().trim(); /*时间段*/
+			var klz = $("[name='klz_td']",rule).text().trim();/*扣率组*/
+		}else{
+			var sjd = sjdInput.val(); /*时间段*/
+			var klz = $("[name$=':klz']",rule).val();/*扣率组*/
+		}
+		if(!sjd || !klz) return;
+		 /*第一次设置开始金额为当前金额为第几行*/	
+		var len=-1;
+		for(var j=0,r;r=combineMessage[j++];){
+			if(r.split("-")[0] == sjd+","+klz){
+				len =Number(r.split("-")[1]);
+			}
+		}
+		if(len!=-1){
+			HyUtil.fillColorByCompare(rule,kouLvRules[len],"kl");
+		}
+		combineMessage.push(sjd+","+klz+"-"+(i-1));
+	}
+}
+
+/*处理扣率时间段*/
+function handelKoulvTime(obj){
+	var curRow = $(obj).closest(".listRow");
+	var shijianNo = $("[name$=':sjd']",curRow).val();
+	if(!shijianNo) return ;
+	/*取的时间段*/
+	var shiJianDuanNo =$("[name='s:me_zlzlybg_jsbzxx:sjd'][value="+shijianNo+"]");
+	if(shiJianDuanNo.length ==0) {
+		$.ligerDialog.warn("该时间段不存在! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	var shiJianDuanRow = shiJianDuanNo.closest(".listRow");
+	var StartDate = $("[name$=':ksrq']",shiJianDuanRow).val();
+	var endDate = $("[name$=':jsrq']",shiJianDuanRow).val();
+	if(!StartDate || !endDate) {
+		$.ligerDialog.warn("该时间段信息不完善! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	
+	$("[name$=':ksrq']",curRow).val(StartDate);
+	$("[name$=':jsrq']",curRow).val(endDate);
+}
+
+/*处理扣率组*/
+function handelKoulvGroup(obj){
+	var koulvNo = $(obj).val();
+	if(!koulvNo) return ;
+	/*取扣率组*/
+	var KouLv =$("[name='s:me_zlzlybg_klz:klzbh'][value="+koulvNo+"]");
+	if(KouLv.length ==0) {
+		$.ligerDialog.warn("该扣率组不存在! “"+koulvNo+"”",'请核查');
+		$(obj).val("");
+		return ;
+	}
+	
+	var klzgzMs = KouLv.closest("tr").find("[name='s:me_zlzlybg_klz:ms']").val();		//	扣率组的【描述】
+	$(obj).closest("tr").find("[name='s:me_zlzlybg_klzgz:ms']").val(klzgzMs);	
+}
+
+/*【物业费条款添加事件】*/
+function me_zlzlybg_wyftkAddRowAfterEvent(curRow){
+	var preRow = $(".listRow:visible",$("[tablename='me_zlzlybg_wyftk']")).not(curRow).last();
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx) preNx=0;
+	$("[name$=':nx']",curRow).val(1+Number(preNx));
+	$("[name$=':xh']",curRow).val(1+Number(preNx));
+	
+	validateWyfDates(curRow);
+	
+}
+
+/*校验物业费时间**/
+function validateWyfDates(curRow){
+	//租赁有效期起
+	var zuLinStart  =$("[name='m:me_zlzlybg:zlyxqq']");
+	var zuLinStartDate  =zuLinStart.val();
+	var zuLinEndDate  =$("[name='m:me_zlzlybg:zlyxqz']").val();
+	if((!zuLinStartDate || !zuLinEndDate) && curRow ){ 
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		$(curRow).remove();
+		return ;
+	}
+	//循环计算开始日期
+	var rows = $(".listRow:visible",$("[tablename='me_zlzlybg_wyftk']"));
+	for(var i=0,row;row=rows[i++];){
+		if(i>1){
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"xs");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"dj");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"je");
+			HyUtil.fillColorByCompare(row,$(rows[i-2]),"zje");
+			}
+		if(zuLinStart.length==0) continue;
+		if(i>1){
+			//开始日期等于上个结束日期加1
+			var startDate = HyUtil.getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		//校验结束日期
+		var jsrq =$("[name$=':jsrq']",row).val();
+		if(!jsrq) continue;
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate);
+		if(differTotalEndDate<0){
+			$.ligerDialog.warn("结束日期不能晚于租赁有效期止!",'请核查!');
+			$("[name$=':jsrq']",curRow).val("");
+		}
+	}
+}
+/*总物业费**/
+function calZongWuYeFei(obj){
+	var curRow = $(obj).closest(".listRow"); 
+	//校验是否超过总日期
+	var zuLinEndDate =$("[name='m:me_zlzlybg:zlyxqz']").val();
+	var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",curRow).val(), zuLinEndDate);
+	if(differTotalEndDate<0)  {
+		$.ligerDialog.warn("当前结束日期不能晚于租赁有效期止!",'请核查');
+		$("[name$=':jsrq']",curRow).val("")
+		return ;
+	}
+	
+	var danJia = FormUtil.commaback($("[name$=':dj']",curRow).val()); // 单价
+	
+	var mianJi = $("[name='m:me_zlzlybg:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var baoDiType =$("[name$=':glfxx']",curRow).val();
+	var xs = $("[name$=':xs']",curRow).val();
+	var jine = FormMath.tofixed(mianJi * danJia * xs,2);
+
+	$("[name$=':je']",curRow).val(jine); $("[name$=':je']",curRow).trigger("change");
+	
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	if(baoDiType =="0"){ //按月
+		var zongJine = HyUtil.calMoneyWithMount(startDate,endDate,jine);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongJine = FormMath.tofixed(jine*days,2);
+	}
+	$("[name$=':zje']",curRow).val(zongJine);$("[name$=':zje']",curRow).trigger("change");
+ }
+
+/*加载时,改变【每月收费项目】的【收费方式】*/
+function loadChangeSffs(){
+	var sffs = $("[name$=':sffs']:visible",$("[tablename='me_zlzlybg_mysfxm']"));
+	sffs.each(function (i) {
+		changeSffs(this);
+	});
+}
+
+/*改变【每月收费项目】的【收费方式】*/
+function changeSffs(obj){
+	var $tr = $(obj).closest("tr");
+	var sffs = $("[name$=':sffs']",$tr).val(); /*收费方式*/
+	$("[name$=':dj']",$tr).off();
+	switch(sffs){
+		case "0":		/*固定金额类型 : 只能录入收费金额*/
+		case "4":		
+			 $("[name$=':dj']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':sfje']",$tr).removeAttr("readonly");
+			 $("[name$=':kl']",$tr).attr("readonly","readonly").val("");
+		  break;
+		case "1":		/*比率类型:只能录入比率*/
+		case "2":
+		case "5":
+			 $("[name$=':sfgz']",$tr).val("");
+			 $("[name$=':sfgzID']",$tr).val("");
+			$("[name$=':dj']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':kl']",$tr).removeAttr("readonly");
+		  break;
+		case "3":		/*每平方米单价类型:只能录入单价*/
+			 $("[name$=':dj']",$tr).removeAttr("readonly");
+			$("[name$=':sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$=':kl']",$tr).attr("readonly","readonly").val("");
+			//计算价格
+			 $("[name$=':dj']",$tr).on("blur",function(){
+				 var dj =  FormUtil.commaback($(this).val()); 
+				 var curRow = $(this).closest(".listRow"); 
+				 var mianJi = $("[name='m:me_zlzlybg:jzmj']").val();
+				 if(!mianJi) $.ligerDialog.warn("尚未生成面积!",'提示');
+				 $("[name$=':sfje']",curRow).val(FormMath.tofixed(dj*mianJi,2));
+			 });
+		  break;
+		default:
+	}
+}
+
+/*校验每月收费项目*/
+function validateMysfxm (){
+	var zuLinStartDate  =$("[name='m:me_zlzlybg:zlyxqq']").val();
+	var zuLinEndDate  =$("[name='m:me_zlzlybg:zlyxqz']").val();
+	
+	var sfxm = $(".listRow:visible",$("[tablename='me_zlzlybg_mysfxm']"));
+	var sfxmArray =[];
+	 
+	for(var i=0,row;row=sfxm[i++];){
+		var ksrq = $("[name$=':ksrq']",$(row)).val();
+		var jsrq = $("[name$=':jsrq']",$(row)).val();
+		if(!ksrq) continue;
+		var differTotalStartDate = FormDate.compareDate(ksrq, zuLinStartDate);
+		if(differTotalStartDate>0){
+			$.ligerDialog.warn("每月收费项目开始日期不能早于 租赁开始日期!",'请核查');
+			$("[name$=':ksrq']",$(row)).val("")
+		}
+		
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate);
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("每月收费项目结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$=':jsrq']",$(row)).val("")
+		}
+		var curSfxmID =$("[name$=':sfxmID']",$(row)).val();
+		if(curSfxmID){
+			for(var j=0,prevSfxm;prevSfxm=sfxmArray[j++];){
+				var thisSfxmId = prevSfxm.split("$")[0];
+				/*当前收费项目第二次出现*/
+				if(thisSfxmId == curSfxmID){
+					var differToPrevDate = FormDate.compareDate(ksrq,prevSfxm.split("$")[1]);
+					if(differToPrevDate>=0){
+						$.ligerDialog.warn("同一收费项目日期不得重复!",'请核查');
+						$("[name$=':ksrq']",$(row)).val("");
+					}
+				}
+			  }
+			
+			sfxmArray.push(curSfxmID+"$"+jsrq);
+			}
+		}
+}
+
+function calBaodiMubiao(obj,targetInput){
+	var curRow = $(obj).closest(".listRow"); 
+	var baodi = FormUtil.commaback($(obj).val());
+	var mianJi = $("[name='m:me_zlzlybg:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	var ZongJine = HyUtil.calMoneyWithMount(startDate,endDate,baodi);
+	$("[name$=':"+targetInput+"']",curRow).val(ZongJine).trigger("change");
+}
+
+
+/**分解代码 begin**/
+
+/*结算标准信息   删除行事件*/
+function me_zlzlybg_jsbzxxDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:me_zlzlybg_bdxx:xh'][value=" + xh + "]").closest("tr").remove();/*清除旧的记录*/
+}
+
+/*物业费条款   删除行事件*/
+function me_zlzlybg_wyftkDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:me_zlzlybg_wyffj:xh'][value=" + xh + "]").closest("tr").remove();/*清除旧的记录*/
+}
+
+function decomposeAllBaodi(){
+	decomposeAllJs('me_zlzlybg_jsbzxx','me_zlzlybg_bdxx');
+}
+function decomposeSingleBaodi(){
+	decomposeSingleJs('me_zlzlybg_jsbzxx','me_zlzlybg_bdxx');
+}
+
+function decomposeAllWyf(){
+	decomposeAllJs('me_zlzlybg_wyftk','me_zlzlybg_wyffj');
+}
+function decomposeSingleWyf(){
+	decomposeSingleJs('me_zlzlybg_wyftk','me_zlzlybg_wyffj');
+}
+
+/*分解所有结算标准信息  */
+function decomposeAllJs(tableName,fenjieTable){
+	var $jsbzxxTrs = $(".listRow:visible",$("[tablename='"+tableName+"']"));   
+	$jsbzxxTrs.each(function (i) {
+		decomposeSingle($(this),fenjieTable);
+	});
+}
+
+/*单个分解*/
+function decomposeSingleJs(tableName,fenjieTable){
+	$jsbzxxTrs = $("input[name$=':fjsj']:checked",$("div[tablename='"+tableName+"']")).closest("tr");
+	if($jsbzxxTrs.length ==0){
+		$.ligerDialog.warn("请选择要分解的数据!","提示信息");
+		return;
+	}
+	//分解
+	decomposeSingle($jsbzxxTrs,fenjieTable);
+} 
+
+/*【通过分解目标行来分解信息,将结果输出至分解表】 
+ * 被选中的行:selectRow,分解表的表明fenJieTable*/
+function decomposeSingle(selectRow,fenJieTable){
+	var xh = selectRow.find("[name$=':xh']").val();	
+	if(!xh){
+		$.ligerDialog.warn("请选择要分解数据","提示");
+		return ;
+	}
+	//分解表
+	var fenJieTableDiv = $("div[tablename='"+fenJieTable+"']");
+	/*将序号与当前选中行所有分解信息删除*/
+	$("[name$=':xh'][value=" + xh + "]",fenJieTableDiv).closest("tr").remove(); /*清除旧的记录*/
+	
+	var sjd =$("[name$=':xh']",selectRow).val(); //序号
+	var ksrq =$("[name$=':ksrq']",selectRow).val();
+	var jsrq =$("[name$=':jsrq']",selectRow).val();
+	var yzj,mbxs,type;
+	//物业费
+	if(fenJieTable == 'me_zlzlybg_wyffj'){
+		yzj =FormUtil.commaback($("[name$=':je']",selectRow).val());
+		type =$("[name$=':glfxx']",selectRow).val();
+	//标准租金
+	}else{
+		yzj=FormUtil.commaback($("[name$=':bdxs']",selectRow).val()); 
+		 mbxs =FormUtil.commaback($("[name$=':mbxs']",selectRow).val()); 
+	}
+	var jsonData = HyUtil.decompose(sjd,ksrq,jsrq,yzj,"",mbxs,type);
+	/*填充数据*/
+	for (var i = 0, c; c = jsonData[i++];) {
+		FormUtil.addRow(fenJieTableDiv);
+		var rowcount=$("input[name$=':ny']",fenJieTableDiv).length;
+		$($("input[name$=':xh']",fenJieTableDiv).get(rowcount-1)).val(xh);		
+		$($("input[name$=':ny']",fenJieTableDiv).get(rowcount-1)).val(c.ZQY);				
+		$($("input[name$=':ksrq']",fenJieTableDiv).get(rowcount-1)).val(c.KSRQ);			
+		$($("input[name$=':jsrq']",fenJieTableDiv).get(rowcount-1)).val(c.JSRQ);			
+		if(fenJieTable == 'me_zlzlybg_wyffj'){
+			$($("input[name$=':je']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);	
+		}else{
+			$($("input[name$=':bdxsml']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);	
+			$($("input[name$=':mbxsml']",fenJieTableDiv).get(rowcount-1)).val(c.MBXS);
+		}
+	}
+}
+
+/*显示分解信息列表 */
+function showFjsj(obj,fenJieTable){
+	var fenJieTableDiv =$("div[tablename='"+fenJieTable+"']");
+	var tableDiv = $(obj).closest("[tablename]");
+	var selectRow =$("input[name=':fjsj']:checked",tableDiv).closest("tr");
+	var xhVal = selectRow.find("[name$=':xh']").val();/*选择行*/
+	
+	HyUtil.showDecompose(fenJieTable,'xh',xhVal);
+}
+
+
+/**分解代码 end**/
+
+/*格式化填充的日期*/
+$().ready(function (){
+	$("[issameas]").change(function(){
+		var me = $(this),curVal = me.val();
+		var curNameStr = me.attr("name");
+		if(!curNameStr) return; 
+		var curName = curNameStr.split(":");
+		var targetName = me.attr("isSameAs");
+		if(targetName=='true' && curName.length == 3){
+			targetName =curName[0] +":"+curName[1] +":y"+curName[2]
+		}
+		if(!window.isSameToCheck_)window.isSameToCheck_ = [];
+		window.isSameToCheck_.push(curNameStr+";"+targetName);
+		
+		window.setTimeout(function(){checkIsSame()},50); 
+	});
+	
+	function checkIsSame(){
+		if(!window.isSameToCheck_) return ;
+		
+		for(var checkStr;checkStr=window.isSameToCheck_.pop();){
+			var targetValue = $("[name='"+checkStr.split(";")[1]+"']").val();
+			var curInput = $("[name='"+checkStr.split(";")[0]+"']");
+			 
+			if(targetValue != curInput.val()){
+				curInput.css("color","red");
+			}else{
+				curInput.css("color","");
+			}
+		}
+	}
+	
+	$("div[tablename='me_zlzlybg_yydzjfj']").hide();	// 原月度租金分解
+	$("div[tablename='me_zlzlybg_bdxx']").hide();	// 保底信息
+	$("div[tablename='me_zlzlybg_ywyffj']").hide();	// 原物业费分解
+	$("div[tablename='me_zlzlybg_wyffj']").hide();	// 物业费分解
+});
+
+function CustomQueryAftEvent__(){
+	$("div[tablename='me_zlzlybg_yydzjfj']").hide();	// 原月度租金分解
+	$("div[tablename='me_zlzlybg_bdxx']").hide();	// 保底信息
+	$("div[tablename='me_zlzlybg_ywyffj']").hide();	// 原物业费分解
+	$("div[tablename='me_zlzlybg_wyffj']").hide();	// 物业费分解
+	
+	validateZujinBiaozhunDates();//处理样式显示
+	HandlKouLvRules(); //处理扣率组样式显示
+	validateWyfDates();//物业费样式显示 
+	
+	needUnbindContextMenuEvent();
+	HyUtil.unbindContextMenuEvent("me_zlzlybg_yzjbzxx");	//	原租金标准信息
+	HyUtil.unbindContextMenuEvent("me_zlzlybg_yklz");		//	原扣率组
+	HyUtil.unbindContextMenuEvent("me_zlzlybg_yklzgz");		//	原扣率规则
+	HyUtil.unbindContextMenuEvent("me_zlzlybg_ywyftk");		//	原物业费条款
+	HyUtil.unbindContextMenuEvent("me_zlzlybg_ybzj");		//	原保证金
+	HyUtil.unbindContextMenuEvent("me_zlzlybg_ymysfxm");	//	原每月收费项目
+	HyUtil.unbindContextMenuEvent("me_zlzlybg_ysffsxm");	//	原收款方式项目
+	HyUtil.unbindContextMenuEvent("me_zlzlybg_yhykzkft");	//	原折扣分摊标准
+	HyUtil.unbindContextMenuEvent("me_zlzlybg_yhyklx");		//	原会员卡类型
+}
+
+/*改变【是否接受会员卡】*/
+function changeSfjshyk(){
+	var sfjshyk = $("[name='m:me_zlzlybg:sfjshyk']");
+	if(sfjshyk.length ==0){
+		sfjshyk =$("#sfjshyk").text().trim();
+	}else{
+		sfjshyk = sfjshyk.val();
+	} 
+	if(sfjshyk=='是'){
+		$(".sfjshyk_flag").show();
+	} else{ 
+		$(".sfjshyk_flag").hide();
+	}
+}
+
+//需要特殊处理右键事件
+function needUnbindContextMenuEvent(){
+	var pwxxRows = $(".listRow:visible",$("[tablename='me_zlzlybg_pwxx']"));
+	pwxxRows.each(function (i){
+		var sfypw = $(this).find("[name='s:me_zlzlybg_pwxx:sfypw']").val();
+		if(sfypw == "true"){
+			$(this).unbind("contextmenu");
+			$(this).find(".extend").remove();
+			$(this).find("[name='s:me_zlzlybg_pwxx:zywz']").attr("readonly","readonly");
+		}
+	});
+}
+
+
+

+ 38 - 0
web/js/haiya/form/me/zycsjxhtqd.js

@@ -0,0 +1,38 @@
+/**自营超市经销合同*/
+$(function(){
+	$("[name='s:me_zybhjxht_jskk:kklx']").trigger("change");
+});
+
+/**扣款类型change事件*/
+function kklxChangeEvent(obj){
+	var kklx = $(obj).val();
+	var curRow = $(obj).closest(".listRow");
+	$("[name$=':kkje'],[name$=':kkbl']",curRow).removeAttr("readonly");
+	switch(kklx){
+		case "0":
+		case "1":		
+			 $("[name$='kkbl']",curRow).attr("readonly","readonly").val("");
+		  break;
+		case "2":
+		case "3":
+			 $("[name$=':kkje']",curRow).attr("readonly","readonly").val("");
+		  break;
+	}
+}
+
+/*显示租户信息*/
+function showZh(){
+	var zhid = $("[name$='zhID']").val();
+	if(zhid == null || zhid == ""){
+		$.ligerDialog.warn("请先选择租户!","提示");
+		return ;
+	}
+	var url=__ctx + "/platform/form/bpmDataTemplate/detailData_shxxwh.ht?__pk__="+zhid;
+	DialogUtil.open({
+		height:600,
+		width: 800,
+		title : "查看租户信息",
+		url: url, 
+		isResize: true
+	});
+}

+ 388 - 0
web/js/haiya/form/op/hdcdxy.js

@@ -0,0 +1,388 @@
+
+$().ready(function (){
+	$("div[tablename='op_hdcdxy_ydzjfj']").hide();	/* 【月度租金分解】不能直接控制隐藏,否则权限控制不了,先隐藏全部,在显示个体 */
+});
+
+
+/*租赁时间变化事件**/
+function zlsjChangeEvent(){
+	validateZujinBiaozhunDates();
+	$("[name='s:op_hdcdxy_klzgz:sjd']").trigger("change");
+}
+
+/*校验临时场地子表填充数据是否重复**/ 
+function checkDataBeforeInsert(data,tableName){
+    if(tableName == 'op_hdcdxy_lscd'){
+        var rows = $(".listRow",$("[tableName='"+tableName+"']"));  //左右子表列
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:"+tableName+":lscddm']",$(row)).val();  //唯一值
+            if(id == data.WLDPDM) {                       //对话框,返回数据的那列
+      	   $.ligerDialog.warn(data.WLDPDM+"已经存在了!","提示信息");
+              return false
+           }
+        }
+        
+      //校验该铺位是否被租出去
+        var zuLinStartDate  =$("[name='m:op_hdcdxy:zlyxqq']").val();
+		if(!zuLinStartDate ){  $.ligerDialog.warn("尚未输入租赁开始日期!",'请核查');  return false }
+		var conf ={aliasName:'validatePuweiIsUsed',pwid:data.WLDPID,sDate:zuLinStartDate};
+		var json = RunAliasScript(conf);
+		if(json.isSuccess ==1){
+			 $.ligerDialog.warn(json.msg);return false;
+		}else if(json.result){
+			$.ligerDialog.warn("该铺位到期日期为:"+json.result+",请核查!");return false;
+		}
+      }
+
+    return true;
+  }
+
+/* 表租金标准信息 添加行事件*/
+function op_hdcdxy_zjbzxxAddRowAfterEvent(row){
+	var preRow = $(row).prev();
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx)preNx = 0;
+	$("[name$=':xh']",row).val(1+Number(preNx));
+	validateZujinBiaozhunDates(row)
+};
+// 校验租金标准 时间
+function validateZujinBiaozhunDates(curRow){
+	//租赁有效期起,止
+	var zuLinStartDate  =$("[name='m:op_hdcdxy:zlyxqq']").val();
+	var zuLinEndDate  =$("[name='m:op_hdcdxy:zlyxqz']").val();
+	if((!zuLinStartDate || !zuLinEndDate)&&curRow){
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		$(curRow).remove();
+		return;
+	}
+	
+	var rows = $(".listRow:visible",$("[tablename='op_hdcdxy_zjbzxx']"));
+	for(var i=0,row;row=rows[i++];){
+		var shiJianDuan = $("[name$=':sjd']",$(row)).val(i); //时间段
+		
+		if(i>1){
+			//开始日期等于上个结束日期加1
+			var startDate = HyUtil.getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			var differTotalStartDate = FormDate.compareDate($("[name$=':ksrq']",$(row)).val(), zuLinStartDate);
+			if(differTotalEndDate>=0)  {
+				$.ligerDialog.warn("当前开始日期不能早于 租赁开始日期!",'请核查');
+			} 
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",$(row)).val(), zuLinEndDate, "day");
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("当前结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$=':jsrq']",$(row)).val("")
+			return ;
+		}
+	}
+	$("[name$=':dj']",curRow).trigger("change");
+	$("[name='s:op_hdcdxy_klzgz:sjd']").trigger("change");
+}
+/*修改租金规则*/
+function changeZujinGz(obj){
+	var curRow = $(obj).closest(".listRow");
+	var danJia = $("[name$=':dj']",curRow);
+	var zuJin = $("[name$=':zj']",curRow);
+	var rule = $(obj).val();
+	
+	if(rule == "2" || rule=="4"){ //纯扣
+		danJia.val(0);zuJin.val(0);
+		danJia.attr("readonly","readonly");
+		zuJin.attr("readonly","readonly");
+	}else{
+		danJia.removeAttr("readonly");
+		zuJin.removeAttr("readonly"); 
+	}
+	danJia.trigger("change");zuJin.trigger("change");
+	if(rule != "1"){	//	固定金额 不加扣率组
+		createKoulvzu();
+	}
+	addNeedForKouLv();//	判断是否需要增加扣率组和扣率规则必填
+	
+}
+
+function createKoulvzu(){
+	if($(".listRow:visible",$("[tablename$='klz']")).length ==0){
+		FormUtil.addRow($('div[tablename$="klz"]'));  
+		var appendRow = $(".listRow:visible",$("[tablename$='klz']"))[0];
+		$("[name$=':klzbh']",appendRow).val(1);
+		$("[name$=':jckl']",appendRow).val(1);
+		$("[name$=':ms']",appendRow).val("扣组率1");
+	}
+}
+
+//判断是否需要增加扣率组和扣率规则必填
+function addNeedForKouLv(){
+	var isNeed = false;
+	$("[name='s:op_hdcdxy_zjbzxx:zjgzID']").each(function (i){
+		var val = $(this).val();
+		if(val != 1){
+			isNeed = true;
+			return false; // 终止循环
+		}
+	});
+	if(isNeed){
+		$("div[tablename='op_hdcdxy_klz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='op_hdcdxy_klz']")).length ==0){
+			$("div[tablename='op_hdcdxy_klz']").addClass("validError");
+		}
+		
+		$("div[tablename='op_hdcdxy_klzgz']").attr("right","b");
+		if($(".listRow:visible",$("[tablename='op_hdcdxy_klzgz']")).length ==0){
+			$("div[tablename='op_hdcdxy_klzgz']").addClass("validError");
+		}
+	}else{
+		$("div[tablename='op_hdcdxy_klz']").attr("right","w");
+		$("div[tablename='op_hdcdxy_klzgz']").attr("right","w");
+		$("div[tablename='op_hdcdxy_klz']").removeClass("validError");
+		$("div[tablename='op_hdcdxy_klzgz']").removeClass("validError");
+	}
+}
+
+//扣率组删除事件
+function op_hdcdxy_klzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+//扣率组规则删除事件
+function op_hdcdxy_klzgzDelRowBeforeEvent(row){
+	addNeedForKouLv();
+}
+
+//扣率组添加事件
+function op_hdcdxy_klzAddRowAfterEvent(row){
+	autoNumForKlzbm(row);
+}
+
+// 对扣率组编号自动编码
+function autoNumForKlzbm(row){
+	var preRow = $(row).prev();
+	var preBh = $("[name$=':klzbh']",preRow).val();
+	if(!preBh)preBh = 0;
+	$("[name$=':klzbh']",row).val(1+Number(preBh));
+}
+
+/**单价计算总租金
+ * trigger 'dj'/'zj' 触发者
+ * */
+function calZongZuJin(obj){
+	var trigger = $(obj).attr("name").split(":")[2];
+	var curRow = $(obj).closest(".listRow");
+	var val = FormUtil.commaback($(obj).val()); 
+	if(! val>0) return; 
+	
+	var mianJi = $("[name='m:op_hdcdxy:jzmj']").val();
+	if(!mianJi){ $.ligerDialog.warn(" 合同建筑面积不能为空!",'请核查'); return ; }
+	
+	var zuJin,danJia;
+	/* 如果是单价*/
+	if('dj'== trigger){
+		danJia = val;
+		zuJin = mianJi * danJia;
+		$("[name$=':zj']",curRow).val(FormMath.tofixed(zuJin,2));
+	}else if('zj' == trigger){
+		zuJin = val;
+		danJia =zuJin/mianJi;
+		$("[name$=':dj']",curRow).val(FormMath.tofixed(danJia,2));
+	}else return;
+	
+	var baoDiType =$("[name$=':bdxx']",curRow).val();
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	if(baoDiType =="0"){ //按月
+		var zongZujin = HyUtil.calMoneyWithMount(startDate,endDate,zuJin);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongZujin = zuJin*days;
+	}
+	zongZujin = FormMath.tofixed(zongZujin,2);
+	$("[name$=':zzj']",curRow).val(zongZujin).trigger("change");
+ }
+
+/*生成扣率组规则*/
+function createKoulvRules(){
+	return;
+	//租金标准信息
+	var zjbzxx = $(".listRow:visible",$("[tablename='me_zlzjbzxx']"));
+	var kouLvs = $(".listRow:visible",$("[tablename='me_zlklz']"));
+	var klzggRows =$(".listRow:visible",$("[tablename='me_zlklzgz']")); //扣率组规则
+	var klzggTableDiv = $("div[tablename='me_zlklzgg']");
+	if(zjbzxx.length==0 ||kouLvs.length==0){
+		$.ligerDialog.warn("租金标准信息或者扣率组尚未完善!",'请核查');return;
+	}
+	klzggRows.remove();
+	var rowIndex=0;
+	for(var j=0,kouLv;kouLv=kouLvs[j++];){
+		for(var i=0,row;row=zjbzxx[i++];){
+			var sjd = $("name$='sjd'",row).val();
+			var kouLvZu = $("name$='klzbh'",kouLv).val();
+			
+			FormUtil.addRow(klzggTableDiv);
+			var curRol = $(".listRow:visible",$("[tablename='me_zlklzgz']"))[rowIndex++];
+			$("name$='sjd'",curRol).val(sjd);$("name$='sjd'",curRol).trigger("change");
+			$("name$='lvz'",curRol).val(kouLvZu);$("name$='lvz'",curRol).trigger("change");
+		}
+	}
+}
+/*处理扣率时间段*/
+function handelKoulvTime(obj){
+	var curRow = $(obj).closest(".listRow");
+	var shijianNo = $("[name$=':sjd']",curRow).val();
+	if(!shijianNo) return ;
+	/*取的时间段*/
+	var shiJianDuanNo =$("[name='s:op_hdcdxy_zjbzxx:sjd'][value="+shijianNo+"]");
+	if(shiJianDuanNo.length ==0) {
+		$.ligerDialog.warn("该时间段不存在! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	var shiJianDuanRow = shiJianDuanNo.closest(".listRow");
+	var StartDate = $("[name$=':ksrq']",shiJianDuanRow).val();
+	var endDate = $("[name$=':jsrq']",shiJianDuanRow).val();
+	if(!StartDate || !endDate) {
+		$.ligerDialog.warn("改时间段信息不完善! “"+shijianNo+"”",'请核查');
+		$("[name$=':sjd']",curRow).val("");
+		return ;
+	}
+	
+	$("[name$=':ksrq']",curRow).val(StartDate);
+	$("[name$=':jsrq']",curRow).val(endDate);
+	
+	initXSJEQvalidateRule();
+}
+
+/*处理扣率组*/
+function handelKoulvGroup(obj){
+	var curRow = $(obj).closest(".listRow");
+	var koulvNo = $(obj).val();
+	if(!koulvNo) return ;
+	/*取扣率组*/
+	var KouLv =$("[name='s:op_hdcdxy_klz:klzbh'][value="+koulvNo+"]");
+	if(KouLv.length ==0) {
+		$.ligerDialog.warn("该扣率组不存在! “"+koulvNo+"”",'请核查');
+		$(obj).val("");
+		return ;
+	}
+	initXSJEQvalidateRule();
+}
+/*循环判断扣率组,初始化销售金额起,校验扣率组金额*/
+function initXSJEQvalidateRule(){ 
+	var kouLvRules = $(".listRow:visible",$("[tablename='op_hdcdxy_klzgz']"));
+	var combineMessage = [];
+	for(var i=0,rule;rule=kouLvRules[i++];){
+		var sjd = $("[name$=':sjd']",rule).val(); /*时间段*/
+		var klz = $("[name$=':klz']",rule).val();/*扣率组*/
+		if(!sjd || !klz) return;
+		 /*第一次设置开始金额为当前金额为第几行*/	
+		var len=-1;
+		for(var j=0,r;r=combineMessage[j++];){
+			if(r.split("-")[0] == sjd+","+klz){
+				len =Number(r.split("-")[1]);
+			}
+		}
+		if(len==-1){
+			$("[name$=':zqxsjeq']",rule).val("0");$("[name$=':zqxsjeq']",rule).trigger("change");
+		}else{
+			var JJJine = $("[name$=':zqxsjez']",$(kouLvRules[len])).val(); /*上一截止金额*/
+			$("[name$=':zqxsjeq']",rule).val(JJJine);
+		}
+		combineMessage.push(sjd+","+klz+"-"+(i-1));
+	}
+} 
+/*校验周期销售金额止*/
+function  checkZqxsjez(obj){
+	var curRow = $(obj).closest(".listRow");
+	 var JeQi = $("[name$=':zqxsjeq']",curRow).val();
+	 var jeZhi =$(obj).val();
+	 if(JeQi&&jeZhi)
+	 if( FormUtil.commaback(JeQi) > FormUtil.commaback(jeZhi)){
+		 $.ligerDialog.warn("周期销售金额止:"+jeZhi+  " 不应该小于 周期销售金额起 :"+JeQi+"",'请核查!');
+			$(obj).val("");
+			return ;
+	 }
+	 initXSJEQvalidateRule();
+}
+
+
+/*结算标准信息   删除行事件*/
+function op_hdcdxy_zjbzxxDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:me_lybdxx:xh'][value=" + xh + "]").closest("tr").remove();/*清除旧的记录*/
+}
+
+/*分解所有结算标准信息  */
+function decomposeAllJs(tableName,fenjieTable){
+	var $jsbzxxTrs = $(".listRow:visible",$("[tablename='"+tableName+"']"));   
+	$jsbzxxTrs.each(function (i) {
+		decomposeSingle($(this),fenjieTable);
+	});
+}
+
+function decomposeAllYzj(){
+	decomposeAllJs('op_hdcdxy_zjbzxx','op_hdcdxy_ydzjfj');
+}
+function decomposeSingleYzj(){
+	decomposeSingleJs('op_hdcdxy_zjbzxx','op_hdcdxy_ydzjfj');
+}
+
+/*单个分解*/
+function decomposeSingleJs(tableName,fenjieTable){
+	$jsbzxxTrs = $("input[name$=':fjsj']:checked",$("div[tablename='"+tableName+"']")).closest("tr");
+	if($jsbzxxTrs.length ==0){
+		$.ligerDialog.warn("请选择要分解的数据!","提示信息");
+		return;
+	}
+	//分解
+	decomposeSingle($jsbzxxTrs,fenjieTable);
+} 
+
+/*【通过分解目标行来分解信息,将结果输出至分解表】 
+ * 被选中的行:selectRow,分解表的表明fenJieTable*/
+function decomposeSingle(selectRow,fenJieTable){
+	var xh = selectRow.find("[name$=':xh']").val();	
+	if(!xh){
+		$.ligerDialog.warn("请选择要分解的时间段","提示");
+		return ;
+	}
+	//分解表
+	var fenJieTableDiv = $("div[tablename='"+fenJieTable+"']");
+	/*将序号与当前选中行所有分解信息删除*/
+	$("[name$=':xh'][value=" + xh + "]",fenJieTableDiv).closest("tr").remove(); /*清除旧的记录*/
+	
+	var sjd =$("[name$=':xh']",selectRow).val(); //序号
+	var ksrq =$("[name$=':ksrq']",selectRow).val();
+	var jsrq =$("[name$=':jsrq']",selectRow).val();
+	var type=$("[name$=':bdxx']",selectRow).val();
+	var zj =FormUtil.commaback($("[name$=':zj']",selectRow).val()); 
+	var sfgz =$("[name='m:op_hdcdxy:zjsfgzID']").val();
+
+	var jsonData =  HyUtil.decompose(sjd,ksrq,jsrq,zj,sfgz,"",type);
+	/*填充数据*/
+	for (var i = 0, c; c = jsonData[i++];) {
+		FormUtil.addRow(fenJieTableDiv);
+		var rowcount=$("input[name$=':ny']",fenJieTableDiv).length;
+		$($("input[name$=':xh']",fenJieTableDiv).get(rowcount-1)).val(xh);		
+		$($("input[name$=':ny']",fenJieTableDiv).get(rowcount-1)).val(c.ZQY);				
+		$($("input[name$=':ksrq']",fenJieTableDiv).get(rowcount-1)).val(c.KSRQ);			
+		$($("input[name$=':jsrq']",fenJieTableDiv).get(rowcount-1)).val(c.JSRQ);			
+		$($("input[name$=':je']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);	
+		$($("input[name$=':scrq']",fenJieTableDiv).get(rowcount-1)).val(c.CDRQ);	
+	}
+}
+
+/*显示分解信息列表 */
+function showFjsj(obj,fenJieTable){
+	var fenJieTableDiv =$("div[tablename='"+fenJieTable+"']");
+	var tableDiv = $(obj).closest("[tablename]");
+	var selectRow =$("input[name=':fjsj']:checked",tableDiv).closest("tr");
+	
+	var xhVal = selectRow.find("[name$=':xh']").val();/*选择行*/
+	
+	HyUtil.showDecompose(fenJieTable,'xh',xhVal);
+}
+
+/**分解代码 end**/
+

+ 673 - 0
web/js/haiya/form/op/zjkysq.js

@@ -0,0 +1,673 @@
+/***
+ * 专柜开业申请
+ */
+$(function(){
+	loadChangeSffs();
+	loadChangeJsgz();
+});
+/*校验子表填充数据是否重复**/ 
+function checkDataBeforeInsert(data,tableName){
+    if(tableName == 'op_zjkysq_qtzkft' || tableName == 'op_zjkysq_hykzkft'){
+      var rows =   $(".listRow",$("[tableName='"+tableName+"']"));  //左右子表列
+      for(var i =0,row;row=rows[i++];){
+        var id = $("[name='s:"+tableName+":ppID']",$(row)).val();  //唯一值
+        if(id ==data.PPBM) {                            //对话框,返回数据的那列
+     	   $.ligerDialog.warn(data.PPNAME+"已经存在了!","提示信息");
+             return false
+          }
+      }
+    }
+    
+    if(tableName == 'op_zjkysq_hyklx'){
+        var rows = $(".listRow",$("[tableName='"+tableName+"']"));  //左右子表列
+        for(var i =0,row;row=rows[i++];){
+          var id = $("[name='s:"+tableName+":hyklxID']",$(row)).val();  //唯一值
+            if(id == data.HYKTYPE) {                            //对话框,返回数据的那列
+      	   $.ligerDialog.warn(data.HYKNAME+"已经存在了!","提示信息");
+              return false
+           }
+        }
+      }
+    
+     return true;
+  }
+
+
+/*租赁时间变化事件**/
+function zlsjChangeEvent(){
+	validateZujinBiaozhunDates();
+	$("[name='s:op_zjkysq_klzgz:sjd']").trigger("change");
+	validateWyfDates();
+	validateMysfxm();
+	FormDate.doDateCalculate();
+}
+
+/* 计算起租日 */
+function calQizuRi(){
+	var shouPuRi = $("[name='m:op_zjkysq:sprq']").val();
+	var mianZuTian = $("[name='m:op_zjkysq:mzzxq']").val();
+	var kaiYeRi = $("[name='m:op_zjkysq:sjhtksrq']").val();
+	if(!mianZuTian||!kaiYeRi)return;
+	var showPuData = new Date(shouPuRi.replace(/\-/g, "/")); // 收铺日
+	var kaiYeDate = new Date(kaiYeRi.replace(/\-/g, "/")); // 开业日
+
+	showPuData = AddDays(showPuData,parseInt(mianZuTian)+1);
+	/* 开业日 小于收铺+免租,取开业 */
+	if(kaiYeDate.getTime()<= showPuData.getTime()){
+		$("[name='m:op_zjkysq:qzr']").val(kaiYeRi);
+	}else  $("[name='m:op_zjkysq:qzr']").val(showPuData.Format());
+}
+
+function AddDays(date,value)
+{
+    return new Date(Date.parse(date) + (86400000 * value));   
+}
+
+/* 【表结算标准信息】 添加行事件*/
+function op_zjkysq_jsbzxxAddRowAfterEvent(row){
+	var preRow = $(row).prev();
+	/*获取原结算标准最后一个序号*/
+	if(preRow.length==0 ||preRow.attr("style") == 'display: none;'){
+		preRow = $(".listRow:visible",$("[tablename='op_zjkysq_yjsbzxx']")).last();
+	}
+	var preNx = $("[name$=':xh']",preRow).val();
+	if(!preNx)preNx = 0;
+	$("[name$=':xh']",row).val(1+Number(preNx));
+	$("[name$='sjd']",row).val(1+Number(preNx));
+	validateZujinBiaozhunDates(row)
+	changeJsgz(row);
+};
+// 校验租金标准 时间
+function validateZujinBiaozhunDates(curRow){
+	//租赁有效期起,止 
+	var zuLinStartDate  =$("[name='m:op_zjkysq:sjhtksrq']").val();
+	var zuLinEndDate  =$("[name='m:op_zjkysq:sjhtjsrq']").val();
+	if((!zuLinStartDate || !zuLinStartDate) && curRow){  
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		$(curRow).remove();
+		return ;
+	}
+	
+	var rows = $(".listRow:visible",$("[tablename='op_zjkysq_jsbzxx']"));
+	for(var i=0,row;row=rows[i++];){
+		if(i>1){
+			//开始日期等于上个结束日期加1
+			var startDate = getNewDataStr($("[name$='jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$='ksrq']",$(row)).val(startDate);
+		}else{
+			var differTotalStartDate = FormDate.compareDate($("[name$='ksrq']",$(row)).val(), zuLinStartDate, "day");
+			if(differTotalEndDate>=0)  {
+				$.ligerDialog.warn("当前开始日期不能早于 租赁开始日期!",'请核查');
+			} 
+			$("[name$='ksrq']",$(row)).val(zuLinStartDate);
+		}
+		var differTotalEndDate = FormDate.compareDate($("[name$='jsrq']",$(row)).val(), zuLinEndDate, "day");
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("当前结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$='jsrq']",$(row)).val("")
+			return ;
+		}
+	}
+	$("[name='s:op_zjkysq_klzgz:sjd']").trigger("change");
+}
+
+/*加载时,改变【结算标准信息】的【结算规则】*/
+function loadChangeJsgz(){
+	var $jsgz = $("[name$=':jsgz']:visible",$("[tablename='op_zjkysq_jsbzxx']")); 
+	$jsgz.each(function (i) {
+		changeJsgz(this);
+	});
+}
+
+/*改变【结算标准信息】的【结算规则】*/
+function changeJsgz(obj){
+	var $tr = $(obj).closest("tr");
+	var jsgz = $("[name$=':jsgz']",$tr).val(); /*结算规则*/
+	console.log(jsgz);
+	 generateKlz(); // 产生扣率组
+	switch(jsgz){
+		case "1":		/*实销实结,产生一条扣率组,保底销售(只读)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).attr("readonly","readonly").val("");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly").val("");
+			 
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+			 //$("#xs_span").show();	/*保底销售*/
+			// $("#ml_span").hide();	/*保底毛利*/
+			 
+		 break;
+		case "2":		/*固定毛利额,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly").val("");
+			 
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+			 //$("#xs_span").show();	/*保底销售*/
+			// $("#ml_span").hide();	/*保底毛利*/
+			 
+		  break;
+		case "3":		/*有保底销售无目标销售,产生一条扣率组,保底销售(可编辑)、保底扣率(可编辑)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).removeAttr("readonly");	
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly").val("");
+			 
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+			 //$("#xs_span").show();	/*保底销售*/
+			 //$("#ml_span").hide();	/*保底毛利*/
+		  break;
+		case "4":		/*有保底毛利无目标毛利,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(只读)、超额目标扣率(只读)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':mbxs']",$tr).attr("readonly","readonly").val("");
+			 
+			 $("[name$=':cmbkl']",$tr).attr("readonly","readonly").val("");	
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+			 //$("#xs_span").hide();	/*保底销售*/
+			 //$("#ml_span").show();	/*保底毛利*/
+			break;
+		case "5":		/*有保底销售有目标销售,产生一条扣率组,保底毛利(可编辑)、保底扣率(可编辑)、目标销售(可编辑)、超额目标扣率(可编辑)、总保底(只读)、总目标(只读),分解为(保底销售)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");		
+			 $("[name$=':bdkl']",$tr).removeAttr("readonly");
+			 $("[name$=':mbxs']",$tr).removeAttr("readonly");
+			 
+			 $("[name$=':cmbkl']",$tr).removeAttr("readonly");	
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");	
+			 
+			 //$("#xs_span").show();	/*保底销售*/
+			 //$("#ml_span").hide();	/*保底毛利*/
+			break;
+		case "6":		/*有保底毛利有目标毛利,产生一条扣率组,保底毛利(可编辑)、保底扣率(只读)、目标销售(可编辑)、超额目标扣率(可编辑)、总保底(只读)、总目标(只读),分解为(保底毛利)*/
+			 $("[name$=':bdxs']",$tr).removeAttr("readonly");	
+			 $("[name$=':bdkl']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':mbxs']",$tr).removeAttr("readonly");
+			 
+			 $("[name$=':cmbkl']",$tr).removeAttr("readonly");
+			 $("[name$=':zbd']",$tr).attr("readonly","readonly").val("");
+			 $("[name$=':zmb']",$tr).attr("readonly","readonly").val("");
+			 
+			 //$("#xs_span").hide();	/*保底销售*/
+			 //$("#ml_span").show();	/*保底毛利*/
+			 
+			break;
+		default:
+	}
+}
+/*产生一条扣率组*/
+function generateKlz(){
+	if($(".listRow:visible",$("[tablename$='klz']")).length ==0){
+		FormUtil.addRow($('div[tablename$="klz"]'));  
+		var appendRow = $(".listRow:visible",$("[tablename$='klz']"))[0];
+		$("[name$='klzbh']",appendRow).val(1);
+		$("[name$='ms']",appendRow).val("扣组率1");
+	}
+}
+
+//单价计算总租金
+function calZongZuJin(obj){
+	var curRow = $(obj).closest(".listRow");
+	var danJia = FormUtil.commaback($("[name$='dj']",curRow).val()); // 单价
+	if(! danJia>0) return; 
+	
+	var mianJi = $("[name='m:op_zjkysq:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn(" 合同建筑面积不能为空!",'请核查');
+		return ;
+	}
+	var baoDiType =$("[name$='bdxx']",curRow).val();
+	var zuJin = mianJi * danJia;
+	$("[name$=':zj']",curRow).val(zuJin); $("[name$=':zj']",curRow).trigger("change");
+	
+	var startDate = $("[name$='ksrq']",curRow).val();
+	var endDate = $("[name$='jsrq']",curRow).val();
+	if(baoDiType =="0"){ //按月
+		var zongZujin = calMountZujin(startDate,endDate,zuJin);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongZujin = zuJin*days;
+	}
+	
+	
+	$("[name$='zzj']",curRow).val(zongZujin); $("[name$='zzj']",curRow).trigger("change");
+ }
+/*总租金。月度计算法*/
+function calMountZujin(startTime,endTime,zuJin){
+	startTime = startTime.replace(/\-/g, "/");
+	endTime = endTime.replace(/\-/g, "/");
+	var startDate = new Date(startTime); //开始时间
+	var endDate = new Date(endTime); //结束时间
+	
+	var num=0;
+	var year=endDate.getFullYear()-startDate.getFullYear();
+		num+=year*12;
+	var month=endDate.getMonth()-startDate.getMonth();
+		num+=month;
+		
+	var amount = zuJin * num; //月租金
+	var day=endDate.getDate()-startDate.getDate()+1;   //
+	amount = amount + day*zuJin/30  // 少于一月 减相差金额,多于一月 加多的金额
+	return amount;
+}
+
+
+/*生成扣率组规则*/
+function createKoulvRules(){
+	return;
+	//时间段
+	var rows = $(".listRow:visible",$("[tablename='op_zlzjbzxx']"));
+	var kouLvs = $(".listRow:visible",$("[tablename='op_zlklz']"));
+	for(var i=0,row;row=rows[i++];){
+		for(var j=0,kouLv;kouLv=kouLvs[j++];){
+
+		}
+	}
+}
+/*处理扣率时间段*/
+function handelKoulvTime(obj){
+	var curRow = $(obj).closest(".listRow");
+	var shijianNo = $("[name$='sjd']",curRow).val();
+	if(!shijianNo) return ;
+	/*取的时间段*/
+	var shiJianDuanNo =$("[name='s:op_zjkysq_jsbzxx:sjd'][value="+shijianNo+"]");
+	if(shiJianDuanNo.length ==0) {
+		$.ligerDialog.warn("该时间段不存在! “"+shijianNo+"”",'请核查');
+		$("[name$='sjd']",curRow).val("");
+		return ;
+	}
+	var shiJianDuanRow = shiJianDuanNo.closest(".listRow");
+	var StartDate = $("[name$='ksrq']",shiJianDuanRow).val();
+	var endDate = $("[name$='jsrq']",shiJianDuanRow).val();
+	if(!StartDate || !endDate) {
+		$.ligerDialog.warn("该时间段信息不完善! “"+shijianNo+"”",'请核查');
+		$("[name$='sjd']",curRow).val("");
+		return ;
+	}
+	
+	$("[name$='ksrq']",curRow).val(StartDate);
+	$("[name$='jsrq']",curRow).val(endDate);
+}
+
+/*处理扣率组*/
+function handelKoulvGroup(obj){
+	var curRow = $(obj).closest(".listRow");
+	var koulvNo = $(obj).val();
+	if(!koulvNo) return ;
+	/*取扣率组*/
+	var KouLv =$("[name='s:op_zjkysq_klz:klzbh'][value="+koulvNo+"]");
+	if(KouLv.length ==0) {
+		$.ligerDialog.warn("该扣率组不存在! “"+koulvNo+"”",'请核查');
+		$(obj).val("");
+		return ;
+	}
+}
+/*【物业费条款添加事件】op_zjkysq_wyftk*/
+function op_zjkysq_wyftkAddRowAfterEvent(curRow){
+	var preRow = $(curRow).prev();
+	var preNx = $("[name$='nx']",preRow).val();
+	if(!preNx) preNx=0;
+	$("[name$='nx']",curRow).val(1+Number(preNx));
+	$("[name$='xh']",curRow).val(1+Number(preNx));
+	
+	if(validateWyfDates() == false)$(curRow).remove();
+}
+
+/*校验物业费时间**/
+function validateWyfDates(){
+	//租赁有效期起
+	var zuLinStartDate  =$("[name='m:op_zjkysq:sjhtksrq']").val();
+	var zuLinEndDate  =$("[name='m:op_zjkysq:sjhtjsrq']").val();
+	if(!zuLinStartDate || !zuLinStartDate){ 
+		$.ligerDialog.warn("尚未输入租赁起止日期!",'请核查'); 
+		return false;
+	}
+	//循环计算开始日期
+	var rows = $(".listRow:visible",$("[tablename='op_zjkysq_wyftk']"));
+	for(var i=0,row;row=rows[i++];){
+		var shiJianDuan = $("[name$='sjd']",$(row)).val(i); //时间段
+		if(i>1){
+			//开始日期等于上个结束日期加1
+			var startDate = getNewDataStr($("[name$=':jsrq']",$(rows[i-2])).val(),1,1);
+			$("[name$=':ksrq']",$(row)).val(startDate);
+		}else{
+			$("[name$=':ksrq']",$(row)).val(zuLinStartDate);
+		}
+		//校验结束日期
+		var jsrq =$("[name$=':jsrq']",row).val();
+		if(!jsrq) continue;
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate, "day");
+		if(differTotalEndDate<0){
+			$.ligerDialog.warn("结束日期不能晚于租赁有效期止!",'请核查!');
+			$("[name$='jsrq']",curRow).val("")
+		}
+	}
+}
+/*总物业费**/
+function calZongWuYeFei(obj){
+	var curRow = $(obj).closest(".listRow"); 
+	//校验是否超过总日期
+	var zuLinEndDate =$("[name='m:op_zjkysq:sjhtjsrq']").val();
+	var differTotalEndDate = FormDate.compareDate($("[name$=':jsrq']",curRow).val(), zuLinEndDate, "day");
+	if(differTotalEndDate<0)  {
+		$.ligerDialog.warn("当前结束日期不能晚于租赁有效期止!",'请核查');
+		$("[name$='jsrq']",curRow).val("")
+		return ;
+	}
+	
+	var danJia = FormUtil.commaback($("[name$='dj']",curRow).val()); // 单价
+	if(! danJia>0) return; 
+	
+	var mianJi = $("[name='m:op_zjkysq:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var baoDiType =$("[name$='glfxx']",curRow).val();
+	var jine = mianJi * danJia;
+	$("[name$='je']",curRow).val(jine); $("[name$='je']",curRow).trigger("change");
+	
+	var startDate = $("[name$='ksrq']",curRow).val();
+	var endDate = $("[name$='jsrq']",curRow).val();
+	if(baoDiType =="0"){ //按月
+		var zongJine = calMountZujin(startDate,endDate,jine);
+	}else{
+		var days = FormDate.dateVal(startDate, endDate, "day");
+		var zongJine = jine*days;
+	}
+	$("[name$='zje']",curRow).val(zongJine);$("[name$='zje']",curRow).trigger("change");
+ }
+
+/*加载时,改变【每月收费项目】的【收费方式】*/
+function loadChangeSffs(){
+	var sffs = $("[name$='sffs']:visible",$("[tablename='op_zjkysq_mysfxm']"));
+	sffs.each(function (i) {
+		changeSffs(this);
+	});
+}
+
+/*改变【每月收费项目】的【收费方式】*/
+function changeSffs(obj){
+	var $tr = $(obj).closest("tr");
+	var sffs = $("[name$='sffs']",$tr).val(); /*收费方式*/
+	$("[name$='dj']",$tr).off();
+	switch(sffs){
+		case "0":		/*固定金额类型 : 只能录入收费金额*/
+		case "4":		
+			 $("[name$='dj']",$tr).attr("readonly","readonly").val("");
+			 $("[name$='sfje']",$tr).removeAttr("readonly");
+			 $("[name$='kl']",$tr).attr("readonly","readonly").val("");
+		  break;
+		case "1":		/*比率类型:只能录入比率*/
+		case "2":
+		case "5":
+			 $("[name$=':sfgz']",$tr).val("");
+			 $("[name$=':sfgzID']",$tr).val("");
+			$("[name$='dj']",$tr).attr("readonly","readonly").val("");
+			$("[name$='sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$='kl']",$tr).removeAttr("readonly");
+		  break;
+		case "3":		/*每平方米单价类型:只能录入单价*/
+			 $("[name$='dj']",$tr).removeAttr("readonly");
+			$("[name$='sfje']",$tr).attr("readonly","readonly").val("");
+			$("[name$='kl']",$tr).attr("readonly","readonly").val("");
+			//计算价格
+			 $("[name$='dj']",$tr).on("blur",function(){
+				 var dj =  FormUtil.commaback($(this).val()); 
+				 var curRow = $(this).closest(".listRow"); 
+				 var mianJi = $("[name='m:op_zjkysq:jzmj']").val();
+				 if(!mianJi) alert("尚未生成面积");
+				 $("[name$='sfje']",curRow).val(dj*mianJi);
+			 });
+		  break;
+		default:
+	}
+}
+
+/*校验每月收费项目*/
+function validateMysfxm (){
+	var zuLinStartDate  =$("[name='m:op_zjkysq:sjhtksrq']").val();
+	var zuLinEndDate  =$("[name='m:op_zjkysq:sjhtjsrq']").val();
+	
+	var sfxm = $(".listRow:visible",$("[tablename='op_zjkysq_mysfxm']"));
+	var sfxmArray =[];
+	 
+	for(var i=0,row;row=sfxm[i++];){
+		var ksrq = $("[name$='ksrq']",$(row)).val();
+		var jsrq = $("[name$='jsrq']",$(row)).val();
+		if(!ksrq) continue;
+		var differTotalStartDate = FormDate.compareDate(ksrq, zuLinStartDate, "day");
+		if(differTotalStartDate>0){
+			$.ligerDialog.warn("每月收费项目开始日期不能早于 租赁开始日期!",'请核查');
+			$("[name$='ksrq']",$(row)).val("")
+		}
+		
+		var differTotalEndDate = FormDate.compareDate(jsrq, zuLinEndDate, "day");
+		if(differTotalEndDate<0)  {
+			$.ligerDialog.warn("每月收费项目结束日期不能晚于 租赁有效期止!",'请核查');
+			$("[name$='jsrq']",$(row)).val("")
+		}
+		var curSfxmID =$("[name$='sfxmID']",$(row)).val();
+		if(curSfxmID){
+			for(var j=0,prevSfxm;prevSfxm=sfxmArray[j++];){
+				var thisSfxmId = prevSfxm.split("$")[0];
+				/*当前收费项目第二次出现*/
+				if(thisSfxmId == curSfxmID){
+					var differToPrevDate = FormDate.compareDate(ksrq,prevSfxm.split("$")[1], "day");
+					if(differToPrevDate>=0){
+						$.ligerDialog.warn("同一收费项目日期不得重复!",'请核查');
+						$("[name$='ksrq']",$(row)).val("");
+					}
+				}
+			  }
+			
+			sfxmArray.push(curSfxmID+"$"+jsrq);
+			}
+		}
+}
+
+function calBaodiMubiao(obj,targetInput){
+	var curRow = $(obj).closest(".listRow"); 
+	var baodi = FormUtil.commaback($(obj).val());
+	if(!baodi) return;
+	var mianJi = $("[name='m:op_zjkysq:jzmj']").val();
+	if(!mianJi){
+		$.ligerDialog.warn("建筑面积尚未计算生成!",'请核查');
+		return ;
+	}
+	var startDate = $("[name$=':ksrq']",curRow).val();
+	var endDate = $("[name$=':jsrq']",curRow).val();
+	var ZongJine = calMountZujin(startDate,endDate,baodi);
+	$("[name$='"+targetInput+"']",curRow).val(ZongJine).trigger("change");
+}
+
+
+/**分解代码 begin**/
+
+/*结算标准信息   删除行事件*/
+function op_zjkysq_zjbzxxDelRowBeforeEvent(row){
+	var xh = $("[name$=':xh']",row).val();
+	$("[name='s:op_zjkysq_bdxx:xh'][value=" + xh + "]").closest("tr").remove();/*清除旧的记录*/
+}
+
+/*分解所有结算标准信息  */
+function decomposeAllJs(tableName,fenjieTable){
+	var $jsbzxxTrs = $(".listRow:visible",$("[tablename='"+tableName+"']"));   
+	$jsbzxxTrs.each(function (i) {
+		decomposeSingle($(this),fenjieTable);
+	});
+	
+	var $jsbzxxTrs = $("input[name$=':fjsj']:checked",$("[tablename='"+tableName+"']")).closest("tr");
+	showFjsj($jsbzxxTrs,fenjieTable);
+}
+function decomposeAllWyf(){
+	decomposeAllJs('op_zjkysq_wyftk','op_zjkysq_wyffj');
+}
+function decomposeSingleWyf(){
+	decomposeSingleJs('op_zjkysq_wyftk','op_zjkysq_wyffj');
+}
+function decomposeAllBaodi(){
+	decomposeAllJs('op_zjkysq_jsbzxx','op_zjkysq_bdxx');
+}
+function decomposeSingleBaodi(){
+	decomposeSingleJs('op_zjkysq_jsbzxx','op_zjkysq_bdxx');
+}
+
+/*单个分解*/
+function decomposeSingleJs(tableName,fenjieTable){
+	$jsbzxxTrs = $("input[name$=':fjsj']:checked",$("div[tablename='"+tableName+"']")).closest("tr");
+	if($jsbzxxTrs.length ==0){
+		$.ligerDialog.warn("请选择要分解的数据!","提示信息");
+		return;
+	}
+	
+	//分解
+	decomposeSingle($jsbzxxTrs,fenjieTable);
+	showFjsj($jsbzxxTrs,fenjieTable);
+} 
+
+/*【通过分解目标行来分解信息,将结果输出至分解表】 
+ * 被选中的行:selectRow,分解表的表明fenJieTable*/
+function decomposeSingle(selectRow,fenJieTable){
+	var xh = selectRow.find("[name$=':xh']").val();	
+	if(!xh){
+		$.ligerDialog.warn("请选择要分解数据","提示");
+		return ;
+	}
+	//分解表
+	var fenJieTableDiv = $("div[tablename='"+fenJieTable+"']");
+	/*将序号与当前选中行所有分解信息删除*/
+	$("[name$=':xh'][value=" + xh + "]",fenJieTableDiv).closest("tr").remove(); /*清除旧的记录*/
+	
+	var sjd =$("[name$='xh']",selectRow).val(); //序号
+	var ksrq =$("[name$='ksrq']",selectRow).val();
+	var jsrq =$("[name$='jsrq']",selectRow).val();
+	var yzj,mbxs,type=0;
+	//物业费
+	if(fenJieTable == 'op_zjkysq_wyffj'){
+		yzj =FormUtil.commaback($("[name$='je']",selectRow).val());
+		type=$("[name$='glfxx']",selectRow).val();
+	//标准租金
+	}else{
+		yzj=FormUtil.commaback($("[name$='bdxs']",selectRow).val()); 
+		mbxs =FormUtil.commaback($("[name$='mbxs']",selectRow).val()); 
+	}
+	var jsonData = decompose(sjd,ksrq,jsrq,yzj,"",mbxs,type);
+	/*填充数据*/
+	for (var i = 0, c; c = jsonData[i++];) {
+		FormUtil.addRow(fenJieTableDiv);
+		var rowcount=$("input[name$=':ny']",fenJieTableDiv).length;
+		$($("input[name$=':xh']",fenJieTableDiv).get(rowcount-1)).val(xh);		
+		$($("input[name$=':ny']",fenJieTableDiv).get(rowcount-1)).val(c.ZQY);				
+		$($("input[name$=':ksrq']",fenJieTableDiv).get(rowcount-1)).val(c.KSRQ);			
+		$($("input[name$=':jsrq']",fenJieTableDiv).get(rowcount-1)).val(c.JSRQ);			
+		if(fenJieTable == 'op_zjkysq_wyffj'){
+			$($("input[name$=':je']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);	
+		}else{
+			$($("input[name$=':bdxsml']",fenJieTableDiv).get(rowcount-1)).val(c.YZJ);	
+			$($("input[name$=':mbxsml']",fenJieTableDiv).get(rowcount-1)).val(c.MBXS);
+		}
+	}
+	
+}
+
+/*分解数据*/
+function decompose(sjd,ksrq,jsrq,zj,gzid,mbxs,type){
+	if(!zj) zj =0;
+	if(!type) type=0;
+	var paramJson = {jlbh:sjd,ksrq:ksrq,jsrq:jsrq,yzj:zj,zjjsbz:type};
+	if(mbxs) paramJson.mbxs =mbxs;
+	if(gzid) paramJson.gzid =gzid;
+	var jsonParams = [];
+	jsonParams.push(paramJson);
+	var conf = {aliasName:'decompose',paramJson:JSON.stringify(jsonParams)};
+	var json = RunAliasScript(conf); 
+    if(json.isSuccess==0){
+    	return JSON.parse(json.result);
+	 }else{
+		 $.ligerDialog.error("分解失败:"+json.result,"提示信息");
+		 return [];
+	 }
+}
+
+/*显示分解信息列表 */
+function showFjsj(obj,fenJieTable){
+	var fenJieTableDiv =$("div[tablename='"+fenJieTable+"']");
+	var tableDiv = $(obj).closest("[tablename]");
+	var selectRow =$("input[name=':fjsj']:checked",tableDiv).closest("tr");
+	
+	var xh = selectRow.find("[name$=':xh']").val();/*选择行*/
+	
+	$(".listRow:visible",fenJieTableDiv).hide();	/*先隐藏全部,在显示个体*/
+	$("[name$=':xh'][value=" + xh + "]",fenJieTableDiv).closest("tr").show(); 
+	
+	$("[name$=':zj']").trigger("blur");
+}
+
+
+/**分解代码 end**/
+
+/**日期加上多少天     type: m=2/d=1/y=3   number:几天 */
+function getNewDataStr(curDateStr,number,type){
+	if(!curDateStr) return "";
+	curDateStr = curDateStr.replace(/\-/g, "/");
+	var curDate =  new Date(curDateStr);
+	if(type==1) curDate.setDate(curDate.getDate()+number); 
+	if(type==2) curDate.setMonth(curDate.getMonth()+number);
+	if(type==3) curDate.setFullYear(curDate.getFullYear()+number); 
+
+	var y=curDate.getFullYear();
+	var m=curDate.getMonth()+1;
+	var d=curDate.getDate();
+	 
+	if(m<=9)m="0"+m; if(d<=9)d="0"+d;
+	var cdate=y+"-"+m+"-"+d;
+	return cdate; //开始时间
+} 
+
+
+/*格式化填充的日期*/
+$().ready(function (){
+	$("[datefmt]").live('change',function (){
+		var me = $(this),val = me.val();
+		if(!$.isEmpty(val)){
+			var arry = val.split(".");
+			if(arry[0].indexOf("-") == -1) return; 
+			var sTime = arry[0].replace(/\-/g, "/");
+			var datefmt = me.attr("datefmt");
+			var nowDate = new Date(sTime).Format(datefmt);
+			me.val(nowDate);
+		}
+	});
+	
+	$("[issameas]").change(function(){
+		var me = $(this),curVal = me.val();
+		var curName = me.attr("name");
+		if(!curName) return;
+		curName = curName.split(":");
+		var targetName = me.attr("isSameAs");
+		if(targetName=='true' && curName.length == 3){
+			targetName =curName[0] +":"+curName[1] +":y"+curName[2]
+		}
+		 
+		var targetValue = $("[name='"+targetName+"']").val();
+		if(targetValue != curVal){
+			me.css("color","red");
+		}else{
+			me.css("color","");
+		}
+	});
+	
+});
+
+
+

Разница между файлами не показана из-за своего большого размера
+ 1702 - 0
web/js/haiya/oa/gzjhkhFunc.js


+ 30 - 0
web/js/haiya/oa/hyVoteResult.js

@@ -0,0 +1,30 @@
+
+/**
+ * 查看投票结果
+ * @param voteId
+ */
+function showResult(voteId){
+	var url	= __ctx+"/produce/oa/hyVoteResult/calResult.ht?voteId="+voteId;
+	url=url.getNewUrl();
+	DialogUtil.open({
+		url:url,
+		title:"查看投票结果",
+		height:'600',
+		width:'900'
+	});
+}
+
+/**
+ * 查看投票结果明细
+ * @param voteObjId
+ */
+function showResultDetail(voteObjId){
+	var url	= __ctx+"/produce/oa/hyVoteResult/list.ht?voteObjId="+voteObjId;
+	url=url.getNewUrl();
+	DialogUtil.open({
+		url:url,
+		title:"查看明细",
+		height:'600',
+		width:'900'
+	});
+}

+ 52 - 0
web/js/haiya/oa/sysquestionnaire/SysAnswerController.js

@@ -0,0 +1,52 @@
+var sysAnswerApp = angular.module('SysAnswerApp', [ 'baseServices','SysAnswerServiceApp' ]);
+
+sysAnswerApp.controller('SysAnswerCtrl',['$scope','BaseService','SysAnswerService','$timeout',function($scope,BaseService,sysAnswerService,$timeout){
+	var service = sysAnswerService;
+	
+	if(typeof sysAnswer != 'undefined')
+		$scope.sysAnswer  = sysAnswer||{};	// 问卷赋值
+	
+	if(typeof answerDetailMap != 'undefined')
+		$scope.answerDetailMap  = answerDetailMap||{};	// 问卷赋值
+		
+	$scope.save = function(){
+		$.ligerDialog.confirm("提交问卷后,将不能再次修改,是否确定提交?","提示信息",function(rtn) {
+			if (!rtn) 
+				return;
+			var answerDetailMap = $scope.answerDetailMap;
+			if($("#saveData").hasClass("disabled"))
+				return ;
+			var form = $('#sysAnswerForm').form();
+			if (!form.valid()){
+				$.ligerDialog.warn("验证不成功!");
+				return;
+			}
+			
+			var arry = new Array();
+			for(var i in answerDetailMap){
+				var answerDetail = answerDetailMap[i];
+				if(answerDetail.textResult != null 
+						&& answerDetail.textResult != ""
+							&& answerDetail.optionId == -1){
+					arry.push(answerDetail);
+				}
+				else if(answerDetail.optionId != 0){
+					arry.push(answerDetail);
+				}
+			}
+			
+			sysAnswer.sysAnswerDetailList = arry;
+			
+			$("#saveData").addClass("disabled");
+			service.customFormSubmit(sysAnswer);
+		});
+	};
+	$scope.validMaxNum = function(questionId,maxNum){
+		service.validMaxNum(questionId,maxNum);
+	};
+
+}]);
+
+
+
+

+ 355 - 0
web/js/haiya/utils/HyUtil.js

@@ -0,0 +1,355 @@
+if (typeof HyUtil == 'undefined') {
+	HyUtil = {};
+};
+
+$().ready(function() {
+	HyUtil.init();
+});
+
+HyUtil.init = function() {
+	HyUtil.formatDate();
+};
+
+/**
+ * 绑定日期格式化
+ */
+HyUtil.formatDate = function() {
+	$("[datefmt]").live('change', function() {
+		var me = $(this), val = me.val();
+		if (!$.isEmpty(val)) {
+			
+			var datefmt = me.attr("datefmt");
+			var arry = val.split(".");
+			var sTime = arry[0].replace(/\-/g, "/");
+			if(datefmt == "yyyyMM" ){
+				sTime = sTime.substr(0,4) + "/" + sTime.substr(4,5) + "/01";
+			}
+			var nowDate = new Date(sTime).Format(datefmt);
+			me.val(nowDate);
+		}
+	});
+};
+
+
+/**
+ * 获取某年某月的天数
+ */
+HyUtil.getDaysInMonth = function(year, month) {
+	month = parseInt(month, 10);
+	var temp = new Date(year, month, "0");
+	var day = temp.getDate();
+	return temp.getDate();
+};
+
+/**
+ * 计算时间段内的总金额,头一月和尾一月独立计算
+ */
+HyUtil.calMoneyWithMount = function(startTime, endTime, money) {
+	startTime = startTime.replace(/\-/g, "/");
+	endTime = endTime.replace(/\-/g, "/");
+	var startDate = new Date(startTime); // 开始时间
+	var endDate = new Date(endTime); // 结束时间
+
+	var startDay = HyUtil.getDaysInMonth(startDate.getFullYear(), startDate
+			.getMonth() + 1); // 获取开始年月拥有多少天数
+	var endDay = HyUtil.getDaysInMonth(endDate.getFullYear(), endDate
+			.getMonth() + 1); // 获取结束年月拥有多少天数
+
+	var sDay = startDay - startDate.getDate() + 1; // 第一月有多少天
+	var eDay = endDate.getDate(); // 最后一月有多少天
+	var num = 0;
+	var year = endDate.getFullYear() - startDate.getFullYear();
+	num += year * 12;
+	var month = endDate.getMonth() - startDate.getMonth();
+	num += month;
+	var amountAll = 0; // 总租金
+	var amountTemp = 0; // 中间月租金
+	var amountFirst = 0;// 头月租金
+	var amountLast = 0; // 尾月租金
+	if (num >= 2) {
+		amountTemp = money * (num - 1); // 月租金
+		amountFirst = sDay * money / startDay;
+		amountLast = eDay * money / endDay;
+	}
+	if (num == 1) {
+		amountFirst = sDay * money / startDay;
+		amountLast = eDay * money / endDay;
+	}
+	if (num < 1) {
+		sDay = endDate.getDate() - startDate.getDate() + 1;
+		amountFirst = sDay * money / startDay;
+	}
+	var amountAll = Number(amountTemp.toFixed(2))
+			+ Number(amountFirst.toFixed(2)) + Number(amountLast.toFixed(2)); // 总租金
+	return amountAll.toFixed(2);
+};
+
+/**
+ * 显示租户信息
+ */
+HyUtil.showZh = function(zhid) {
+	if (zhid == null || zhid == "") {
+		$.ligerDialog.warn("请先选择租户!", "提示");
+		return;
+	}
+	var url = __ctx
+			+ "/platform/form/bpmDataTemplate/detailData_shxxwh.ht?__pk__="
+			+ zhid;
+	DialogUtil.open({
+		height : 600,
+		width : 800,
+		title : "查看租户信息",
+		url : url,
+		isResize : true
+	});
+};
+
+/**
+ * 显示品牌信息
+ */
+HyUtil.showPP = function(ppId) {
+	if (!ppId) {
+		$.ligerDialog.warn("请先选择品牌!", "提示");
+		return;
+	}
+	var url = __ctx
+			+ "/platform/form/bpmDataTemplate/detailData_ppxxdj.ht?__pk__="
+			+ ppId;
+	DialogUtil.open({
+		height : 600,
+		width : 800,
+		title : "查看品牌信息",
+		url : url,
+		isResize : true
+	});
+};
+
+/**
+ * 显示店铺明细 
+ */
+HyUtil.showDpmx = function (pwID){
+	if(pwID == null || pwID == ""){
+		$.ligerDialog.warn("请先选择店铺!","提示");
+		return ;
+	}
+	var url=__ctx + "/produce/report/DPMX.ht?op=write&WLDPID="+pwID;
+	DialogUtil.open({
+		height:600,
+		width: 800,
+		title : "查看店铺信息",
+		url: url, 
+		isResize: true
+	});
+};
+
+/**
+ * 验证合同号是否唯一
+ */
+HyUtil.validateHth = function() {
+	/*
+	 * $("input[name='m:me_zlhtqd:hth']").change(function(){ var hth =
+	 * $(this).val(); if(!hth) return; if(hth.length != 9){
+	 * $.ligerDialog.warn("合同号为:三位门店号 + 三位楼层号 + 三位本楼层流水","提示"); return; } var
+	 * conf = {aliasName:'validateHTHisExist',hth:hth}; var result =
+	 * RunAliasScript(conf); if(result.result === 0) return;
+	 * 
+	 * hth = hth.substring(0,6); if(result.isSuccess == 1)
+	 * $.ligerDialog.error(result.msg,"哎呦出错了!"); else
+	 * $.ligerDialog.error("该合同号已经存在!","提示");
+	 * 
+	 * $(this).val(hth); });
+	 */
+};
+
+/**
+ * 与目标数据大小进行比较,只读取 name+_td ,非只读去input $:+name
+ * 
+ * @param currentRow
+ *            当前行
+ * @param tragetRow
+ *            目标行
+ * @param name
+ *            名字
+ */
+HyUtil.fillColorByCompare = function(currentRow, tragetRow, name) {
+	var curObj = $("[name$=':" + name + "']", currentRow);
+	if (curObj.length == 0) { /* 只读状态 */
+		curObj = $("[name='" + name + "_td']", currentRow);
+		var targetVal = FormUtil.commaback($("[name='" + name + "_td']",
+				tragetRow).text().trim());
+		var currentVal = FormUtil.commaback(curObj.text().trim());
+	} else {
+		var targetVal = FormUtil.commaback($("[name$=':" + name + "']",
+				tragetRow).val()); // 比较对象金额
+		var currentVal = FormUtil.commaback(curObj.val());
+		if (!targetVal || !currentVal) {
+			return; // 对象为0不再计算
+		}
+	}
+	if (currentVal > targetVal) {
+		curObj.css("color", "red");
+	} else if (currentVal < targetVal) {
+		curObj.css("color", "green");
+	} else {
+		curObj.css("color", "");
+	}
+};
+
+/**
+ * 日期加上多少天 type: m=2/d=1/y=3 number:几天
+ */
+HyUtil.getNewDataStr = function(curDateStr, number, type) {
+	if (!curDateStr)
+		return "";
+	curDateStr = curDateStr.replace(/\-/g, "/");
+	var curDate = new Date(curDateStr);
+	if (type == 1)
+		curDate.setDate(curDate.getDate() + number);
+	if (type == 2)
+		curDate.setMonth(curDate.getMonth() + number);
+	if (type == 3)
+		curDate.setFullYear(curDate.getFullYear() + number);
+
+	var y = curDate.getFullYear();
+	var m = curDate.getMonth() + 1;
+	var d = curDate.getDate();
+
+	if (m <= 9)
+		m = "0" + m;
+	if (d <= 9)
+		d = "0" + d;
+	var cdate = y + "-" + m + "-" + d;
+	return cdate; // 开始时间
+};
+
+/**
+ * 分解
+ */
+HyUtil.decompose = function(sjd, ksrq, jsrq, zj, gzid, mbxs, type) {
+	if(!zj) 
+		zj =0;
+	if (!type)
+		type = 0;
+	var paramJson = {
+		jlbh : sjd,
+		ksrq : ksrq,
+		jsrq : jsrq,
+		yzj : zj,
+		zjjsbz : type
+	};
+	if (mbxs)
+		paramJson.mbxs = mbxs;
+	if (gzid)
+		paramJson.gzid = gzid;
+	var jsonParams = [];
+	jsonParams.push(paramJson);
+	var conf = {
+		aliasName : 'decompose',
+		paramJson : JSON.stringify(jsonParams)
+	};
+	var json = RunAliasScript(conf);
+	if (json.isSuccess == 0) {
+		return JSON.parse(json.result);
+	} else {
+		$.ligerDialog.error("分解失败:" + json.msg, "提示信息");
+		return [];
+	}
+};
+
+/**
+ * 显示分解,
+ * <div type="subtable" tabledesc="月度租金分解" tablename="me_zlhtqd_ydzjfj">
+ * <table class="listTable" id="me_zlhtqd_ydzjfj">
+ *	一定要这样子设置
+ */
+HyUtil.showDecompose = function(tableName,xh,xhVal){
+	var $tableDiv =$("div[tablename='"+tableName+"']");
+	var $tableName = $("#"+tableName);
+	$tableDiv.show();
+	$(".listRow:visible",$tableDiv).hide();	/*先隐藏全部,在显示个体*/
+	var $tr = $("[name$=':"+xh+"'][value=" + xhVal + "]",$tableDiv).closest("tr");
+	$tr.show(); 
+	handRowEvent(null,$tr);//	重新计算序号
+	
+	$.ligerDialog.open({ 
+		width: 900,
+		height:500,
+		title: '查看分解信息', 
+		isResize:true,
+		showMax : false,
+		showToggle : true,
+		allowClose : false,
+		isDrag : false,
+		modal:true,
+		target:$tableName,
+		buttons: [{ text: '确定', onclick:function(item,dialog){
+			$tableDiv.append($tableName);
+			$tableDiv.hide();
+			$.ligerDialog.hide();
+			$.ligerDialog.close();
+		}}]
+	});
+};
+
+/**
+ * 根据【付款天数】获取【联营结算日】,
+ * 付款天数为5时、结算日为:5、10、15、20、25;付款天数为10时,结算日为:10、20;
+ * 付款天数为20时,结算日为20;付款天数等于大于30时,结算日为空;
+ */
+HyUtil.getJsrByFkts = function(fkts){
+	var rtVal = "";
+	switch(fkts){
+		case "5":
+			rtVal = "5、10、15、20、25";
+			break;
+		case "10":
+			rtVal = "10、20";
+			break;
+		case "20":
+			rtVal = "20";
+			break;
+		default:
+	}
+	return rtVal;
+}
+
+/**
+ * 禁止右键
+ */
+HyUtil.unbindContextMenuEvent = function (tableName){
+	$("div[tablename='"+tableName+"']").find("tr[class='listRow']").unbind("contextmenu");
+}
+
+
+/** 
+ * 将数值四舍五入后格式化. 
+ * 
+ * @param num 数值(Number或者String) 
+ * @param cent 要保留的小数位(Number) 
+ * @return 格式的字符串,如'1,234,567.45' 
+ * @type String 
+ */  
+HyUtil.commafy = function (num,cent){
+	num = num.toString().replace(/\$|\,/g,'');  
+    // 检查传入数值为数值类型
+      if(isNaN(num))  
+        num = "0";  
+    // 获取符号(正/负数)
+    sign = (num == (num = Math.abs(num)));  
+    num = Math.floor(num*Math.pow(10,cent)+0.50000000001);  // 把指定的小数位先转换成整数.多余的小数位四舍五入
+    cents = num%Math.pow(10,cent);              // 求出小数位数值
+    num = Math.floor(num/Math.pow(10,cent)).toString();   // 求出整数位数值
+    cents = cents.toString();               // 把小数位转换成字符串,以便求小数位长度
+    // 补足小数位到指定的位数
+    while(cents.length<cent)  
+      cents = "0" + cents;  
+      // 对整数部分进行千分位格式化.
+    for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)  
+    	num = num.substring(0,num.length-(4*i+3))+','+ num.substring(num.length-(4*i+3));  
+    if (cent > 0)  
+      return (((sign)?'':'-') + num + '.' + cents);  
+    else  
+      return (((sign)?'':'-') + num);  
+}
+
+

+ 163 - 0
web/js/haiya/utils/TableFix.js

@@ -0,0 +1,163 @@
+/**
+ * 
+ * 固定普通table的行和列
+ *
+ * 
+ */
+if (typeof TableFix == 'undefined') {
+	TableFix = {};
+};
+
+/**
+ * 固定表头和列([tablefix='headRow']表示平台中的表头)
+ * 
+ * @param tableId
+ * @param fixColNum	需要固定的列数
+ * @param width
+ * @param height
+ */
+TableFix.fix = function(tableId, fixColNum, width, height) {
+	if ($("#" + tableId + "_tableLayout").length != 0) {
+		$("#" + tableId + "_tableLayout").before($("#" + tableId));
+		$("#" + tableId + "_tableLayout").empty();
+	} else {
+		$("#" + tableId).after("<div id='" + tableId + "_tableLayout' style='overflow:hidden;height:" 
+				+ height + "px; width:" + width + "px;'></div>");
+	}
+	$('<div id="' + tableId + '_tableFix"></div>'
+			+ '<div id="' + tableId + '_tableHead"></div>'
+			+ '<div id="' + tableId + '_tableColumn"></div>'
+			+ '<div id="' + tableId + '_tableData"></div>')
+			.appendTo("#" + tableId + "_tableLayout");
+	var oldtable = $("#" + tableId);
+	var tableFixClone = oldtable.clone(true);
+	tableFixClone.attr("id", tableId + "_tableFixClone");
+	$("#" + tableId + "_tableFix").append(tableFixClone);
+	var tableHeadClone = oldtable.clone(true);
+	tableHeadClone.attr("id", tableId + "_tableHeadClone");
+	$("#" + tableId + "_tableHead").append(tableHeadClone);
+	var tableColumnClone = oldtable.clone(true);
+	tableColumnClone.attr("id", tableId + "_tableColumnClone");
+	$("#" + tableId + "_tableColumn").append(tableColumnClone);
+	$("#" + tableId + "_tableData").append(oldtable);
+	$("#" + tableId + "_tableLayout table").each(function() {
+		$(this).css("margin", "0");
+	});
+	
+	var headHeight = 0;
+	$("#" + tableId + "_tableHead").find("[tablefix='headRow']").each(function (){
+		headHeight = headHeight + $(this).height();
+	});
+	$("#" + tableId + "_tableHead").css("height", headHeight);
+	$("#" + tableId + "_tableFix").css("height", headHeight);
+	var ColumnsWidth = 0;
+	var ColumnsNumber = 0;
+	$("#" + tableId + "_tableColumn tr:last td:lt(" + fixColNum + ")").each(function() {
+			ColumnsWidth += $(this).outerWidth(true);
+			ColumnsNumber++;
+		});
+	ColumnsWidth += 2;
+	if ($.browser.msie) {
+		switch ($.browser.version) {
+		case "7.0":
+			if (ColumnsNumber >= 3)
+				ColumnsWidth--;
+			break;
+		case "8.0":
+			if (ColumnsNumber >= 2)
+				ColumnsWidth--;
+			break;
+		}
+	}
+	$("#" + tableId + "_tableColumn").css("width", ColumnsWidth);
+	$("#" + tableId + "_tableFix").css("width", ColumnsWidth);
+	$("#" + tableId + "_tableData").scroll(
+			function() {
+				$("#" + tableId + "_tableHead").scrollLeft(
+						$("#" + tableId + "_tableData").scrollLeft());
+				$("#" + tableId + "_tableColumn").scrollTop(
+						$("#" + tableId + "_tableData").scrollTop());
+			});
+	$("#" + tableId + "_tableFix").css({
+		"overflow" : "hidden",
+		"position" : "relative",
+		"z-index" : "50",
+		"background-color" : "Silver"
+	});
+	$("#" + tableId + "_tableHead").css({
+		"overflow" : "hidden",
+		"width" : width - 17,
+		"position" : "relative",
+		"z-index" : "45",
+		"background-color" : "Silver"
+	});
+	$("#" + tableId + "_tableColumn").css({
+		"overflow" : "hidden",
+		"height" : height - 17,
+		"position" : "relative",
+		"z-index" : "40",
+		"background-color" : "Silver"
+	});
+	$("#" + tableId + "_tableData").css({
+		"overflow" : "auto",
+		"width" : width,
+		"height" : height,
+		"position" : "relative",
+		"z-index" : "35"
+	});
+	if ($("#" + tableId + "_tableHead").width() > $(
+			"#" + tableId + "_tableFix table").width()) {
+		$("#" + tableId + "_tableHead").css("width",
+				$("#" + tableId + "_tableFix table").width());
+		$("#" + tableId + "_tableData").css("width",
+				$("#" + tableId + "_tableFix table").width() + 17);
+	}
+	if ($("#" + tableId + "_tableColumn").height() > $(
+			"#" + tableId + "_tableColumn table").height()) {
+		$("#" + tableId + "_tableColumn").css("height",
+				$("#" + tableId + "_tableColumn table").height());
+		$("#" + tableId + "_tableData").css("height",
+				$("#" + tableId + "_tableColumn table").height() + 17);
+	}
+	$("#" + tableId + "_tableFix").offset(
+			$("#" + tableId + "_tableLayout").offset());
+	$("#" + tableId + "_tableHead").offset(
+			$("#" + tableId + "_tableLayout").offset());
+	$("#" + tableId + "_tableColumn").offset(
+			$("#" + tableId + "_tableLayout").offset());
+	$("#" + tableId + "_tableData").offset(
+			$("#" + tableId + "_tableLayout").offset());
+	
+	
+	$("#" + tableId + "_tableFixClone").find("[name]").removeAttr("name");
+	$("#" + tableId + "_tableHeadClone").find("[name]").removeAttr("name");
+	$("#" + tableId + "_tableColumnClone").find("[name]").removeAttr("name");
+
+	$("#" + tableId + "_tableFixClone").find("[formtype]").removeAttr("formtype");
+	$("#" + tableId + "_tableHeadClone").find("[formtype]").removeAttr("formtype");
+	$("#" + tableId + "_tableColumnClone").find("[formtype]").removeAttr("formtype");
+	
+}
+
+/**
+ * 还原改变后的表
+ * 
+ * @param tableId
+ */
+TableFix.restore = function(tableId) {
+	var table = $("#" + tableId);
+	var tableClone = table.clone(true);
+	$("#" + tableId + "_tableLayout").after(tableClone);
+	$("#" + tableId + "_tableLayout").remove();
+}
+
+
+
+
+
+
+
+
+
+
+

Разница между файлами не показана из-за своего большого размера
+ 1438 - 0
web/js/jqplot/excanvas.js


+ 259 - 0
web/js/jqplot/jquery.jqplot.css

@@ -0,0 +1,259 @@
+/*rules for the plot target div.  These will be cascaded down to all plot elements according to css rules*/
+.jqplot-target {
+    position: relative;
+    color: #666666;
+    font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
+    font-size: 1em;
+/*    height: 300px;
+    width: 400px;*/
+}
+
+/*rules applied to all axes*/
+.jqplot-axis {
+    font-size: 0.75em;
+}
+
+.jqplot-xaxis {
+    margin-top: 10px;
+}
+
+.jqplot-x2axis {
+    margin-bottom: 10px;
+}
+
+.jqplot-yaxis {
+    margin-right: 10px;
+}
+
+.jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis, .jqplot-yMidAxis {
+    margin-left: 10px;
+    margin-right: 10px;
+}
+
+/*rules applied to all axis tick divs*/
+.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick, .jqplot-yMidAxis-tick {
+    position: absolute;
+    white-space: pre;
+}
+
+
+.jqplot-xaxis-tick {
+    top: 0px;
+    /* initial position untill tick is drawn in proper place */
+    left: 15px;
+/*    padding-top: 10px;*/
+    vertical-align: top;
+}
+
+.jqplot-x2axis-tick {
+    bottom: 0px;
+    /* initial position untill tick is drawn in proper place */
+    left: 15px;
+/*    padding-bottom: 10px;*/
+    vertical-align: bottom;
+}
+
+.jqplot-yaxis-tick {
+    right: 0px;
+    /* initial position untill tick is drawn in proper place */
+    top: 15px;
+/*    padding-right: 10px;*/
+    text-align: right;
+}
+
+.jqplot-yaxis-tick.jqplot-breakTick {
+	right: -20px;
+	margin-right: 0px;
+	padding:1px 5px 1px 5px;
+/*	background-color: white;*/
+	z-index: 2;
+	font-size: 1.5em;
+}
+
+.jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick {
+    left: 0px;
+    /* initial position untill tick is drawn in proper place */
+    top: 15px;
+/*    padding-left: 10px;*/
+/*    padding-right: 15px;*/
+    text-align: left;
+}
+
+.jqplot-yMidAxis-tick {
+    text-align: center;
+    white-space: nowrap;
+}
+
+.jqplot-xaxis-label {
+    margin-top: 10px;
+    font-size: 11pt;
+    position: absolute;
+}
+
+.jqplot-x2axis-label {
+    margin-bottom: 10px;
+    font-size: 11pt;
+    position: absolute;
+}
+
+.jqplot-yaxis-label {
+    margin-right: 10px;
+/*    text-align: center;*/
+    font-size: 11pt;
+    position: absolute;
+}
+
+.jqplot-yMidAxis-label {
+    font-size: 11pt;
+    position: absolute;
+}
+
+.jqplot-y2axis-label, .jqplot-y3axis-label, .jqplot-y4axis-label, .jqplot-y5axis-label, .jqplot-y6axis-label, .jqplot-y7axis-label, .jqplot-y8axis-label, .jqplot-y9axis-label {
+/*    text-align: center;*/
+    font-size: 11pt;
+    margin-left: 10px;
+    position: absolute;
+}
+
+.jqplot-meterGauge-tick {
+    font-size: 0.75em;
+    color: #999999;
+}
+
+.jqplot-meterGauge-label {
+    font-size: 1em;
+    color: #999999;
+}
+
+table.jqplot-table-legend {
+    margin-top: 12px;
+    margin-bottom: 12px;
+    margin-left: 12px;
+    margin-right: 12px;
+}
+
+table.jqplot-table-legend, table.jqplot-cursor-legend {
+    background-color: rgba(255,255,255,0.6);
+    border: 1px solid #cccccc;
+    position: absolute;
+    font-size: 0.75em;
+}
+
+td.jqplot-table-legend {
+    vertical-align:middle;
+}
+
+/*
+These rules could be used instead of assigning
+element styles and relying on js object properties.
+*/
+
+/*
+td.jqplot-table-legend-swatch {
+    padding-top: 0.5em;
+    text-align: center;
+}
+
+tr.jqplot-table-legend:first td.jqplot-table-legend-swatch {
+    padding-top: 0px;
+}
+*/
+
+td.jqplot-seriesToggle:hover, td.jqplot-seriesToggle:active {
+    cursor: pointer;
+}
+
+.jqplot-table-legend .jqplot-series-hidden {
+    text-decoration: line-through;
+}
+
+div.jqplot-table-legend-swatch-outline {
+    border: 1px solid #cccccc;
+    padding:1px;
+}
+
+div.jqplot-table-legend-swatch {
+    width:0px;
+    height:0px;
+    border-top-width: 5px;
+    border-bottom-width: 5px;
+    border-left-width: 6px;
+    border-right-width: 6px;
+    border-top-style: solid;
+    border-bottom-style: solid;
+    border-left-style: solid;
+    border-right-style: solid;
+}
+
+.jqplot-title {
+    top: 0px;
+    left: 0px;
+    padding-bottom: 0.5em;
+    font-size: 1.2em;
+}
+
+table.jqplot-cursor-tooltip {
+    border: 1px solid #cccccc;
+    font-size: 0.75em;
+}
+
+
+.jqplot-cursor-tooltip {
+    border: 1px solid #cccccc;
+    font-size: 0.75em;
+    white-space: nowrap;
+    background: rgba(208,208,208,0.5);
+    padding: 1px;
+}
+
+.jqplot-highlighter-tooltip, .jqplot-canvasOverlay-tooltip {
+    border: 1px solid #cccccc;
+    font-size: 0.75em;
+    white-space: nowrap;
+    background: rgba(208,208,208,0.5);
+    padding: 1px;
+}
+
+.jqplot-point-label {
+    font-size: 0.75em;
+    z-index: 2;
+}
+      
+td.jqplot-cursor-legend-swatch {
+    vertical-align: middle;
+    text-align: center;
+}
+
+div.jqplot-cursor-legend-swatch {
+    width: 1.2em;
+    height: 0.7em;
+}
+
+.jqplot-error {
+/*   Styles added to the plot target container when there is an error go here.*/
+    text-align: center;
+}
+
+.jqplot-error-message {
+/*    Styling of the custom error message div goes here.*/
+    position: relative;
+    top: 46%;
+    display: inline-block;
+}
+
+div.jqplot-bubble-label {
+    font-size: 0.8em;
+/*    background: rgba(90%, 90%, 90%, 0.15);*/
+    padding-left: 2px;
+    padding-right: 2px;
+    color: rgb(20%, 20%, 20%);
+}
+
+div.jqplot-bubble-label.jqplot-bubble-label-highlight {
+    background: rgba(90%, 90%, 90%, 0.7);
+}
+
+div.jqplot-noData-container {
+	text-align: center;
+	background-color: rgba(96%, 96%, 96%, 0.3);
+}

Разница между файлами не показана из-за своего большого размера
+ 11381 - 0
web/js/jqplot/jquery.jqplot.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
web/js/jqplot/jquery.jqplot.min.css


Разница между файлами не показана из-за своего большого размера
+ 9046 - 0
web/js/jqplot/jquery.js


Разница между файлами не показана из-за своего большого размера
+ 4 - 0
web/js/jqplot/jquery.min.js


+ 313 - 0
web/js/jqplot/plugins/jqplot.BezierCurveRenderer.js

@@ -0,0 +1,313 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    // Class: $.jqplot.BezierCurveRenderer.js
+    // Renderer which draws lines as stacked bezier curves.
+    // Data for the line will not be specified as an array of
+    // [x, y] data point values, but as a an array of [start piont, bezier curve]
+    // So, the line is specified as: [[xstart, ystart], [cp1x, cp1y, cp2x, cp2y, xend, yend]].
+    $.jqplot.BezierCurveRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.BezierCurveRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.BezierCurveRenderer.prototype.constructor = $.jqplot.BezierCurveRenderer;
+
+    
+    // Method: setGridData
+    // converts the user data values to grid coordinates and stores them
+    // in the gridData array.
+    // Called with scope of a series.
+    $.jqplot.BezierCurveRenderer.prototype.setGridData = function(plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        // this._plotData should be same as this.data
+        var data = this.data;
+        this.gridData = [];
+        this._prevGridData = [];
+        // if seriesIndex = 0, fill to x axis.
+        // if seriesIndex > 0, fill to previous series data.
+        var idx = this.index;
+        if (data.length == 2) {
+            if (idx == 0) {
+                this.gridData = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),  
+                        xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
+                    [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)],
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
+                ];
+            }
+            else {
+                var psd = plot.series[idx-1].data;
+                this.gridData = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),  
+                        xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
+                    [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])],
+                    [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), 
+                        xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),  
+                        xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
+                ];
+            }
+        }
+        else {
+            if (idx == 0) {
+                this.gridData = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),  
+                        xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
+                    [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)],
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
+                ];
+            }
+            else {
+                var psd = plot.series[idx-1].data;
+                this.gridData = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),  
+                        xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
+                    [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])],
+                    [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), 
+                        xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),  
+                        xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
+                ];
+            }
+        }
+    };
+    
+    // Method: makeGridData
+    // converts any arbitrary data values to grid coordinates and
+    // returns them.  This method exists so that plugins can use a series'
+    // linerenderer to generate grid data points without overwriting the
+    // grid data associated with that series.
+    // Called with scope of a series.
+    $.jqplot.BezierCurveRenderer.prototype.makeGridData = function(data, plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var gd = [];
+        var pgd = [];
+        // if seriesIndex = 0, fill to x axis.
+        // if seriesIndex > 0, fill to previous series data.
+        var idx = this.index;
+        if (data.length == 2) {
+            if (idx == 0) {
+                gd = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),  
+                        xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
+                    [xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, this._yaxis.min)],
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
+                ];
+            }
+            else {
+                var psd = plot.series[idx-1].data;
+                gd = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[1][2]), yp.call(this._yaxis, data[1][3]),  
+                        xp.call(this._xaxis, data[1][4]), yp.call(this._yaxis, data[1][5])],
+                    [xp.call(this._xaxis, psd[1][4]), yp.call(this._yaxis, psd[1][5])],
+                    [xp.call(this._xaxis, psd[1][2]), yp.call(this._yaxis, psd[1][3]), 
+                        xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),  
+                        xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
+                ];
+            }
+        }
+        else {
+            if (idx == 0) {
+                gd = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),  
+                        xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
+                    [xp.call(this._xaxis, data[3][1]), yp.call(this._yaxis, this._yaxis.min)],
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, this._yaxis.min)]
+                ];
+            }
+            else {
+                var psd = plot.series[idx-1].data;
+                gd = [
+                    [xp.call(this._xaxis, data[0][0]), yp.call(this._yaxis, data[0][1])], 
+                    [xp.call(this._xaxis, data[1][0]), yp.call(this._yaxis, data[1][1]), 
+                        xp.call(this._xaxis, data[2][0]), yp.call(this._yaxis, data[2][1]),  
+                        xp.call(this._xaxis, data[3][0]), yp.call(this._yaxis, data[3][1])],
+                    [xp.call(this._xaxis, psd[3][0]), yp.call(this._yaxis, psd[3][1])],
+                    [xp.call(this._xaxis, psd[2][0]), yp.call(this._yaxis, psd[2][1]), 
+                        xp.call(this._xaxis, psd[1][0]), yp.call(this._yaxis, psd[1][1]),  
+                        xp.call(this._xaxis, psd[0][0]), yp.call(this._yaxis, psd[0][1])]
+                ];
+            }
+        }
+        return gd;
+    };
+    
+
+    // called within scope of series.
+    $.jqplot.BezierCurveRenderer.prototype.draw = function(ctx, gd, options) {
+        var i;
+        ctx.save();
+        if (gd.length) {
+            if (this.showLine) {
+                ctx.save();
+                var opts = (options != null) ? options : {};
+                ctx.fillStyle = opts.fillStyle || this.color;
+                ctx.beginPath();
+                ctx.moveTo(gd[0][0], gd[0][1]);
+                ctx.bezierCurveTo(gd[1][0], gd[1][1], gd[1][2], gd[1][3], gd[1][4], gd[1][5]);
+                ctx.lineTo(gd[2][0], gd[2][1]);
+                if (gd[3].length == 2) {
+                    ctx.lineTo(gd[3][0], gd[3][1]);
+                }
+                else {
+                    ctx.bezierCurveTo(gd[3][0], gd[3][1], gd[3][2], gd[3][3], gd[3][4], gd[3][5]);
+                }
+                ctx.closePath();
+                ctx.fill();
+                ctx.restore();
+            }
+        }
+        
+        ctx.restore();
+    };  
+    
+    $.jqplot.BezierCurveRenderer.prototype.drawShadow = function(ctx, gd, options) {
+        // This is a no-op, shadows drawn with lines.
+    };
+    
+    $.jqplot.BezierAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+    };
+    
+    $.jqplot.BezierAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.BezierAxisRenderer.prototype.constructor = $.jqplot.BezierAxisRenderer;
+        
+    
+    // Axes on a plot with Bezier Curves
+    $.jqplot.BezierAxisRenderer.prototype.init = function(options){
+        $.extend(true, this, options);
+        var db = this._dataBounds;
+        // Go through all the series attached to this axis and find
+        // the min/max bounds for this axis.
+        for (var i=0; i<this._series.length; i++) {
+            var s = this._series[i];
+            var d = s.data;  
+            if (d.length == 4) {
+                for (var j=0; j<d.length; j++) { 
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        if (d[j][0] < db.min || db.min == null) {
+                            db.min = d[j][0];
+                        }
+                        if (d[j][0] > db.max || db.max == null) {
+                            db.max = d[j][0];
+                        }
+                    }              
+                    else {
+                        if (d[j][1] < db.min || db.min == null) {
+                            db.min = d[j][1];
+                        }
+                        if (d[j][1] > db.max || db.max == null) {
+                            db.max = d[j][1];
+                        }
+                    }              
+                }
+            }          
+            else {    
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    if (d[0][0] < db.min || db.min == null) {
+                        db.min = d[0][0];
+                    }
+                    if (d[0][0] > db.max || db.max == null) {
+                        db.max = d[0][0];
+                    }
+                    for (var j=0; j<5; j+=2) {
+                        if (d[1][j] < db.min || db.min == null) {
+                            db.min = d[1][j];
+                        }
+                        if (d[1][j] > db.max || db.max == null) {
+                            db.max = d[1][j];
+                        }
+                    }
+                }              
+                else {
+                    if (d[0][1] < db.min || db.min == null) {
+                        db.min = d[0][1];
+                    }
+                    if (d[0][1] > db.max || db.max == null) {
+                        db.max = d[0][1];
+                    }
+                    for (var j=1; j<6; j+=2) {
+                        if (d[1][j] < db.min || db.min == null) {
+                            db.min = d[1][j];
+                        }
+                        if (d[1][j] > db.max || db.max == null) {
+                            db.max = d[1][j];
+                        }
+                    }
+                }           
+            }
+        }
+    };
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = $.extend(true, {pad:0}, options.axesDefaults);
+        options.legend = $.extend(true, {placement:'outside'}, options.legend);
+        // only set these if there is a pie series
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.BezierCurveRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.BezierCurveRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.BezierAxisRenderer;
+        }
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    
+})(jQuery);    

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.BezierCurveRenderer.min.js


+ 797 - 0
web/js/jqplot/plugins/jqplot.barRenderer.js

@@ -0,0 +1,797 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    
+    // Class: $.jqplot.BarRenderer
+    // A plugin renderer for jqPlot to draw a bar plot.
+    // Draws series as a line.
+    
+    $.jqplot.BarRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.BarRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.BarRenderer.prototype.constructor = $.jqplot.BarRenderer;
+    
+    // called with scope of series.
+    $.jqplot.BarRenderer.prototype.init = function(options, plot) {
+        // Group: Properties
+        //
+        // prop: barPadding
+        // Number of pixels between adjacent bars at the same axis value.
+        this.barPadding = 8;
+        // prop: barMargin
+        // Number of pixels between groups of bars at adjacent axis values.
+        this.barMargin = 10;
+        // prop: barDirection
+        // 'vertical' = up and down bars, 'horizontal' = side to side bars
+        this.barDirection = 'vertical';
+        // prop: barWidth
+        // Width of the bar in pixels (auto by devaul).  null = calculated automatically.
+        this.barWidth = null;
+        // prop: shadowOffset
+        // offset of the shadow from the slice and offset of 
+        // each succesive stroke of the shadow from the last.
+        this.shadowOffset = 2;
+        // prop: shadowDepth
+        // number of strokes to apply to the shadow, 
+        // each stroke offset shadowOffset from the last.
+        this.shadowDepth = 5;
+        // prop: shadowAlpha
+        // transparency of the shadow (0 = transparent, 1 = opaque)
+        this.shadowAlpha = 0.08;
+        // prop: waterfall
+        // true to enable waterfall plot.
+        this.waterfall = false;
+        // prop: groups
+        // group bars into this many groups
+        this.groups = 1;
+        // prop: varyBarColor
+        // true to color each bar of a series separately rather than
+        // have every bar of a given series the same color.
+        // If used for non-stacked multiple series bar plots, user should
+        // specify a separate 'seriesColors' array for each series.
+        // Otherwise, each series will set their bars to the same color array.
+        // This option has no Effect for stacked bar charts and is disabled.
+        this.varyBarColor = false;
+        // prop: highlightMouseOver
+        // True to highlight slice when moused over.
+        // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
+        this.highlightMouseOver = true;
+        // prop: highlightMouseDown
+        // True to highlight when a mouse button is pressed over a slice.
+        // This will be disabled if highlightMouseOver is true.
+        this.highlightMouseDown = false;
+        // prop: highlightColors
+        // an array of colors to use when highlighting a bar.
+        this.highlightColors = [];
+        // prop: transposedData
+        // NOT IMPLEMENTED YET.  True if this is a horizontal bar plot and 
+        // x and y values are "transposed".  Tranposed, or "swapped", data is 
+        // required prior to rev. 894 builds of jqPlot with horizontal bars. 
+        // Allows backward compatability of bar renderer horizontal bars with 
+        // old style data sets.
+        this.transposedData = true;
+        this.renderer.animation = {
+            show: false,
+            direction: 'down',
+            speed: 3000,
+            _supported: true
+        };
+        this._type = 'bar';
+        
+        // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
+        if (options.highlightMouseDown && options.highlightMouseOver == null) {
+            options.highlightMouseOver = false;
+        }
+        
+        //////
+        // This is probably wrong here.
+        // After going back and forth on wether renderer should be the thing
+        // or extend the thing, it seems that it it best if it is a property
+        // on the thing.  This should be something that is commonized 
+        // among series renderers in the future.
+        //////
+        $.extend(true, this, options);
+
+        // really should probably do this
+        $.extend(true, this.renderer, options);
+        // fill is still needed to properly draw the legend.
+        // bars have to be filled.
+        this.fill = true;
+
+        // if horizontal bar and animating, reset the default direction
+        if (this.barDirection === 'horizontal' && this.rendererOptions.animation && this.rendererOptions.animation.direction == null) {
+            this.renderer.animation.direction = 'left';
+        }
+        
+        if (this.waterfall) {
+            this.fillToZero = false;
+            this.disableStack = true;
+        }
+        
+        if (this.barDirection == 'vertical' ) {
+            this._primaryAxis = '_xaxis';
+            this._stackAxis = 'y';
+            this.fillAxis = 'y';
+        }
+        else {
+            this._primaryAxis = '_yaxis';
+            this._stackAxis = 'x';
+            this.fillAxis = 'x';
+        }
+        // index of the currenty highlighted point, if any
+        this._highlightedPoint = null;
+        // total number of values for all bar series, total number of bar series, and position of this series
+        this._plotSeriesInfo = null;
+        // Array of actual data colors used for each data point.
+        this._dataColors = [];
+        this._barPoints = [];
+        
+        // set the shape renderer options
+        var opts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, strokeStyle:this.color, fillStyle:this.color, closePath:this.fill};
+        this.renderer.shapeRenderer.init(opts);
+        // set the shadow renderer options
+        var sopts = {lineJoin:'miter', lineCap:'round', fill:true, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, closePath:this.fill};
+        this.renderer.shadowRenderer.init(sopts);
+        
+        plot.postInitHooks.addOnce(postInit);
+        plot.postDrawHooks.addOnce(postPlotDraw);
+        plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
+        plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
+        plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
+        plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
+        plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick); 
+    };
+    
+    // called with scope of series
+    function barPreInit(target, data, seriesDefaults, options) {
+        if (this.rendererOptions.barDirection == 'horizontal') {
+            this._stackAxis = 'x';
+            this._primaryAxis = '_yaxis';
+        }
+        if (this.rendererOptions.waterfall == true) {
+            this._data = $.extend(true, [], this.data);
+            var sum = 0;
+            var pos = (!this.rendererOptions.barDirection || this.rendererOptions.barDirection === 'vertical' || this.transposedData === false) ? 1 : 0;
+            for(var i=0; i<this.data.length; i++) {
+                sum += this.data[i][pos];
+                if (i>0) {
+                    this.data[i][pos] += this.data[i-1][pos];
+                }
+            }
+            this.data[this.data.length] = (pos == 1) ? [this.data.length+1, sum] : [sum, this.data.length+1];
+            this._data[this._data.length] = (pos == 1) ? [this._data.length+1, sum] : [sum, this._data.length+1];
+        }
+        if (this.rendererOptions.groups > 1) {
+            this.breakOnNull = true;
+            var l = this.data.length;
+            var skip = parseInt(l/this.rendererOptions.groups, 10);
+            var count = 0;
+            for (var i=skip; i<l; i+=skip) {
+                this.data.splice(i+count, 0, [null, null]);
+                this._plotData.splice(i+count, 0, [null, null]);
+                this._stackData.splice(i+count, 0, [null, null]);
+                count++;
+            }
+            for (i=0; i<this.data.length; i++) {
+                if (this._primaryAxis == '_xaxis') {
+                    this.data[i][0] = i+1;
+                    this._plotData[i][0] = i+1;
+                    this._stackData[i][0] = i+1;
+                }
+                else {
+                    this.data[i][1] = i+1;
+                    this._plotData[i][1] = i+1;
+                    this._stackData[i][1] = i+1;
+                }
+            }
+        }
+    }
+    
+    $.jqplot.preSeriesInitHooks.push(barPreInit);
+    
+    // needs to be called with scope of series, not renderer.
+    $.jqplot.BarRenderer.prototype.calcSeriesNumbers = function() {
+        var nvals = 0;
+        var nseries = 0;
+        var paxis = this[this._primaryAxis];
+        var s, series, pos;
+        // loop through all series on this axis
+        for (var i=0; i < paxis._series.length; i++) {
+            series = paxis._series[i];
+            if (series === this) {
+                pos = i;
+            }
+            // is the series rendered as a bar?
+            if (series.renderer.constructor == $.jqplot.BarRenderer) {
+                // gridData may not be computed yet, use data length insted
+                nvals += series.data.length;
+                nseries += 1;
+            }
+        }
+        // return total number of values for all bar series, total number of bar series, and position of this series
+        return [nvals, nseries, pos];
+    };
+
+    $.jqplot.BarRenderer.prototype.setBarWidth = function() {
+        // need to know how many data values we have on the approprate axis and figure it out.
+        var i;
+        var nvals = 0;
+        var nseries = 0;
+        var paxis = this[this._primaryAxis];
+        var s, series, pos;
+        var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
+        nvals = temp[0];
+        nseries = temp[1];
+        var nticks = paxis.numberTicks;
+        var nbins = (nticks-1)/2;
+        // so, now we have total number of axis values.
+        if (paxis.name == 'xaxis' || paxis.name == 'x2axis') {
+            if (this._stack) {
+                this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals * nseries - this.barMargin;
+            }
+            else {
+                this.barWidth = ((paxis._offsets.max - paxis._offsets.min)/nbins  - this.barPadding * (nseries-1) - this.barMargin*2)/nseries;
+                // this.barWidth = (paxis._offsets.max - paxis._offsets.min) / nvals - this.barPadding - this.barMargin/nseries;
+            }
+        }
+        else {
+            if (this._stack) {
+                this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals * nseries - this.barMargin;
+            }
+            else {
+                this.barWidth = ((paxis._offsets.min - paxis._offsets.max)/nbins  - this.barPadding * (nseries-1) - this.barMargin*2)/nseries;
+                // this.barWidth = (paxis._offsets.min - paxis._offsets.max) / nvals - this.barPadding - this.barMargin/nseries;
+            }
+        }
+        return [nvals, nseries];
+    };
+
+    function computeHighlightColors (colors) {
+        var ret = [];
+        for (var i=0; i<colors.length; i++){
+            var rgba = $.jqplot.getColorComponents(colors[i]);
+            var newrgb = [rgba[0], rgba[1], rgba[2]];
+            var sum = newrgb[0] + newrgb[1] + newrgb[2];
+            for (var j=0; j<3; j++) {
+                // when darkening, lowest color component can be is 60.
+                newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
+                newrgb[j] = parseInt(newrgb[j], 10);
+            }
+            ret.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
+        }
+        return ret;
+    }
+
+    function getStart(sidx, didx, comp, plot, axis) {
+        // check if sign change
+        var seriesIndex = sidx,
+            prevSeriesIndex = sidx - 1,
+            start,
+            prevVal,
+            aidx = (axis === 'x') ? 0 : 1;
+
+        // is this not the first series?
+        if (seriesIndex > 0) {
+            prevVal = plot.series[prevSeriesIndex]._plotData[didx][aidx];
+
+            // is there a sign change
+            if ((comp * prevVal) < 0) {
+                start = getStart(prevSeriesIndex, didx, comp, plot, axis);
+            }
+
+            // no sign change.
+            else {
+                start = plot.series[prevSeriesIndex].gridData[didx][aidx];
+            }
+
+        }
+
+        // if first series, return value at 0
+        else {
+
+            start = (aidx === 0) ? plot.series[seriesIndex]._xaxis.series_u2p(0) : plot.series[seriesIndex]._yaxis.series_u2p(0);
+        }
+
+        return start;
+    }
+
+    
+    $.jqplot.BarRenderer.prototype.draw = function(ctx, gridData, options, plot) {
+        var i;
+        // Ughhh, have to make a copy of options b/c it may be modified later.
+        var opts = $.extend({}, options);
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var xaxis = this.xaxis;
+        var yaxis = this.yaxis;
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var pointx, pointy;
+        // clear out data colors.
+        this._dataColors = [];
+        this._barPoints = [];
+        
+        if (this.barWidth == null) {
+            this.renderer.setBarWidth.call(this);
+        }
+        
+        var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
+        var nvals = temp[0];
+        var nseries = temp[1];
+        var pos = temp[2];
+		var points = [];
+        
+        if (this._stack) {
+            this._barNudge = 0;
+        }
+        else {
+            this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding);
+        }
+        if (showLine) {
+            var negativeColors = new $.jqplot.ColorGenerator(this.negativeSeriesColors);
+            var positiveColors = new $.jqplot.ColorGenerator(this.seriesColors);
+            var negativeColor = negativeColors.get(this.index);
+            if (! this.useNegativeColors) {
+                negativeColor = opts.fillStyle;
+            }
+            var positiveColor = opts.fillStyle;
+			var base;
+			var xstart; 
+			var ystart;
+            
+            if (this.barDirection == 'vertical') {
+                for (var i=0; i<gridData.length; i++) {
+                    if (!this._stack && this.data[i][1] == null) {
+                        continue;
+                    }
+                    points = [];
+                    base = gridData[i][0] + this._barNudge;
+                    
+                    // stacked
+                    if (this._stack && this._prevGridData.length) {
+                        ystart = getStart(this.index, i, this._plotData[i][1], plot, 'y');
+                    }
+
+                    // not stacked
+                    else {
+                        if (this.fillToZero) {
+                            ystart = this._yaxis.series_u2p(0);
+                        }
+                        else if (this.waterfall && i > 0 && i < this.gridData.length-1) {
+                            ystart = this.gridData[i-1][1];
+                        }
+                        else if (this.waterfall && i == 0 && i < this.gridData.length-1) {
+                            if (this._yaxis.min <= 0 && this._yaxis.max >= 0) {
+                                ystart = this._yaxis.series_u2p(0);
+                            }
+                            else if (this._yaxis.min > 0) {
+                                ystart = ctx.canvas.height;
+                            }
+                            else {
+                                ystart = 0;
+                            }
+                        }
+                        else if (this.waterfall && i == this.gridData.length - 1) {
+                            if (this._yaxis.min <= 0 && this._yaxis.max >= 0) {
+                                ystart = this._yaxis.series_u2p(0);
+                            }
+                            else if (this._yaxis.min > 0) {
+                                ystart = ctx.canvas.height;
+                            }
+                            else {
+                                ystart = 0;
+                            }
+                        }
+                        else {
+                            ystart = ctx.canvas.height;
+                        }
+                    }
+                    if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) {
+                        if (this.varyBarColor && !this._stack) {
+                            if (this.useNegativeColors) {
+                                opts.fillStyle = negativeColors.next();
+                            }
+                            else {
+                                opts.fillStyle = positiveColors.next();
+                            }
+                        }
+                        else {
+                            opts.fillStyle = negativeColor;
+                        }
+                    }
+                    else {
+                        if (this.varyBarColor && !this._stack) {
+                            opts.fillStyle = positiveColors.next();
+                        }
+                        else {
+                            opts.fillStyle = positiveColor;
+                        }
+                    }
+					
+					if (!this.fillToZero || this._plotData[i][1] >= 0) { 
+						points.push([base-this.barWidth/2, ystart]);
+						points.push([base-this.barWidth/2, gridData[i][1]]);
+						points.push([base+this.barWidth/2, gridData[i][1]]);
+						points.push([base+this.barWidth/2, ystart]);
+					}
+					// for negative bars make sure points are always ordered clockwise
+					else {              
+						points.push([base-this.barWidth/2, gridData[i][1]]);
+						points.push([base-this.barWidth/2, ystart]);
+						points.push([base+this.barWidth/2, ystart]);
+						points.push([base+this.barWidth/2, gridData[i][1]]);
+					}
+                    this._barPoints.push(points);
+                    // now draw the shadows if not stacked.
+                    // for stacked plots, they are predrawn by drawShadow
+                    if (shadow && !this._stack) {
+                        var sopts = $.extend(true, {}, opts);
+                        // need to get rid of fillStyle on shadow.
+                        delete sopts.fillStyle;
+                        this.renderer.shadowRenderer.draw(ctx, points, sopts);
+                    }
+                    var clr = opts.fillStyle || this.color;
+                    this._dataColors.push(clr);
+                    this.renderer.shapeRenderer.draw(ctx, points, opts); 
+                }
+            }
+            
+            else if (this.barDirection == 'horizontal'){
+                for (var i=0; i<gridData.length; i++) {
+                    if (!this._stack && this.data[i][0] == null) {
+                        continue;
+                    }
+                    points = [];
+                    base = gridData[i][1] - this._barNudge;
+                    xstart;
+                    
+                    if (this._stack && this._prevGridData.length) {
+                        xstart = getStart(this.index, i, this._plotData[i][0], plot, 'x');
+                    }
+                    // not stacked
+                    else {
+                        if (this.fillToZero) {
+                            xstart = this._xaxis.series_u2p(0);
+                        }
+                        else if (this.waterfall && i > 0 && i < this.gridData.length-1) {
+                            xstart = this.gridData[i-1][0];
+                        }
+                        else if (this.waterfall && i == 0 && i < this.gridData.length-1) {
+                            if (this._xaxis.min <= 0 && this._xaxis.max >= 0) {
+                                xstart = this._xaxis.series_u2p(0);
+                            }
+                            else if (this._xaxis.min > 0) {
+                                xstart = 0;
+                            }
+                            else {
+                                xstart = 0;
+                            }
+                        }
+                        else if (this.waterfall && i == this.gridData.length - 1) {
+                            if (this._xaxis.min <= 0 && this._xaxis.max >= 0) {
+                                xstart = this._xaxis.series_u2p(0);
+                            }
+                            else if (this._xaxis.min > 0) {
+                                xstart = 0;
+                            }
+                            else {
+                                xstart = ctx.canvas.width;
+                            }
+                        }
+                        else {
+                            xstart = 0;
+                        }
+                    }
+                    if ((this.fillToZero && this._plotData[i][1] < 0) || (this.waterfall && this._data[i][1] < 0)) {
+                        if (this.varyBarColor && !this._stack) {
+                            if (this.useNegativeColors) {
+                                opts.fillStyle = negativeColors.next();
+                            }
+                            else {
+                                opts.fillStyle = positiveColors.next();
+                            }
+                        }
+                    }
+                    else {
+                        if (this.varyBarColor && !this._stack) {
+                            opts.fillStyle = positiveColors.next();
+                        }
+                        else {
+                            opts.fillStyle = positiveColor;
+                        }                    
+                    }
+                    
+
+                    if (!this.fillToZero || this._plotData[i][0] >= 0) {
+                        points.push([xstart, base + this.barWidth / 2]);
+                        points.push([xstart, base - this.barWidth / 2]);
+                        points.push([gridData[i][0], base - this.barWidth / 2]);
+                        points.push([gridData[i][0], base + this.barWidth / 2]);
+                    }
+                    else {
+                        points.push([gridData[i][0], base + this.barWidth / 2]);
+                        points.push([gridData[i][0], base - this.barWidth / 2]);
+                        points.push([xstart, base - this.barWidth / 2]);
+                        points.push([xstart, base + this.barWidth / 2]);
+                    }
+
+                    this._barPoints.push(points);
+                    // now draw the shadows if not stacked.
+                    // for stacked plots, they are predrawn by drawShadow
+                    if (shadow && !this._stack) {
+                        var sopts = $.extend(true, {}, opts);
+                        delete sopts.fillStyle;
+                        this.renderer.shadowRenderer.draw(ctx, points, sopts);
+                    }
+                    var clr = opts.fillStyle || this.color;
+                    this._dataColors.push(clr);
+                    this.renderer.shapeRenderer.draw(ctx, points, opts);
+                } 
+            }
+        }                
+        
+        if (this.highlightColors.length == 0) {
+            this.highlightColors = $.jqplot.computeHighlightColors(this._dataColors);
+        }
+        
+        else if (typeof(this.highlightColors) == 'string') {
+            var temp = this.highlightColors;
+            this.highlightColors = [];
+            for (var i=0; i<this._dataColors.length; i++) {
+                this.highlightColors.push(temp);
+            }
+        }
+        
+    };
+    
+     
+    // for stacked plots, shadows will be pre drawn by drawShadow.
+    $.jqplot.BarRenderer.prototype.drawShadow = function(ctx, gridData, options, plot) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var xaxis = this.xaxis;
+        var yaxis = this.yaxis;
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var pointx, points, pointy, nvals, nseries, pos;
+        
+        if (this._stack && this.shadow) {
+            if (this.barWidth == null) {
+                this.renderer.setBarWidth.call(this);
+            }
+        
+            var temp = this._plotSeriesInfo = this.renderer.calcSeriesNumbers.call(this);
+            nvals = temp[0];
+            nseries = temp[1];
+            pos = temp[2];
+        
+            if (this._stack) {
+                this._barNudge = 0;
+            }
+            else {
+                this._barNudge = (-Math.abs(nseries/2 - 0.5) + pos) * (this.barWidth + this.barPadding);
+            }
+            if (showLine) {
+            
+                if (this.barDirection == 'vertical') {
+                    for (var i=0; i<gridData.length; i++) {
+                        if (this.data[i][1] == null) {
+                            continue;
+                        }
+                        points = [];
+                        var base = gridData[i][0] + this._barNudge;
+                        var ystart;
+                    
+                        if (this._stack && this._prevGridData.length) {
+                            ystart = getStart(this.index, i, this._plotData[i][1], plot, 'y');
+                        }
+                        else {
+                            if (this.fillToZero) {
+                                ystart = this._yaxis.series_u2p(0);
+                            }
+                            else {
+                                ystart = ctx.canvas.height;
+                            }
+                        }
+                    
+                        points.push([base-this.barWidth/2, ystart]);
+                        points.push([base-this.barWidth/2, gridData[i][1]]);
+                        points.push([base+this.barWidth/2, gridData[i][1]]);
+                        points.push([base+this.barWidth/2, ystart]);
+                        this.renderer.shadowRenderer.draw(ctx, points, opts);
+                    }
+                }
+            
+                else if (this.barDirection == 'horizontal'){
+                    for (var i=0; i<gridData.length; i++) {
+                        if (this.data[i][0] == null) {
+                            continue;
+                        }
+                        points = [];
+                        var base = gridData[i][1] - this._barNudge;
+                        var xstart;
+                    
+                        if (this._stack && this._prevGridData.length) {
+                            xstart = getStart(this.index, i, this._plotData[i][0], plot, 'x');
+                        }
+                        else {
+                            if (this.fillToZero) {
+                                xstart = this._xaxis.series_u2p(0);
+                            }
+                            else {
+                                xstart = 0;
+                            }
+                        }
+                    
+                        points.push([xstart, base+this.barWidth/2]);
+                        points.push([gridData[i][0], base+this.barWidth/2]);
+                        points.push([gridData[i][0], base-this.barWidth/2]);
+                        points.push([xstart, base-this.barWidth/2]);
+                        this.renderer.shadowRenderer.draw(ctx, points, opts);
+                    }  
+                }
+            }   
+            
+        }
+    };
+    
+    function postInit(target, data, options) {
+        for (var i=0; i<this.series.length; i++) {
+            if (this.series[i].renderer.constructor == $.jqplot.BarRenderer) {
+                // don't allow mouseover and mousedown at same time.
+                if (this.series[i].highlightMouseOver) {
+                    this.series[i].highlightMouseDown = false;
+                }
+            }
+        }
+    }
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    function postPlotDraw() {
+        // Memory Leaks patch    
+        if (this.plugins.barRenderer && this.plugins.barRenderer.highlightCanvas) {
+
+            this.plugins.barRenderer.highlightCanvas.resetCanvas();
+            this.plugins.barRenderer.highlightCanvas = null;
+        }
+         
+        this.plugins.barRenderer = {highlightedSeriesIndex:null};
+        this.plugins.barRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        this.eventCanvas._elem.before(this.plugins.barRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-barRenderer-highlight-canvas', this._plotDimensions, this));
+        this.plugins.barRenderer.highlightCanvas.setContext();
+        this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
+    }   
+    
+    function highlight (plot, sidx, pidx, points) {
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.barRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.barRenderer.highlightedSeriesIndex = sidx;
+        var opts = {fillStyle: s.highlightColors[pidx]};
+        s.renderer.shapeRenderer.draw(canvas._ctx, points, opts);
+        canvas = null;
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.barRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        for (var i=0; i<plot.series.length; i++) {
+            plot.series[i]._highlightedPoint = null;
+        }
+        plot.plugins.barRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+        canvas =  null;
+    }
+    
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt1 = jQuery.Event('jqplotDataMouseOver');
+            evt1.pageX = ev.pageX;
+            evt1.pageY = ev.pageY;
+            plot.target.trigger(evt1, ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+		evt.which = ev.which;
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.barRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+		evt.which = ev.which;
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, neighbor.seriesIndex, neighbor.pointIndex, neighbor.points);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
+        var idx = plot.plugins.barRenderer.highlightedSeriesIndex;
+        if (idx != null && plot.series[idx].highlightMouseDown) {
+            unhighlight(plot);
+        }
+    }
+    
+    function handleClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt = jQuery.Event('jqplotDataClick');
+	    evt.which = ev.which;
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var idx = plot.plugins.barRenderer.highlightedSeriesIndex;
+            if (idx != null && plot.series[idx].highlightMouseDown) {
+                unhighlight(plot);
+            }
+            var evt = jQuery.Event('jqplotDataRightClick');
+	    evt.which = ev.which;
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    
+})(jQuery);    

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.barRenderer.min.js


Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.blockRenderer.min.js


Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.bubbleRenderer.min.js


+ 203 - 0
web/js/jqplot/plugins/jqplot.canvasAxisLabelRenderer.js

@@ -0,0 +1,203 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    /**
+    * Class: $.jqplot.CanvasAxisLabelRenderer
+    * Renderer to draw axis labels with a canvas element to support advanced
+    * featrues such as rotated text.  This renderer uses a separate rendering engine
+    * to draw the text on the canvas.  Two modes of rendering the text are available.
+    * If the browser has native font support for canvas fonts (currently Mozila 3.5
+    * and Safari 4), you can enable text rendering with the canvas fillText method.
+    * You do so by setting the "enableFontSupport" option to true. 
+    * 
+    * Browsers lacking native font support will have the text drawn on the canvas
+    * using the Hershey font metrics.  Even if the "enableFontSupport" option is true
+    * non-supporting browsers will still render with the Hershey font.
+    * 
+    */
+    $.jqplot.CanvasAxisLabelRenderer = function(options) {
+        // Group: Properties
+        
+        // prop: angle
+        // angle of text, measured clockwise from x axis.
+        this.angle = 0;
+        // name of the axis associated with this tick
+        this.axis;
+        // prop: show
+        // wether or not to show the tick (mark and label).
+        this.show = true;
+        // prop: showLabel
+        // wether or not to show the label.
+        this.showLabel = true;
+        // prop: label
+        // label for the axis.
+        this.label = '';
+        // prop: fontFamily
+        // CSS spec for the font-family css attribute.
+        // Applies only to browsers supporting native font rendering in the
+        // canvas tag.  Currently Mozilla 3.5 and Safari 4.
+        this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif';
+        // prop: fontSize
+        // CSS spec for font size.
+        this.fontSize = '11pt';
+        // prop: fontWeight
+        // CSS spec for fontWeight:  normal, bold, bolder, lighter or a number 100 - 900
+        this.fontWeight = 'normal';
+        // prop: fontStretch
+        // Multiplier to condense or expand font width.  
+        // Applies only to browsers which don't support canvas native font rendering.
+        this.fontStretch = 1.0;
+        // prop: textColor
+        // css spec for the color attribute.
+        this.textColor = '#666666';
+        // prop: enableFontSupport
+        // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+.
+        // If true, label will be drawn with canvas tag native support for fonts.
+        // If false, label will be drawn with Hershey font metrics.
+        this.enableFontSupport = true;
+        // prop: pt2px
+        // Point to pixel scaling factor, used for computing height of bounding box
+        // around a label.  The labels text renderer has a default setting of 1.4, which 
+        // should be suitable for most fonts.  Leave as null to use default.  If tops of
+        // letters appear clipped, increase this.  If bounding box seems too big, decrease.
+        // This is an issue only with the native font renderering capabilities of Mozilla
+        // 3.5 and Safari 4 since they do not provide a method to determine the font height.
+        this.pt2px = null;
+        
+        this._elem;
+        this._ctx;
+        this._plotWidth;
+        this._plotHeight;
+        this._plotDimensions = {height:null, width:null};
+        
+        $.extend(true, this, options);
+        
+        if (options.angle == null && this.axis != 'xaxis' && this.axis != 'x2axis') {
+            this.angle = -90;
+        }
+        
+        var ropts = {fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily};
+        if (this.pt2px) {
+            ropts.pt2px = this.pt2px;
+        }
+        
+        if (this.enableFontSupport) {
+            if ($.jqplot.support_canvas_text()) {
+                this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts);
+            }
+            
+            else {
+                this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); 
+            }
+        }
+        else {
+            this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); 
+        }
+    };
+    
+    $.jqplot.CanvasAxisLabelRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+        this._textRenderer.init({fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily});
+    };
+    
+    // return width along the x axis
+    // will check first to see if an element exists.
+    // if not, will return the computed text box width.
+    $.jqplot.CanvasAxisLabelRenderer.prototype.getWidth = function(ctx) {
+        if (this._elem) {
+         return this._elem.outerWidth(true);
+        }
+        else {
+            var tr = this._textRenderer;
+            var l = tr.getWidth(ctx);
+            var h = tr.getHeight(ctx);
+            var w = Math.abs(Math.sin(tr.angle)*h) + Math.abs(Math.cos(tr.angle)*l);
+            return w;
+        }
+    };
+    
+    // return height along the y axis.
+    $.jqplot.CanvasAxisLabelRenderer.prototype.getHeight = function(ctx) {
+        if (this._elem) {
+         return this._elem.outerHeight(true);
+        }
+        else {
+            var tr = this._textRenderer;
+            var l = tr.getWidth(ctx);
+            var h = tr.getHeight(ctx);
+            var w = Math.abs(Math.cos(tr.angle)*h) + Math.abs(Math.sin(tr.angle)*l);
+            return w;
+        }
+    };
+    
+    $.jqplot.CanvasAxisLabelRenderer.prototype.getAngleRad = function() {
+        var a = this.angle * Math.PI/180;
+        return a;
+    };
+    
+    $.jqplot.CanvasAxisLabelRenderer.prototype.draw = function(ctx, plot) {
+          // Memory Leaks patch
+          if (this._elem) {
+              if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
+                  window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
+              }
+            
+              this._elem.emptyForce();
+              this._elem = null;
+          }
+
+        // create a canvas here, but can't draw on it untill it is appended
+        // to dom for IE compatability.
+        var elem = plot.canvasManager.getCanvas();
+
+        this._textRenderer.setText(this.label, ctx);
+        var w = this.getWidth(ctx);
+        var h = this.getHeight(ctx);
+        elem.width = w;
+        elem.height = h;
+        elem.style.width = w;
+        elem.style.height = h;
+        
+		elem = plot.canvasManager.initCanvas(elem);
+		
+        this._elem = $(elem);
+        this._elem.css({ position: 'absolute'});
+        this._elem.addClass('jqplot-'+this.axis+'-label');
+        
+        elem = null;
+        return this._elem;
+    };
+    
+    $.jqplot.CanvasAxisLabelRenderer.prototype.pack = function() {
+        this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label);
+    };
+    
+})(jQuery);

+ 243 - 0
web/js/jqplot/plugins/jqplot.canvasAxisTickRenderer.js

@@ -0,0 +1,243 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    /**
+    *  Class: $.jqplot.CanvasAxisTickRenderer
+    * Renderer to draw axis ticks with a canvas element to support advanced
+    * featrues such as rotated text.  This renderer uses a separate rendering engine
+    * to draw the text on the canvas.  Two modes of rendering the text are available.
+    * If the browser has native font support for canvas fonts (currently Mozila 3.5
+    * and Safari 4), you can enable text rendering with the canvas fillText method.
+    * You do so by setting the "enableFontSupport" option to true. 
+    * 
+    * Browsers lacking native font support will have the text drawn on the canvas
+    * using the Hershey font metrics.  Even if the "enableFontSupport" option is true
+    * non-supporting browsers will still render with the Hershey font.
+    */
+    $.jqplot.CanvasAxisTickRenderer = function(options) {
+        // Group: Properties
+        
+        // prop: mark
+        // tick mark on the axis.  One of 'inside', 'outside', 'cross', '' or null.
+        this.mark = 'outside';
+        // prop: showMark
+        // wether or not to show the mark on the axis.
+        this.showMark = true;
+        // prop: showGridline
+        // wether or not to draw the gridline on the grid at this tick.
+        this.showGridline = true;
+        // prop: isMinorTick
+        // if this is a minor tick.
+        this.isMinorTick = false;
+        // prop: angle
+        // angle of text, measured clockwise from x axis.
+        this.angle = 0;
+        // prop:  markSize
+        // Length of the tick marks in pixels.  For 'cross' style, length
+        // will be stoked above and below axis, so total length will be twice this.
+        this.markSize = 4;
+        // prop: show
+        // wether or not to show the tick (mark and label).
+        this.show = true;
+        // prop: showLabel
+        // wether or not to show the label.
+        this.showLabel = true;
+        // prop: labelPosition
+        // 'auto', 'start', 'middle' or 'end'.
+        // Whether tick label should be positioned so the start, middle, or end
+        // of the tick mark.
+        this.labelPosition = 'auto';
+        this.label = '';
+        this.value = null;
+        this._styles = {};
+        // prop: formatter
+        // A class of a formatter for the tick text.
+        // The default $.jqplot.DefaultTickFormatter uses sprintf.
+        this.formatter = $.jqplot.DefaultTickFormatter;
+        // prop: formatString
+        // string passed to the formatter.
+        this.formatString = '';
+        // prop: prefix
+        // String to prepend to the tick label.
+        // Prefix is prepended to the formatted tick label.
+        this.prefix = '';
+        // prop: fontFamily
+        // css spec for the font-family css attribute.
+        this.fontFamily = '"Trebuchet MS", Arial, Helvetica, sans-serif';
+        // prop: fontSize
+        // CSS spec for font size.
+        this.fontSize = '10pt';
+        // prop: fontWeight
+        // CSS spec for fontWeight
+        this.fontWeight = 'normal';
+        // prop: fontStretch
+        // Multiplier to condense or expand font width.  
+        // Applies only to browsers which don't support canvas native font rendering.
+        this.fontStretch = 1.0;
+        // prop: textColor
+        // css spec for the color attribute.
+        this.textColor = '#666666';
+        // prop: enableFontSupport
+        // true to turn on native canvas font support in Mozilla 3.5+ and Safari 4+.
+        // If true, tick label will be drawn with canvas tag native support for fonts.
+        // If false, tick label will be drawn with Hershey font metrics.
+        this.enableFontSupport = true;
+        // prop: pt2px
+        // Point to pixel scaling factor, used for computing height of bounding box
+        // around a label.  The labels text renderer has a default setting of 1.4, which 
+        // should be suitable for most fonts.  Leave as null to use default.  If tops of
+        // letters appear clipped, increase this.  If bounding box seems too big, decrease.
+        // This is an issue only with the native font renderering capabilities of Mozilla
+        // 3.5 and Safari 4 since they do not provide a method to determine the font height.
+        this.pt2px = null;
+        
+        this._elem;
+        this._ctx;
+        this._plotWidth;
+        this._plotHeight;
+        this._plotDimensions = {height:null, width:null};
+        
+        $.extend(true, this, options);
+        
+        var ropts = {fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily};
+        if (this.pt2px) {
+            ropts.pt2px = this.pt2px;
+        }
+        
+        if (this.enableFontSupport) {
+            if ($.jqplot.support_canvas_text()) {
+                this._textRenderer = new $.jqplot.CanvasFontRenderer(ropts);
+            }
+            
+            else {
+                this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); 
+            }
+        }
+        else {
+            this._textRenderer = new $.jqplot.CanvasTextRenderer(ropts); 
+        }
+    };
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.init = function(options) {
+        $.extend(true, this, options);
+        this._textRenderer.init({fontSize:this.fontSize, fontWeight:this.fontWeight, fontStretch:this.fontStretch, fillStyle:this.textColor, angle:this.getAngleRad(), fontFamily:this.fontFamily});
+    };
+    
+    // return width along the x axis
+    // will check first to see if an element exists.
+    // if not, will return the computed text box width.
+    $.jqplot.CanvasAxisTickRenderer.prototype.getWidth = function(ctx) {
+        if (this._elem) {
+         return this._elem.outerWidth(true);
+        }
+        else {
+            var tr = this._textRenderer;
+            var l = tr.getWidth(ctx);
+            var h = tr.getHeight(ctx);
+            var w = Math.abs(Math.sin(tr.angle)*h) + Math.abs(Math.cos(tr.angle)*l);
+            return w;
+        }
+    };
+    
+    // return height along the y axis.
+    $.jqplot.CanvasAxisTickRenderer.prototype.getHeight = function(ctx) {
+        if (this._elem) {
+         return this._elem.outerHeight(true);
+        }
+        else {
+            var tr = this._textRenderer;
+            var l = tr.getWidth(ctx);
+            var h = tr.getHeight(ctx);
+            var w = Math.abs(Math.cos(tr.angle)*h) + Math.abs(Math.sin(tr.angle)*l);
+            return w;
+        }
+    };
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.getAngleRad = function() {
+        var a = this.angle * Math.PI/180;
+        return a;
+    };
+    
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.setTick = function(value, axisName, isMinor) {
+        this.value = value;
+        if (isMinor) {
+            this.isMinorTick = true;
+        }
+        return this;
+    };
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.draw = function(ctx, plot) {
+        if (!this.label) {
+            this.label = this.prefix + this.formatter(this.formatString, this.value);
+        }
+        
+        // Memory Leaks patch
+        if (this._elem) {
+            if ($.jqplot.use_excanvas && window.G_vmlCanvasManager.uninitElement !== undefined) {
+                window.G_vmlCanvasManager.uninitElement(this._elem.get(0));
+            }
+            
+            this._elem.emptyForce();
+            this._elem = null;
+        }
+
+        // create a canvas here, but can't draw on it untill it is appended
+        // to dom for IE compatability.
+
+        var elem = plot.canvasManager.getCanvas();
+
+        this._textRenderer.setText(this.label, ctx);
+        var w = this.getWidth(ctx);
+        var h = this.getHeight(ctx);
+        // canvases seem to need to have width and heigh attributes directly set.
+        elem.width = w;
+        elem.height = h;
+        elem.style.width = w;
+        elem.style.height = h;
+        elem.style.textAlign = 'left';
+        elem.style.position = 'absolute';
+		
+		elem = plot.canvasManager.initCanvas(elem);
+		
+        this._elem = $(elem);
+        this._elem.css(this._styles);
+        this._elem.addClass('jqplot-'+this.axis+'-tick');
+		
+        elem = null;
+        return this._elem;
+    };
+    
+    $.jqplot.CanvasAxisTickRenderer.prototype.pack = function() {
+        this._textRenderer.draw(this._elem.get(0).getContext("2d"), this.label);
+    };
+    
+})(jQuery);

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js


+ 673 - 0
web/js/jqplot/plugins/jqplot.categoryAxisRenderer.js

@@ -0,0 +1,673 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {   
+    /**
+    *  class: $.jqplot.CategoryAxisRenderer
+    *  A plugin for jqPlot to render a category style axis, with equal pixel spacing between y data values of a series.
+    *  
+    *  To use this renderer, include the plugin in your source
+    *  > <script type="text/javascript" language="javascript" src="plugins/jqplot.categoryAxisRenderer.js"></script>
+    *  
+    *  and supply the appropriate options to your plot
+    *  
+    *  > {axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer}}}
+    **/
+    $.jqplot.CategoryAxisRenderer = function(options) {
+        $.jqplot.LinearAxisRenderer.call(this);
+        // prop: sortMergedLabels
+        // True to sort tick labels when labels are created by merging
+        // x axis values from multiple series.  That is, say you have
+        // two series like:
+        // > line1 = [[2006, 4],            [2008, 9], [2009, 16]];
+        // > line2 = [[2006, 3], [2007, 7], [2008, 6]];
+        // If no label array is specified, tick labels will be collected
+        // from the x values of the series.  With sortMergedLabels
+        // set to true, tick labels will be:
+        // > [2006, 2007, 2008, 2009]
+        // With sortMergedLabels set to false, tick labels will be:
+        // > [2006, 2008, 2009, 2007]
+        //
+        // Note, this property is specified on the renderOptions for the 
+        // axes when creating a plot:
+        // > axes:{xaxis:{renderer:$.jqplot.CategoryAxisRenderer, rendererOptions:{sortMergedLabels:true}}}
+        this.sortMergedLabels = false;
+    };
+    
+    $.jqplot.CategoryAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.CategoryAxisRenderer.prototype.constructor = $.jqplot.CategoryAxisRenderer;
+    
+    $.jqplot.CategoryAxisRenderer.prototype.init = function(options){
+        this.groups = 1;
+        this.groupLabels = [];
+        this._groupLabels = [];
+        this._grouped = false;
+        this._barsPerGroup = null;
+        this.reverse = false;
+        // prop: tickRenderer
+        // A class of a rendering engine for creating the ticks labels displayed on the plot, 
+        // See <$.jqplot.AxisTickRenderer>.
+        // this.tickRenderer = $.jqplot.AxisTickRenderer;
+        // this.labelRenderer = $.jqplot.AxisLabelRenderer;
+        $.extend(true, this, {tickOptions:{formatString:'%d'}}, options);
+        var db = this._dataBounds;
+        // Go through all the series attached to this axis and find
+        // the min/max bounds for this axis.
+        for (var i=0; i<this._series.length; i++) {
+            var s = this._series[i];
+            if (s.groups) {
+                this.groups = s.groups;
+            }
+            var d = s.data;
+            
+            for (var j=0; j<d.length; j++) { 
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    if (d[j][0] < db.min || db.min == null) {
+                        db.min = d[j][0];
+                    }
+                    if (d[j][0] > db.max || db.max == null) {
+                        db.max = d[j][0];
+                    }
+                }              
+                else {
+                    if (d[j][1] < db.min || db.min == null) {
+                        db.min = d[j][1];
+                    }
+                    if (d[j][1] > db.max || db.max == null) {
+                        db.max = d[j][1];
+                    }
+                }              
+            }
+        }
+        
+        if (this.groupLabels.length) {
+            this.groups = this.groupLabels.length;
+        }
+    };
+ 
+
+    $.jqplot.CategoryAxisRenderer.prototype.createTicks = function() {
+        // we're are operating on an axis here
+        var ticks = this._ticks;
+        var userTicks = this.ticks;
+        var name = this.name;
+        // databounds were set on axis initialization.
+        var db = this._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var tt, i;
+
+        // if we already have ticks, use them.
+        if (userTicks.length) {
+            // adjust with blanks if we have groups
+            if (this.groups > 1 && !this._grouped) {
+                var l = userTicks.length;
+                var skip = parseInt(l/this.groups, 10);
+                var count = 0;
+                for (var i=skip; i<l; i+=skip) {
+                    userTicks.splice(i+count, 0, ' ');
+                    count++;
+                }
+                this._grouped = true;
+            }
+            this.min = 0.5;
+            this.max = userTicks.length + 0.5;
+            var range = this.max - this.min;
+            this.numberTicks = 2*userTicks.length + 1;
+            for (i=0; i<userTicks.length; i++){
+                tt = this.min + 2 * i * range / (this.numberTicks-1);
+                // need a marker before and after the tick
+                var t = new this.tickRenderer(this.tickOptions);
+                t.showLabel = false;
+                // t.showMark = true;
+                t.setTick(tt, this.name);
+                this._ticks.push(t);
+                var t = new this.tickRenderer(this.tickOptions);
+                t.label = userTicks[i];
+                // t.showLabel = true;
+                t.showMark = false;
+                t.showGridline = false;
+                t.setTick(tt+0.5, this.name);
+                this._ticks.push(t);
+            }
+            // now add the last tick at the end
+            var t = new this.tickRenderer(this.tickOptions);
+            t.showLabel = false;
+            // t.showMark = true;
+            t.setTick(tt+1, this.name);
+            this._ticks.push(t);
+        }
+
+        // we don't have any ticks yet, let's make some!
+        else {
+            if (name == 'xaxis' || name == 'x2axis') {
+                dim = this._plotDimensions.width;
+            }
+            else {
+                dim = this._plotDimensions.height;
+            }
+            
+            // if min, max and number of ticks specified, user can't specify interval.
+            if (this.min != null && this.max != null && this.numberTicks != null) {
+                this.tickInterval = null;
+            }
+            
+            // if max, min, and interval specified and interval won't fit, ignore interval.
+            if (this.min != null && this.max != null && this.tickInterval != null) {
+                if (parseInt((this.max-this.min)/this.tickInterval, 10) != (this.max-this.min)/this.tickInterval) {
+                    this.tickInterval = null;
+                }
+            }
+        
+            // find out how many categories are in the lines and collect labels
+            var labels = [];
+            var numcats = 0;
+            var min = 0.5;
+            var max, val;
+            var isMerged = false;
+            for (var i=0; i<this._series.length; i++) {
+                var s = this._series[i];
+                for (var j=0; j<s.data.length; j++) {
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        val = s.data[j][0];
+                    }
+                    else {
+                        val = s.data[j][1];
+                    }
+                    if ($.inArray(val, labels) == -1) {
+                        isMerged = true;
+                        numcats += 1;      
+                        labels.push(val);
+                    }
+                }
+            }
+            
+            if (isMerged && this.sortMergedLabels) {
+                labels.sort(function(a,b) { return a - b; });
+            }
+            
+            // keep a reference to these tick labels to use for redrawing plot (see bug #57)
+            this.ticks = labels;
+            
+            // now bin the data values to the right lables.
+            for (var i=0; i<this._series.length; i++) {
+                var s = this._series[i];
+                for (var j=0; j<s.data.length; j++) {
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        val = s.data[j][0];
+                    }
+                    else {
+                        val = s.data[j][1];
+                    }
+                    // for category axis, force the values into category bins.
+                    // we should have the value in the label array now.
+                    var idx = $.inArray(val, labels)+1;
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        s.data[j][0] = idx;
+                    }
+                    else {
+                        s.data[j][1] = idx;
+                    }
+                }
+            }
+            
+            // adjust with blanks if we have groups
+            if (this.groups > 1 && !this._grouped) {
+                var l = labels.length;
+                var skip = parseInt(l/this.groups, 10);
+                var count = 0;
+                for (var i=skip; i<l; i+=skip+1) {
+                    labels[i] = ' ';
+                }
+                this._grouped = true;
+            }
+        
+            max = numcats + 0.5;
+            if (this.numberTicks == null) {
+                this.numberTicks = 2*numcats + 1;
+            }
+
+            var range = max - min;
+            this.min = min;
+            this.max = max;
+            var track = 0;
+            
+            // todo: adjust this so more ticks displayed.
+            var maxVisibleTicks = parseInt(3+dim/10, 10);
+            var skip = parseInt(numcats/maxVisibleTicks, 10);
+
+            if (this.tickInterval == null) {
+
+                this.tickInterval = range / (this.numberTicks-1);
+
+            }
+            // if tickInterval is specified, we will ignore any computed maximum.
+            for (var i=0; i<this.numberTicks; i++){
+                tt = this.min + i * this.tickInterval;
+                var t = new this.tickRenderer(this.tickOptions);
+                // if even tick, it isn't a category, it's a divider
+                if (i/2 == parseInt(i/2, 10)) {
+                    t.showLabel = false;
+                    t.showMark = true;
+                }
+                else {
+                    if (skip>0 && track<skip) {
+                        t.showLabel = false;
+                        track += 1;
+                    }
+                    else {
+                        t.showLabel = true;
+                        track = 0;
+                    } 
+                    t.label = t.formatter(t.formatString, labels[(i-1)/2]);
+                    t.showMark = false;
+                    t.showGridline = false;
+                }
+                t.setTick(tt, this.name);
+                this._ticks.push(t);
+            }
+        }
+        
+    };
+    
+    // called with scope of axis
+    $.jqplot.CategoryAxisRenderer.prototype.draw = function(ctx, plot) {
+        if (this.show) {
+            // populate the axis label and value properties.
+            // createTicks is a method on the renderer, but
+            // call it within the scope of the axis.
+            this.renderer.createTicks.call(this);
+            // fill a div with axes labels in the right direction.
+            // Need to pregenerate each axis to get it's bounds and
+            // position it and the labels correctly on the plot.
+            var dim=0;
+            var temp;
+            // Added for theming.
+            if (this._elem) {
+                // this._elem.empty();
+                // Memory Leaks patch
+                this._elem.emptyForce();
+            }
+
+            this._elem = this._elem || $('<div class="jqplot-axis jqplot-'+this.name+'" style="position:absolute;"></div>');
+            
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                this._elem.width(this._plotDimensions.width);
+            }
+            else {
+                this._elem.height(this._plotDimensions.height);
+            }
+            
+            // create a _label object.
+            this.labelOptions.axis = this.name;
+            this._label = new this.labelRenderer(this.labelOptions);
+            if (this._label.show) {
+                var elem = this._label.draw(ctx, plot);
+                elem.appendTo(this._elem);
+            }
+    
+            var t = this._ticks;
+            for (var i=0; i<t.length; i++) {
+                var tick = t[i];
+                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                    var elem = tick.draw(ctx, plot);
+                    elem.appendTo(this._elem);
+                }
+            }
+        
+            this._groupLabels = [];
+            // now make group labels
+            for (var i=0; i<this.groupLabels.length; i++)
+            {
+                var elem = $('<div style="position:absolute;" class="jqplot-'+this.name+'-groupLabel"></div>');
+                elem.html(this.groupLabels[i]);
+                this._groupLabels.push(elem);
+                elem.appendTo(this._elem);
+            }
+        }
+        return this._elem;
+    };
+    
+    // called with scope of axis
+    $.jqplot.CategoryAxisRenderer.prototype.set = function() { 
+        var dim = 0;
+        var temp;
+        var w = 0;
+        var h = 0;
+        var lshow = (this._label == null) ? false : this._label.show;
+        if (this.show) {
+            var t = this._ticks;
+            for (var i=0; i<t.length; i++) {
+                var tick = t[i];
+                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        temp = tick._elem.outerHeight(true);
+                    }
+                    else {
+                        temp = tick._elem.outerWidth(true);
+                    }
+                    if (temp > dim) {
+                        dim = temp;
+                    }
+                }
+            }
+            
+            var dim2 = 0;
+            for (var i=0; i<this._groupLabels.length; i++) {
+                var l = this._groupLabels[i];
+                if (this.name == 'xaxis' || this.name == 'x2axis') {
+                    temp = l.outerHeight(true);
+                }
+                else {
+                    temp = l.outerWidth(true);
+                }
+                if (temp > dim2) {
+                    dim2 = temp;
+                }
+            }
+            
+            if (lshow) {
+                w = this._label._elem.outerWidth(true);
+                h = this._label._elem.outerHeight(true); 
+            }
+            if (this.name == 'xaxis') {
+                dim += dim2 + h;
+                this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
+            }
+            else if (this.name == 'x2axis') {
+                dim += dim2 + h;
+                this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
+            }
+            else if (this.name == 'yaxis') {
+                dim += dim2 + w;
+                this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+            else {
+                dim += dim2 + w;
+                this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+        }  
+    };
+    
+    // called with scope of axis
+    $.jqplot.CategoryAxisRenderer.prototype.pack = function(pos, offsets) {
+        var ticks = this._ticks;
+        var max = this.max;
+        var min = this.min;
+        var offmax = offsets.max;
+        var offmin = offsets.min;
+        var lshow = (this._label == null) ? false : this._label.show;
+        var i;
+		
+        for (var p in pos) {
+            this._elem.css(p, pos[p]);
+        }
+        
+        this._offsets = offsets;
+        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
+        var pixellength = offmax - offmin;
+        var unitlength = max - min;
+        
+        if (!this.reverse) {
+            // point to unit and unit to point conversions references to Plot DOM element top left corner.
+            
+            this.u2p = function(u){
+                return (u - min) * pixellength / unitlength + offmin;
+            };
+
+            this.p2u = function(p){
+                return (p - offmin) * unitlength / pixellength + min;
+            };
+                    
+            if (this.name == 'xaxis' || this.name == 'x2axis'){
+                this.series_u2p = function(u){
+                    return (u - min) * pixellength / unitlength;
+                };
+                this.series_p2u = function(p){
+                    return p * unitlength / pixellength + min;
+                };
+            }
+            
+            else {
+                this.series_u2p = function(u){
+                    return (u - max) * pixellength / unitlength;
+                };
+                this.series_p2u = function(p){
+                    return p * unitlength / pixellength + max;
+                };
+            }
+        }
+
+        else {
+            // point to unit and unit to point conversions references to Plot DOM element top left corner.
+            
+            this.u2p = function(u){
+                return offmin + (max - u) * pixellength / unitlength;
+            };
+
+            this.p2u = function(p){
+                return min + (p - offmin) * unitlength / pixellength;
+            };
+                    
+            if (this.name == 'xaxis' || this.name == 'x2axis'){
+                this.series_u2p = function(u){
+                    return (max - u) * pixellength / unitlength;
+                };
+                this.series_p2u = function(p){
+                    return p * unitlength / pixellength + max;
+                };
+            }
+            
+            else {
+                this.series_u2p = function(u){
+                    return (min - u) * pixellength / unitlength;
+                };
+                this.series_p2u = function(p){
+                    return p * unitlength / pixellength + min;
+                };
+            }
+
+        }
+            
+        
+        if (this.show) {
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {
+                        var shim;
+                        
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'xaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                    if (temp * t.angle < 0) {
+                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    }
+                                    // position at start
+                                    else {
+                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'end':
+                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                case 'start':
+                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    break;
+                                case 'middle':
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                default:
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getWidth()/2;
+                        }
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('left', val);
+                        t.pack();
+                    }
+                }
+                
+                var labeledge=['bottom', 0];
+                if (lshow) {
+                    var w = this._label._elem.outerWidth(true);
+                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
+                    if (this.name == 'xaxis') {
+                        this._label._elem.css('bottom', '0px');
+                        labeledge = ['bottom', this._label._elem.outerHeight(true)];
+                    }
+                    else {
+                        this._label._elem.css('top', '0px');
+                        labeledge = ['top', this._label._elem.outerHeight(true)];
+                    }
+                    this._label.pack();
+                }
+                
+                // draw the group labels
+                var step = parseInt(this._ticks.length/this.groups, 10);
+                for (i=0; i<this._groupLabels.length; i++) {
+                    var mid = 0;
+                    var count = 0;
+                    for (var j=i*step; j<=(i+1)*step; j++) {
+                        if (this._ticks[j]._elem && this._ticks[j].label != " ") {
+                            var t = this._ticks[j]._elem;
+                            var p = t.position();
+                            mid += p.left + t.outerWidth(true)/2;
+                            count++;
+                        }
+                    }
+                    mid = mid/count;
+                    this._groupLabels[i].css({'left':(mid - this._groupLabels[i].outerWidth(true)/2)});
+                    this._groupLabels[i].css(labeledge[0], labeledge[1]);
+                }
+            }
+            else {
+                for (i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {                        
+                        var shim;
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'yaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                case 'end':
+                                    if (temp * t.angle < 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'start':
+                                    if (t.angle > 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'middle':
+                                    // if (t.angle > 0) {
+                                    //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    // }
+                                    // else {
+                                    //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    // }
+                                    shim = -t.getHeight()/2;
+                                    break;
+                                default:
+                                    shim = -t.getHeight()/2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getHeight()/2;
+                        }
+                        
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('top', val);
+                        t.pack();
+                    }
+                }
+                
+                var labeledge=['left', 0];
+                if (lshow) {
+                    var h = this._label._elem.outerHeight(true);
+                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
+                    if (this.name == 'yaxis') {
+                        this._label._elem.css('left', '0px');
+                        labeledge = ['left', this._label._elem.outerWidth(true)];
+                    }
+                    else {
+                        this._label._elem.css('right', '0px');
+                        labeledge = ['right', this._label._elem.outerWidth(true)];
+                    }   
+                    this._label.pack();
+                }
+                
+                // draw the group labels, position top here, do left after label position.
+                var step = parseInt(this._ticks.length/this.groups, 10);
+                for (i=0; i<this._groupLabels.length; i++) {
+                    var mid = 0;
+                    var count = 0;
+                    for (var j=i*step; j<=(i+1)*step; j++) {
+                        if (this._ticks[j]._elem && this._ticks[j].label != " ") {
+                            var t = this._ticks[j]._elem;
+                            var p = t.position();
+                            mid += p.top + t.outerHeight()/2;
+                            count++;
+                        }
+                    }
+                    mid = mid/count;
+                    this._groupLabels[i].css({'top':mid - this._groupLabels[i].outerHeight()/2});
+                    this._groupLabels[i].css(labeledge[0], labeledge[1]);
+                    
+                }
+            }
+        }
+    };    
+    
+    
+})(jQuery);

+ 116 - 0
web/js/jqplot/plugins/jqplot.ciParser.js

@@ -0,0 +1,116 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.ciParser
+     * Data Renderer function which converts a custom JSON data object into jqPlot data format.
+     * Set this as a callable on the jqplot dataRenderer plot option:
+     * 
+     * > plot = $.jqplot('mychart', [data], { dataRenderer: $.jqplot.ciParser, ... });
+     * 
+     * Where data is an object in JSON format or a JSON encoded string conforming to the
+     * City Index API spec.
+     * 
+     * Note that calling the renderer function is handled internally by jqPlot.  The
+     * user does not have to call the function.  The parameters described below will
+     * automatically be passed to the ciParser function.
+     * 
+     * Parameters:
+     * data - JSON encoded string or object.
+     * plot - reference to jqPlot Plot object.
+     * 
+     * Returns:
+     * data array in jqPlot format.
+     * 
+     */
+    $.jqplot.ciParser = function (data, plot) {
+        var ret = [],
+            line,
+			temp,
+            i, j, k, kk;
+    
+         if (typeof(data) == "string") {
+             data =  $.jqplot.JSON.parse(data, handleStrings);
+         }
+ 
+         else if (typeof(data) == "object") {
+             for (k in data) {
+                 for (i=0; i<data[k].length; i++) {
+                     for (kk in data[k][i]) {
+                         data[k][i][kk] = handleStrings(kk, data[k][i][kk]);
+                     }
+                 }
+             }
+         }
+ 
+         else {
+             return null;
+         }
+ 
+         // function handleStrings
+         // Checks any JSON encoded strings to see if they are
+         // encoded dates.  If so, pull out the timestamp.
+         // Expects dates to be represented by js timestamps.
+ 
+         function handleStrings(key, value) {
+            var a;
+            if (value != null) {
+                if (value.toString().indexOf('Date') >= 0) {
+                    //here we will try to extract the ticks from the Date string in the "value" fields of JSON returned data
+                    a = /^\/Date\((-?[0-9]+)\)\/$/.exec(value);
+                    if (a) {
+                        return parseInt(a[1], 10);
+                    }
+                }
+                return value;
+            }
+         }
+ 
+        for (var prop in data) {
+            line = [];
+            temp = data[prop];
+            switch (prop) {
+                case "PriceTicks":
+                    for (i=0; i<temp.length; i++) {
+                        line.push([temp[i]['TickDate'], temp[i]['Price']]);
+                    }
+                    break;
+                case "PriceBars":
+                    for (i=0; i<temp.length; i++) {
+                        line.push([temp[i]['BarDate'], temp[i]['Open'], temp[i]['High'], temp[i]['Low'], temp[i]['Close']]);
+                    }
+                    break;
+            }
+            ret.push(line);
+        }
+        return ret;
+    };
+})(jQuery);

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.ciParser.min.js


Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.cursor.min.js


+ 225 - 0
web/js/jqplot/plugins/jqplot.dragable.js

@@ -0,0 +1,225 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    
+    /**
+     * Class: $.jqplot.Dragable
+     * Plugin to make plotted points dragable by the user.
+     */
+    $.jqplot.Dragable = function(options) {
+        // Group: Properties
+        this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false});
+        this.shapeRenderer = new $.jqplot.ShapeRenderer();
+        this.isDragging = false;
+        this.isOver = false;
+        this._ctx;
+        this._elem;
+        this._point;
+        this._gridData;
+        // prop: color
+        // CSS color spec for the dragged point (and adjacent line segment or bar).
+        this.color;
+        // prop: constrainTo
+        // Constrain dragging motion to an axis or to none.
+        // Allowable values are 'none', 'x', 'y'
+        this.constrainTo = 'none';  // 'x', 'y', or 'none';
+        $.extend(true, this, options);
+    };
+    
+    function DragCanvas() {
+        $.jqplot.GenericCanvas.call(this);
+        this.isDragging = false;
+        this.isOver = false;
+        this._neighbor;
+        this._cursors = [];
+    }
+    
+    DragCanvas.prototype = new $.jqplot.GenericCanvas();
+    DragCanvas.prototype.constructor = DragCanvas;
+    
+    
+    // called within scope of series
+    $.jqplot.Dragable.parseOptions = function (defaults, opts) {
+        var options = opts || {};
+        this.plugins.dragable = new $.jqplot.Dragable(options.dragable);
+        // since this function is called before series options are parsed,
+        // we can set this here and it will be overridden if needed.
+        this.isDragable = $.jqplot.config.enablePlugins;
+    };
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    // add a new DragCanvas object to the plot plugins to handle drawing on this new canvas.
+    $.jqplot.Dragable.postPlotDraw = function() {
+        // Memory Leaks patch    
+        if (this.plugins.dragable && this.plugins.dragable.highlightCanvas) {
+            this.plugins.dragable.highlightCanvas.resetCanvas();
+            this.plugins.dragable.highlightCanvas = null;
+        }
+
+        this.plugins.dragable = {previousCursor:'auto', isOver:false};
+        this.plugins.dragable.dragCanvas = new DragCanvas();
+        
+        this.eventCanvas._elem.before(this.plugins.dragable.dragCanvas.createElement(this._gridPadding, 'jqplot-dragable-canvas', this._plotDimensions, this));
+        var dctx = this.plugins.dragable.dragCanvas.setContext();
+    };
+    
+    //$.jqplot.preInitHooks.push($.jqplot.Dragable.init);
+    $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Dragable.parseOptions);
+    $.jqplot.postDrawHooks.push($.jqplot.Dragable.postPlotDraw);
+    $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
+    $.jqplot.eventListenerHooks.push(['jqplotMouseDown', handleDown]);
+    $.jqplot.eventListenerHooks.push(['jqplotMouseUp', handleUp]);
+
+    
+    function initDragPoint(plot, neighbor) {
+        var s = plot.series[neighbor.seriesIndex];
+        var drag = s.plugins.dragable;
+        
+        // first, init the mark renderer for the dragged point
+        var smr = s.markerRenderer;
+        var mr = drag.markerRenderer;
+        mr.style = smr.style;
+        mr.lineWidth = smr.lineWidth + 2.5;
+        mr.size = smr.size + 5;
+        if (!drag.color) {
+            var rgba = $.jqplot.getColorComponents(smr.color);
+            var newrgb = [rgba[0], rgba[1], rgba[2]];
+            var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
+            drag.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
+        }
+        mr.color = drag.color;
+        mr.init();
+
+        var start = (neighbor.pointIndex > 0) ? neighbor.pointIndex - 1 : 0;
+        var end = neighbor.pointIndex+2;
+        drag._gridData = s.gridData.slice(start, end);
+    }
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (plot.plugins.dragable.dragCanvas.isDragging) {
+            var dc = plot.plugins.dragable.dragCanvas;
+            var dp = dc._neighbor;
+            var s = plot.series[dp.seriesIndex];
+            var drag = s.plugins.dragable;
+            var gd = s.gridData;
+            
+            // compute the new grid position with any constraints.
+            var x = (drag.constrainTo == 'y') ? dp.gridData[0] : gridpos.x;
+            var y = (drag.constrainTo == 'x') ? dp.gridData[1] : gridpos.y;
+            
+            // compute data values for any listeners.
+            var xu = s._xaxis.series_p2u(x);
+            var yu = s._yaxis.series_p2u(y);
+            
+            // clear the canvas then redraw effect at new position.
+            var ctx = dc._ctx;
+            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+            
+            // adjust our gridData for the new mouse position
+            if (dp.pointIndex > 0) {
+                drag._gridData[1] = [x, y];
+            }
+            else {
+                drag._gridData[0] = [x, y];
+            }
+            plot.series[dp.seriesIndex].draw(dc._ctx, {gridData:drag._gridData, shadow:false, preventJqPlotSeriesDrawTrigger:true, color:drag.color, markerOptions:{color:drag.color, shadow:false}, trendline:{show:false}});
+            plot.target.trigger('jqplotSeriesPointChange', [dp.seriesIndex, dp.pointIndex, [xu,yu], [x,y]]);
+        }
+        else if (neighbor != null) {
+            var series = plot.series[neighbor.seriesIndex];
+            if (series.isDragable) {
+                var dc = plot.plugins.dragable.dragCanvas;
+                if (!dc.isOver) {
+                    dc._cursors.push(ev.target.style.cursor);
+                    ev.target.style.cursor = "pointer";
+                }
+                dc.isOver = true;
+            }
+        }
+        else if (neighbor == null) {
+            var dc = plot.plugins.dragable.dragCanvas;
+            if (dc.isOver) {
+                ev.target.style.cursor = dc._cursors.pop();
+                dc.isOver = false;
+            }
+        }
+    }
+    
+    function handleDown(ev, gridpos, datapos, neighbor, plot) {
+        var dc = plot.plugins.dragable.dragCanvas;
+        dc._cursors.push(ev.target.style.cursor);
+        if (neighbor != null) {
+            var s = plot.series[neighbor.seriesIndex];
+            var drag = s.plugins.dragable;
+            if (s.isDragable && !dc.isDragging) {
+                dc._neighbor = neighbor;
+                dc.isDragging = true;
+                initDragPoint(plot, neighbor);
+                drag.markerRenderer.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], dc._ctx);
+                ev.target.style.cursor = "move";
+                plot.target.trigger('jqplotDragStart', [neighbor.seriesIndex, neighbor.pointIndex, gridpos, datapos]);
+            }
+        }
+        // Just in case of a hickup, we'll clear the drag canvas and reset.
+        else {
+           var ctx = dc._ctx;
+           ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+           dc.isDragging = false;
+        }
+    }
+    
+    function handleUp(ev, gridpos, datapos, neighbor, plot) {
+        if (plot.plugins.dragable.dragCanvas.isDragging) {
+            var dc = plot.plugins.dragable.dragCanvas;
+            // clear the canvas
+            var ctx = dc._ctx;
+            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+            dc.isDragging = false;
+            // redraw the series canvas at the new point.
+            var dp = dc._neighbor;
+            var s = plot.series[dp.seriesIndex];
+            var drag = s.plugins.dragable;
+            // compute the new grid position with any constraints.
+            var x = (drag.constrainTo == 'y') ? dp.data[0] : datapos[s.xaxis];
+            var y = (drag.constrainTo == 'x') ? dp.data[1] : datapos[s.yaxis];
+            // var x = datapos[s.xaxis];
+            // var y = datapos[s.yaxis];
+            s.data[dp.pointIndex][0] = x;
+            s.data[dp.pointIndex][1] = y;
+            plot.drawSeries({preventJqPlotSeriesDrawTrigger:true}, dp.seriesIndex);
+            dc._neighbor = null;
+            ev.target.style.cursor = dc._cursors.pop();
+            plot.target.trigger('jqplotDragStop', [gridpos, datapos]);
+        }
+    }
+})(jQuery);

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.dragable.min.js


Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.enhancedLegendRenderer.min.js


Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.funnelRenderer.min.js


+ 465 - 0
web/js/jqplot/plugins/jqplot.highlighter.js

@@ -0,0 +1,465 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMove]);
+    
+    /**
+     * Class: $.jqplot.Highlighter
+     * Plugin which will highlight data points when they are moused over.
+     * 
+     * To use this plugin, include the js
+     * file in your source:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.highlighter.js"></script>
+     * 
+     * A tooltip providing information about the data point is enabled by default.
+     * To disable the tooltip, set "showTooltip" to false.
+     * 
+     * You can control what data is displayed in the tooltip with various
+     * options.  The "tooltipAxes" option controls wether the x, y or both
+     * data values are displayed.
+     * 
+     * Some chart types (e.g. hi-low-close) have more than one y value per
+     * data point. To display the additional values in the tooltip, set the
+     * "yvalues" option to the desired number of y values present (3 for a hlc chart).
+     * 
+     * By default, data values will be formatted with the same formatting
+     * specifiers as used to format the axis ticks.  A custom format code
+     * can be supplied with the tooltipFormatString option.  This will apply 
+     * to all values in the tooltip.  
+     * 
+     * For more complete control, the "formatString" option can be set.  This
+     * Allows conplete control over tooltip formatting.  Values are passed to
+     * the format string in an order determined by the "tooltipAxes" and "yvalues"
+     * options.  So, if you have a hi-low-close chart and you just want to display 
+     * the hi-low-close values in the tooltip, you could set a formatString like:
+     * 
+     * > highlighter: {
+     * >     tooltipAxes: 'y',
+     * >     yvalues: 3,
+     * >     formatString:'<table class="jqplot-highlighter">
+     * >         <tr><td>hi:</td><td>%s</td></tr>
+     * >         <tr><td>low:</td><td>%s</td></tr>
+     * >         <tr><td>close:</td><td>%s</td></tr></table>'
+     * > }
+     * 
+     */
+    $.jqplot.Highlighter = function(options) {
+        // Group: Properties
+        //
+        //prop: show
+        // true to show the highlight.
+        this.show = $.jqplot.config.enablePlugins;
+        // prop: markerRenderer
+        // Renderer used to draw the marker of the highlighted point.
+        // Renderer will assimilate attributes from the data point being highlighted,
+        // so no attributes need set on the renderer directly.
+        // Default is to turn off shadow drawing on the highlighted point.
+        this.markerRenderer = new $.jqplot.MarkerRenderer({shadow:false});
+        // prop: showMarker
+        // true to show the marker
+        this.showMarker  = true;
+        // prop: lineWidthAdjust
+        // Pixels to add to the lineWidth of the highlight.
+        this.lineWidthAdjust = 2.5;
+        // prop: sizeAdjust
+        // Pixels to add to the overall size of the highlight.
+        this.sizeAdjust = 5;
+        // prop: showTooltip
+        // Show a tooltip with data point values.
+        this.showTooltip = true;
+        // prop: tooltipLocation
+        // Where to position tooltip, 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'
+        this.tooltipLocation = 'nw';
+        // prop: fadeTooltip
+        // true = fade in/out tooltip, flase = show/hide tooltip
+        this.fadeTooltip = true;
+        // prop: tooltipFadeSpeed
+        // 'slow', 'def', 'fast', or number of milliseconds.
+        this.tooltipFadeSpeed = "fast";
+        // prop: tooltipOffset
+        // Pixel offset of tooltip from the highlight.
+        this.tooltipOffset = 2;
+        // prop: tooltipAxes
+        // Which axes to display in tooltip, 'x', 'y' or 'both', 'xy' or 'yx'
+        // 'both' and 'xy' are equivalent, 'yx' reverses order of labels.
+        this.tooltipAxes = 'both';
+        // prop; tooltipSeparator
+        // String to use to separate x and y axes in tooltip.
+        this.tooltipSeparator = ', ';
+        // prop; tooltipContentEditor
+        // Function used to edit/augment/replace the formatted tooltip contents.
+        // Called as str = tooltipContentEditor(str, seriesIndex, pointIndex)
+        // where str is the generated tooltip html and seriesIndex and pointIndex identify
+        // the data point being highlighted. Should return the html for the tooltip contents.
+        this.tooltipContentEditor = null;
+        // prop: useAxesFormatters
+        // Use the x and y axes formatters to format the text in the tooltip.
+        this.useAxesFormatters = true;
+        // prop: tooltipFormatString
+        // sprintf format string for the tooltip.
+        // Uses Ash Searle's javascript sprintf implementation
+        // found here: http://hexmen.com/blog/2007/03/printf-sprintf/
+        // See http://perldoc.perl.org/functions/sprintf.html for reference.
+        // Additional "p" and "P" format specifiers added by Chris Leonello.
+        this.tooltipFormatString = '%.5P';
+        // prop: formatString
+        // alternative to tooltipFormatString
+        // will format the whole tooltip text, populating with x, y values as
+        // indicated by tooltipAxes option.  So, you could have a tooltip like:
+        // 'Date: %s, number of cats: %d' to format the whole tooltip at one go.
+        // If useAxesFormatters is true, values will be formatted according to
+        // Axes formatters and you can populate your tooltip string with 
+        // %s placeholders.
+        this.formatString = null;
+        // prop: yvalues
+        // Number of y values to expect in the data point array.
+        // Typically this is 1.  Certain plots, like OHLC, will
+        // have more y values in each data point array.
+        this.yvalues = 1;
+        // prop: bringSeriesToFront
+        // This option requires jQuery 1.4+
+        // True to bring the series of the highlighted point to the front
+        // of other series.
+        this.bringSeriesToFront = false;
+        this._tooltipElem;
+        this.isHighlighting = false;
+        this.currentNeighbor = null;
+
+        $.extend(true, this, options);
+    };
+    
+    var locations = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
+    var locationIndicies = {'nw':0, 'n':1, 'ne':2, 'e':3, 'se':4, 's':5, 'sw':6, 'w':7};
+    var oppositeLocations = ['se', 's', 'sw', 'w', 'nw', 'n', 'ne', 'e'];
+    
+    // axis.renderer.tickrenderer.formatter
+    
+    // called with scope of plot
+    $.jqplot.Highlighter.init = function (target, data, opts){
+        var options = opts || {};
+        // add a highlighter attribute to the plot
+        this.plugins.highlighter = new $.jqplot.Highlighter(options.highlighter);
+    };
+    
+    // called within scope of series
+    $.jqplot.Highlighter.parseOptions = function (defaults, options) {
+        // Add a showHighlight option to the series 
+        // and set it to true by default.
+        this.showHighlight = true;
+    };
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    $.jqplot.Highlighter.postPlotDraw = function() {
+        // Memory Leaks patch    
+        if (this.plugins.highlighter && this.plugins.highlighter.highlightCanvas) {
+            this.plugins.highlighter.highlightCanvas.resetCanvas();
+            this.plugins.highlighter.highlightCanvas = null;
+        }
+
+        if (this.plugins.highlighter && this.plugins.highlighter._tooltipElem) {
+            this.plugins.highlighter._tooltipElem.emptyForce();
+            this.plugins.highlighter._tooltipElem = null;
+        }
+
+        this.plugins.highlighter.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        this.eventCanvas._elem.before(this.plugins.highlighter.highlightCanvas.createElement(this._gridPadding, 'jqplot-highlight-canvas', this._plotDimensions, this));
+        this.plugins.highlighter.highlightCanvas.setContext();
+
+        var elem = document.createElement('div');
+        this.plugins.highlighter._tooltipElem = $(elem);
+        elem = null;
+        this.plugins.highlighter._tooltipElem.addClass('jqplot-highlighter-tooltip');
+        this.plugins.highlighter._tooltipElem.css({position:'absolute', display:'none'});
+        
+        this.eventCanvas._elem.before(this.plugins.highlighter._tooltipElem);
+    };
+    
+    $.jqplot.preInitHooks.push($.jqplot.Highlighter.init);
+    $.jqplot.preParseSeriesOptionsHooks.push($.jqplot.Highlighter.parseOptions);
+    $.jqplot.postDrawHooks.push($.jqplot.Highlighter.postPlotDraw);
+    
+    function draw(plot, neighbor) {
+        var hl = plot.plugins.highlighter;
+        var s = plot.series[neighbor.seriesIndex];
+        var smr = s.markerRenderer;
+        var mr = hl.markerRenderer;
+        mr.style = smr.style;
+        mr.lineWidth = smr.lineWidth + hl.lineWidthAdjust;
+        mr.size = smr.size + hl.sizeAdjust;
+        var rgba = $.jqplot.getColorComponents(smr.color);
+        var newrgb = [rgba[0], rgba[1], rgba[2]];
+        var alpha = (rgba[3] >= 0.6) ? rgba[3]*0.6 : rgba[3]*(2-rgba[3]);
+        mr.color = 'rgba('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+','+alpha+')';
+        mr.init();
+        mr.draw(s.gridData[neighbor.pointIndex][0], s.gridData[neighbor.pointIndex][1], hl.highlightCanvas._ctx);
+    }
+    
+    function showTooltip(plot, series, neighbor) {
+        // neighbor looks like: {seriesIndex: i, pointIndex:j, gridData:p, data:s.data[j]}
+        // gridData should be x,y pixel coords on the grid.
+        // add the plot._gridPadding to that to get x,y in the target.
+        var hl = plot.plugins.highlighter;
+        var elem = hl._tooltipElem;
+        var serieshl = series.highlighter || {};
+
+        var opts = $.extend(true, {}, hl, serieshl);
+
+        if (opts.useAxesFormatters) {
+            var xf = series._xaxis._ticks[0].formatter;
+            var yf = series._yaxis._ticks[0].formatter;
+            var xfstr = series._xaxis._ticks[0].formatString;
+            var yfstr = series._yaxis._ticks[0].formatString;
+            var str;
+            var xstr = xf(xfstr, neighbor.data[0]);
+            var ystrs = [];
+            for (var i=1; i<opts.yvalues+1; i++) {
+                ystrs.push(yf(yfstr, neighbor.data[i]));
+            }
+            if (typeof opts.formatString === 'string') {
+                switch (opts.tooltipAxes) {
+                    case 'both':
+                    case 'xy':
+                        ystrs.unshift(xstr);
+                        ystrs.unshift(opts.formatString);
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
+                        break;
+                    case 'yx':
+                        ystrs.push(xstr);
+                        ystrs.unshift(opts.formatString);
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
+                        break;
+                    case 'x':
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString, xstr]);
+                        break;
+                    case 'y':
+                        ystrs.unshift(opts.formatString);
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
+                        break;
+                    default: // same as xy
+                        ystrs.unshift(xstr);
+                        ystrs.unshift(opts.formatString);
+                        str = $.jqplot.sprintf.apply($.jqplot.sprintf, ystrs);
+                        break;
+                } 
+            }
+            else {
+                switch (opts.tooltipAxes) {
+                    case 'both':
+                    case 'xy':
+                        str = xstr;
+                        for (var i=0; i<ystrs.length; i++) {
+                            str += opts.tooltipSeparator + ystrs[i];
+                        }
+                        break;
+                    case 'yx':
+                        str = '';
+                        for (var i=0; i<ystrs.length; i++) {
+                            str += ystrs[i] + opts.tooltipSeparator;
+                        }
+                        str += xstr;
+                        break;
+                    case 'x':
+                        str = xstr;
+                        break;
+                    case 'y':
+                        str = ystrs.join(opts.tooltipSeparator);
+                        break;
+                    default: // same as 'xy'
+                        str = xstr;
+                        for (var i=0; i<ystrs.length; i++) {
+                            str += opts.tooltipSeparator + ystrs[i];
+                        }
+                        break;
+                    
+                }                
+            }
+        }
+        else {
+            var str;
+            if (typeof opts.formatString ===  'string') {
+                str = $.jqplot.sprintf.apply($.jqplot.sprintf, [opts.formatString].concat(neighbor.data));
+            }
+
+            else {
+                if (opts.tooltipAxes == 'both' || opts.tooltipAxes == 'xy') {
+                    str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]);
+                }
+                else if (opts.tooltipAxes == 'yx') {
+                    str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]) + opts.tooltipSeparator + $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]);
+                }
+                else if (opts.tooltipAxes == 'x') {
+                    str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[0]);
+                }
+                else if (opts.tooltipAxes == 'y') {
+                    str = $.jqplot.sprintf(opts.tooltipFormatString, neighbor.data[1]);
+                } 
+            }
+        }
+        if ($.isFunction(opts.tooltipContentEditor)) {
+            // args str, seriesIndex, pointIndex are essential so the hook can look up
+            // extra data for the point.
+            str = opts.tooltipContentEditor(str, neighbor.seriesIndex, neighbor.pointIndex, plot);
+        }
+        elem.html(str);
+        var gridpos = {x:neighbor.gridData[0], y:neighbor.gridData[1]};
+        var ms = 0;
+        var fact = 0.707;
+        if (series.markerRenderer.show == true) { 
+            ms = (series.markerRenderer.size + opts.sizeAdjust)/2;
+        }
+		
+		var loc = locations;
+		if (series.fillToZero && series.fill && neighbor.data[1] < 0) {
+			loc = oppositeLocations;
+		}
+		
+        switch (loc[locationIndicies[opts.tooltipLocation]]) {
+            case 'nw':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms;
+                var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms;
+                break;
+            case 'n':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
+                var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - ms;
+                break;
+            case 'ne':
+                var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms;
+                var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms;
+                break;
+            case 'e':
+                var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + ms;
+                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
+                break;
+            case 'se':
+                var x = gridpos.x + plot._gridPadding.left + opts.tooltipOffset + fact * ms;
+                var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms;
+                break;
+            case 's':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true)/2;
+                var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + ms;
+                break;
+            case 'sw':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms;
+                var y = gridpos.y + plot._gridPadding.top + opts.tooltipOffset + fact * ms;
+                break;
+            case 'w':
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - ms;
+                var y = gridpos.y + plot._gridPadding.top - elem.outerHeight(true)/2;
+                break;
+            default: // same as 'nw'
+                var x = gridpos.x + plot._gridPadding.left - elem.outerWidth(true) - opts.tooltipOffset - fact * ms;
+                var y = gridpos.y + plot._gridPadding.top - opts.tooltipOffset - elem.outerHeight(true) - fact * ms;
+                break;
+        }
+        elem.css('left', x);
+        elem.css('top', y);
+        if (opts.fadeTooltip) {
+            // Fix for stacked up animations.  Thnanks Trevor!
+            elem.stop(true,true).fadeIn(opts.tooltipFadeSpeed);
+        }
+        else {
+            elem.show();
+        }
+        elem = null;
+        
+    }
+    
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        var hl = plot.plugins.highlighter;
+        var c = plot.plugins.cursor;
+        if (hl.show) {
+            if (neighbor == null && hl.isHighlighting) {
+                var evt = jQuery.Event('jqplotHighlighterUnhighlight');
+                plot.target.trigger(evt);
+
+                var ctx = hl.highlightCanvas._ctx;
+                ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+                if (hl.fadeTooltip) {
+                    hl._tooltipElem.fadeOut(hl.tooltipFadeSpeed);
+                }
+                else {
+                    hl._tooltipElem.hide();
+                }
+                if (hl.bringSeriesToFront) {
+                    plot.restorePreviousSeriesOrder();
+                }
+                hl.isHighlighting = false;
+                hl.currentNeighbor = null;
+                ctx = null;
+            }
+            else if (neighbor != null && plot.series[neighbor.seriesIndex].showHighlight && !hl.isHighlighting) {
+                var evt = jQuery.Event('jqplotHighlighterHighlight');
+                evt.which = ev.which;
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data, plot];
+                plot.target.trigger(evt, ins);
+
+                hl.isHighlighting = true;
+                hl.currentNeighbor = neighbor;
+                if (hl.showMarker) {
+                    draw(plot, neighbor);
+                }
+                if (hl.showTooltip && (!c || !c._zoom.started)) {
+                    showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor);
+                }
+                if (hl.bringSeriesToFront) {
+                    plot.moveSeriesToFront(neighbor.seriesIndex);
+                }
+            }
+            // check to see if we're highlighting the wrong point.
+            else if (neighbor != null && hl.isHighlighting && hl.currentNeighbor != neighbor) {
+                // highlighting the wrong point.
+
+                // if new series allows highlighting, highlight new point.
+                if (plot.series[neighbor.seriesIndex].showHighlight) {
+                    var ctx = hl.highlightCanvas._ctx;
+                    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+                    hl.isHighlighting = true;
+                    hl.currentNeighbor = neighbor;
+                    if (hl.showMarker) {
+                        draw(plot, neighbor);
+                    }
+                    if (hl.showTooltip && (!c || !c._zoom.started)) {
+                        showTooltip(plot, plot.series[neighbor.seriesIndex], neighbor);
+                    }
+                    if (hl.bringSeriesToFront) {
+                        plot.moveSeriesToFront(neighbor.seriesIndex);
+                    }                    
+                }                
+            }
+        }
+    }
+})(jQuery);

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.highlighter.min.js


+ 475 - 0
web/js/jqplot/plugins/jqplot.json2.js

@@ -0,0 +1,475 @@
+/*
+    2010-11-01 Chris Leonello
+    
+    Slightly modified version of the original json2.js to put JSON
+    functions under the $.jqplot namespace.
+    
+    licensing and orignal comments follow:
+    
+    http://www.JSON.org/json2.js
+    2010-08-25
+    
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+
+
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
+
+        $.jqplot.JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the value
+
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
+
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                };
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representations, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            $.jqplot.JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            the indentation will be that many spaces.
+
+            Example:
+
+            text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = $.jqplot.JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+            text = $.jqplot.JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
+
+        $.jqplot.JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = $.jqplot.JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+            myData = $.jqplot.JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+*/
+
+(function($) {
+
+    $.jqplot.JSON = window.JSON;
+
+    if (!window.JSON) {
+        $.jqplot.JSON = {};
+    }
+    
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    if (typeof Date.prototype.toJSON !== 'function') {
+
+        Date.prototype.toJSON = function (key) {
+
+            return isFinite(this.valueOf()) ?
+                   this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z' : null;
+        };
+
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
+
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+
+// Produce a string from holder[key].
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
+
+        case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+            return isFinite(value) ? String(value) : 'null';
+
+        case 'boolean':
+        case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+            return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+        case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+            if (!value) {
+                return 'null';
+            }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+            gap += indent;
+            partial = [];
+
+// Is the value an array?
+
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof $.jqplot.JSON.stringify !== 'function') {
+        $.jqplot.JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = '';
+            indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('$.jqplot.JSON.stringify');
+            }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+            return str('', {'': value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof $.jqplot.JSON.parse !== 'function') {
+        $.jqplot.JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            text = String(text);
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+            if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError('$.jqplot.JSON.parse');
+        };
+    }
+})(jQuery);

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.json2.min.js


+ 529 - 0
web/js/jqplot/plugins/jqplot.logAxisRenderer.js

@@ -0,0 +1,529 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    /**
+    *  class: $.jqplot.LogAxisRenderer
+    *  A plugin for a jqPlot to render a logarithmic axis.
+    * 
+    *  To use this renderer, include the plugin in your source
+    *  > <script type="text/javascript" language="javascript" src="plugins/jqplot.logAxisRenderer.js"></script>
+    *  
+    *  and supply the appropriate options to your plot
+    *  
+    *  > {axes:{xaxis:{renderer:$.jqplot.LogAxisRenderer}}}
+    **/ 
+    $.jqplot.LogAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+        // prop: axisDefaults
+        // Default properties which will be applied directly to the series.
+        //
+        // Group: Properties
+        //
+        // Properties
+        //
+        // base - the logarithmic base, commonly 2, 10 or Math.E
+        // tickDistribution - Deprecated.  "power" distribution of ticks
+        // always used.  Option has no effect.
+        this.axisDefaults = {
+            base : 10,
+            tickDistribution :'power'
+        };
+    };
+    
+    $.jqplot.LogAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.LogAxisRenderer.prototype.constructor = $.jqplot.LogAxisRenderer;
+    
+    $.jqplot.LogAxisRenderer.prototype.init = function(options) {
+        // prop: drawBaseline
+        // True to draw the axis baseline.
+        this.drawBaseline = true;
+        // prop: minorTicks
+        // Number of ticks to add between "major" ticks.
+        // Major ticks are ticks supplied by user or auto computed.
+        // Minor ticks cannot be created by user.
+        this.minorTicks = 'auto';
+        this._scalefact = 1.0;
+
+        $.extend(true, this, options);
+
+        this._autoFormatString = '%d';
+        this._overrideFormatString = false;
+
+        for (var d in this.renderer.axisDefaults) {
+            if (this[d] == null) {
+                this[d] = this.renderer.axisDefaults[d];
+            }
+        }
+
+        this.resetDataBounds();
+    };
+    
+    $.jqplot.LogAxisRenderer.prototype.createTicks = function(plot) {
+        // we're are operating on an axis here
+        var ticks = this._ticks;
+        var userTicks = this.ticks;
+        var name = this.name;
+        var db = this._dataBounds;
+        var dim = (this.name.charAt(0) === 'x') ? this._plotDimensions.width : this._plotDimensions.height;
+        var interval;
+        var min, max;
+        var pos1, pos2;
+        var tt, i;
+
+        var threshold = 30;
+        // For some reason scalefactor is screwing up ticks.
+        this._scalefact =  (Math.max(dim, threshold+1) - threshold)/300;
+
+        // if we already have ticks, use them.
+        // ticks must be in order of increasing value.
+        if (userTicks.length) {
+            // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
+            for (i=0; i<userTicks.length; i++){
+                var ut = userTicks[i];
+                var t = new this.tickRenderer(this.tickOptions);
+                if (ut.constructor == Array) {
+                    t.value = ut[0];
+                    t.label = ut[1];
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(ut[0], this.name);
+                    this._ticks.push(t);
+                }
+
+                else if ($.isPlainObject(ut)) {
+                    $.extend(true, t, ut);
+                    t.axis = this.name;
+                    this._ticks.push(t);
+                }
+                
+                else {
+                    t.value = ut;
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(ut, this.name);
+                    this._ticks.push(t);
+                }
+            }
+            this.numberTicks = userTicks.length;
+            this.min = this._ticks[0].value;
+            this.max = this._ticks[this.numberTicks-1].value;
+        }
+        
+        // we don't have any ticks yet, let's make some!
+        else if (this.min == null && this.max == null) {
+            min = db.min * (2 - this.padMin);
+            max = db.max * this.padMax;
+            
+            // if min and max are same, space them out a bit
+            if (min == max) {
+                var adj = 0.05;
+                min = min*(1-adj);
+                max = max*(1+adj);
+            }
+            
+            // perform some checks
+            if (this.min != null && this.min <= 0) {
+                throw('log axis minimum must be greater than 0');
+            }
+            if (this.max != null && this.max <= 0) {
+                throw('log axis maximum must be greater than 0');
+            }
+
+            function findCeil (val) {
+                var order = Math.pow(10, Math.floor(Math.log(val)/Math.LN10));
+                return Math.ceil(val/order) * order;
+            }
+
+            function findFloor(val) {
+                var order = Math.pow(10, Math.floor(Math.log(val)/Math.LN10));
+                return Math.floor(val/order) * order;
+            }
+
+            // var range = max - min;
+            var rmin, rmax;
+
+            // for power distribution, open up range to get a nice power of axis.renderer.base.
+            // power distribution won't respect the user's min/max settings.
+            rmin = Math.pow(this.base, Math.floor(Math.log(min)/Math.log(this.base)));
+            rmax = Math.pow(this.base, Math.ceil(Math.log(max)/Math.log(this.base)));
+
+            // // if min and max are same, space them out a bit
+            // if (rmin === rmax) {
+            //     var adj = 0.05;
+            //     rmin = rmin*(1-adj);
+            //     rmax = rmax*(1+adj);
+            // }
+
+            var order = Math.round(Math.log(rmin)/Math.LN10);
+
+            if (this.tickOptions == null || !this.tickOptions.formatString) {
+                this._overrideFormatString = true;
+            }
+
+            this.min = rmin;
+            this.max = rmax;
+            var range = this.max - this.min;            
+
+            var minorTicks = (this.minorTicks === 'auto') ? 0 : this.minorTicks;
+            var numberTicks;
+            if (this.numberTicks == null){
+                if (dim > 140) {
+                    numberTicks = Math.round(Math.log(this.max/this.min)/Math.log(this.base) + 1);
+                    if (numberTicks < 2) {
+                        numberTicks = 2;
+                    }
+                    if (minorTicks === 0) {
+                        var temp = dim/(numberTicks - 1);
+                        if (temp < 100) {
+                            minorTicks = 0;
+                        }
+                        else if (temp < 190) {
+                            minorTicks = 1;
+                        }
+                        else if (temp < 250) {
+                            minorTicks = 3;
+                        }
+                        else if (temp < 600) {
+                            minorTicks = 4;
+                        }
+                        else {
+                            minorTicks = 9;
+                        }
+                    }
+                }
+                else {
+                    numberTicks = 2;
+                    if (minorTicks === 0) {
+                        minorTicks = 1;
+                    }
+                    minorTicks = 0;
+                }
+            }
+            else {
+                numberTicks = this.numberTicks;
+            }
+
+            if (order >= 0 && minorTicks !== 3) {
+                this._autoFormatString = '%d';
+            }
+            // Adjust format string for case with 3 ticks where we'll have like 1, 2.5, 5, 7.5, 10
+            else if (order <= 0 && minorTicks === 3) {
+                var temp = -(order - 1);
+                this._autoFormatString = '%.'+ Math.abs(order-1) + 'f';
+            }
+
+            // Adjust format string for values less than 1.
+            else if (order < 0) {
+                var temp = -order;
+                this._autoFormatString = '%.'+ Math.abs(order) + 'f';
+            }
+
+            else {
+                this._autoFormatString = '%d';
+            }
+
+            var to, t, val, tt1, spread, interval;
+            for (var i=0; i<numberTicks; i++){
+                tt = Math.pow(this.base, i - numberTicks + 1) * this.max;
+
+                t = new this.tickRenderer(this.tickOptions);
+            
+                if (this._overrideFormatString) {
+                    t.formatString = this._autoFormatString;
+                }
+                
+                if (!this.showTicks) {
+                    t.showLabel = false;
+                    t.showMark = false;
+                }
+                else if (!this.showTickMarks) {
+                    t.showMark = false;
+                }
+                t.setTick(tt, this.name);
+                this._ticks.push(t);
+
+                if (minorTicks && i<numberTicks-1) {
+                    tt1 = Math.pow(this.base, i - numberTicks + 2) * this.max;
+                    spread = tt1 - tt;
+                    interval = tt1 / (minorTicks+1);
+                    for (var j=minorTicks-1; j>=0; j--) {
+                        val = tt1-interval*(j+1);
+                        t = new this.tickRenderer(this.tickOptions);
+            
+                        if (this._overrideFormatString && this._autoFormatString != '') {
+                            t.formatString = this._autoFormatString;
+                        }
+                        if (!this.showTicks) {
+                            t.showLabel = false;
+                            t.showMark = false;
+                        }
+                        else if (!this.showTickMarks) {
+                            t.showMark = false;
+                        }
+                        t.setTick(val, this.name);
+                        this._ticks.push(t);
+                    }
+                }       
+            }     
+        }
+
+        // min and max are set as would be the case with zooming
+        else if (this.min != null && this.max != null) {
+            var opts = $.extend(true, {}, this.tickOptions, {name: this.name, value: null});
+            var nt, ti;
+            // don't have an interval yet, pick one that gives the most
+            // "round" ticks we can get.
+            if (this.numberTicks == null && this.tickInterval == null) {
+                // var threshold = 30;
+                var tdim = Math.max(dim, threshold+1);
+                var nttarget =  Math.ceil((tdim-threshold)/35 + 1);
+
+                var ret = $.jqplot.LinearTickGenerator.bestConstrainedInterval(this.min, this.max, nttarget);
+
+                this._autoFormatString = ret[3];
+                nt = ret[2];
+                ti = ret[4];
+
+                for (var i=0; i<nt; i++) {
+                    opts.value = this.min + i * ti;
+                    t = new this.tickRenderer(opts);
+                    
+                    if (this._overrideFormatString && this._autoFormatString != '') {
+                        t.formatString = this._autoFormatString;
+                    }
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    this._ticks.push(t);
+                }
+            }
+
+            // for loose zoom, number ticks and interval are also set.
+            else if (this.numberTicks != null && this.tickInterval != null) {
+                nt = this.numberTicks;
+                for (var i=0; i<nt; i++) {
+                    opts.value = this.min + i * this.tickInterval;
+                    t = new this.tickRenderer(opts);
+                    
+                    if (this._overrideFormatString && this._autoFormatString != '') {
+                        t.formatString = this._autoFormatString;
+                    }
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    this._ticks.push(t);
+                }
+            }
+        }
+    };
+    
+    $.jqplot.LogAxisRenderer.prototype.pack = function(pos, offsets) {
+        var lb = parseInt(this.base, 10);
+        var ticks = this._ticks;
+        var trans = function (v) { return Math.log(v)/Math.log(lb); };
+        var invtrans = function (v) { return Math.pow(Math.E, (Math.log(lb)*v)); };
+        var max = trans(this.max);
+        var min = trans(this.min);
+        var offmax = offsets.max;
+        var offmin = offsets.min;
+        var lshow = (this._label == null) ? false : this._label.show;
+        
+        for (var p in pos) {
+            this._elem.css(p, pos[p]);
+        }
+        
+        this._offsets = offsets;
+        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
+        var pixellength = offmax - offmin;
+        var unitlength = max - min;
+        
+        // point to unit and unit to point conversions references to Plot DOM element top left corner.
+        this.p2u = function(p){
+            return invtrans((p - offmin) * unitlength / pixellength + min);
+        };
+        
+        this.u2p = function(u){
+            return (trans(u) - min) * pixellength / unitlength + offmin;
+        };
+        
+        if (this.name == 'xaxis' || this.name == 'x2axis'){
+            this.series_u2p = function(u){
+                return (trans(u) - min) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return invtrans(p * unitlength / pixellength + min);
+            };
+        }
+        // yaxis is max at top of canvas.
+        else {
+            this.series_u2p = function(u){
+                return (trans(u) - max) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return invtrans(p * unitlength / pixellength + max);
+            };
+        }
+        
+        if (this.show) {
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                for (var i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {
+                        var shim;
+                        
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                    if (t.angle < 0) {
+                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    }
+                                    // position at start
+                                    else {
+                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'end':
+                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                case 'start':
+                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    break;
+                                case 'middle':
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                default:
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getWidth()/2;
+                        }
+                        // var shim = t.getWidth()/2;
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('left', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var w = this._label._elem.outerWidth(true);
+                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
+                    if (this.name == 'xaxis') {
+                        this._label._elem.css('bottom', '0px');
+                    }
+                    else {
+                        this._label._elem.css('top', '0px');
+                    }
+                    this._label.pack();
+                }
+            }
+            else {
+                for (var i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {                        
+                        var shim;
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                case 'end':
+                                    if (t.angle < 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'start':
+                                    if (t.angle > 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'middle':
+                                    // if (t.angle > 0) {
+                                    //     shim = -t.getHeight()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    // }
+                                    // else {
+                                    //     shim = -t.getHeight()/2 - t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    // }
+                                    shim = -t.getHeight()/2;
+                                    break;
+                                default:
+                                    shim = -t.getHeight()/2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getHeight()/2;
+                        }
+                        
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('top', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var h = this._label._elem.outerHeight(true);
+                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
+                    if (this.name == 'yaxis') {
+                        this._label._elem.css('left', '0px');
+                    }
+                    else {
+                        this._label._elem.css('right', '0px');
+                    }   
+                    this._label.pack();
+                }
+            }
+        }        
+    };
+})(jQuery);

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.logAxisRenderer.min.js


+ 611 - 0
web/js/jqplot/plugins/jqplot.mekkoAxisRenderer.js

@@ -0,0 +1,611 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    // class: $.jqplot.MekkoAxisRenderer
+    // An axis renderer for a Mekko chart.
+    // Should be used with a Mekko chart where the mekkoRenderer is used on the series.
+    // Displays the Y axis as a range from 0 to 1 (0 to 100%) and the x axis with a tick
+    // for each series scaled to the sum of all the y values.
+    $.jqplot.MekkoAxisRenderer = function() {
+    };
+    
+    // called with scope of axis object.
+    $.jqplot.MekkoAxisRenderer.prototype.init = function(options){
+        // prop: tickMode
+        // How to space the ticks on the axis.
+        // 'bar' will place a tick at the width of each bar.  
+        // This is the default for the x axis.
+        // 'even' will place ticks at even intervals.  This is
+        // the default for x2 axis and y axis.  y axis cannot be changed.
+        this.tickMode;
+        // prop: barLabelRenderer
+        // renderer to use to draw labels under each bar.
+        this.barLabelRenderer = $.jqplot.AxisLabelRenderer;
+        // prop: barLabels
+        // array of labels to put under each bar.
+        this.barLabels = this.barLabels || [];
+        // prop: barLabelOptions
+        // options object to pass to the bar label renderer.
+        this.barLabelOptions = {};
+        this.tickOptions = $.extend(true, {showGridline:false}, this.tickOptions);
+        this._barLabels = [];
+        $.extend(true, this, options);
+        if (this.name == 'yaxis') {
+            this.tickOptions.formatString = this.tickOptions.formatString || "%d\%";
+        }
+        var db = this._dataBounds;
+        db.min = 0;
+        // for y axes, scale always go from 0 to 1 (0 to 100%)
+        if (this.name == 'yaxis' || this.name == 'y2axis') {
+            db.max = 100;
+            this.tickMode = 'even';
+        }
+        // For x axes, scale goes from 0 to sum of all y values.
+        else if (this.name == 'xaxis'){
+            this.tickMode = (this.tickMode == null) ? 'bar' : this.tickMode;
+            for (var i=0; i<this._series.length; i++) {
+                db.max += this._series[i]._sumy;
+            }
+        }
+        else if (this.name == 'x2axis'){
+            this.tickMode = (this.tickMode == null) ? 'even' : this.tickMode;
+            for (var i=0; i<this._series.length; i++) {
+                db.max += this._series[i]._sumy;
+            }
+        }
+    };
+    
+    // called with scope of axis
+    $.jqplot.MekkoAxisRenderer.prototype.draw = function(ctx, plot) {
+        if (this.show) {
+            // populate the axis label and value properties.
+            // createTicks is a method on the renderer, but
+            // call it within the scope of the axis.
+            this.renderer.createTicks.call(this);
+            // fill a div with axes labels in the right direction.
+            // Need to pregenerate each axis to get it's bounds and
+            // position it and the labels correctly on the plot.
+            var dim=0;
+            var temp;
+            
+            var elem = document.createElement('div');
+            this._elem = $(elem);
+            this._elem.addClass('jqplot-axis jqplot-'+this.name);
+            this._elem.css('position', 'absolute');
+            elem = null;
+            
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                this._elem.width(this._plotDimensions.width);
+            }
+            else {
+                this._elem.height(this._plotDimensions.height);
+            }
+            
+            // draw the axis label
+            // create a _label object.
+            this.labelOptions.axis = this.name;
+            this._label = new this.labelRenderer(this.labelOptions);
+            if (this._label.show) {
+                this._elem.append(this._label.draw(ctx));
+            }
+            
+            var t, tick, elem;
+            if (this.showTicks) {
+                t = this._ticks;
+                for (var i=0; i<t.length; i++) {
+                    tick = t[i];
+                    if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                        this._elem.append(tick.draw(ctx));
+                    }
+                }
+            }
+            
+            // draw the series labels
+            for (i=0; i<this.barLabels.length; i++) {
+                this.barLabelOptions.axis = this.name;
+                this.barLabelOptions.label = this.barLabels[i];
+                this._barLabels.push(new this.barLabelRenderer(this.barLabelOptions));
+                if (this.tickMode != 'bar') {
+                    this._barLabels[i].show = false;
+                }
+                if (this._barLabels[i].show) {
+                    var elem = this._barLabels[i].draw(ctx, plot);
+                    elem.removeClass('jqplot-'+this.name+'-label');
+                    elem.addClass('jqplot-'+this.name+'-tick');
+                    elem.addClass('jqplot-mekko-barLabel');
+                    elem.appendTo(this._elem);
+                    elem = null;
+                }   
+            }
+            
+        }
+        return this._elem;
+    };
+    
+    // called with scope of an axis
+    $.jqplot.MekkoAxisRenderer.prototype.reset = function() {
+        this.min = this._min;
+        this.max = this._max;
+        this.tickInterval = this._tickInterval;
+        this.numberTicks = this._numberTicks;
+        // this._ticks = this.__ticks;
+    };
+    
+    // called with scope of axis
+    $.jqplot.MekkoAxisRenderer.prototype.set = function() { 
+        var dim = 0;
+        var temp;
+        var w = 0;
+        var h = 0;
+        var lshow = (this._label == null) ? false : this._label.show;
+        if (this.show && this.showTicks) {
+            var t = this._ticks;
+            for (var i=0; i<t.length; i++) {
+                var tick = t[i];
+                if (tick.showLabel && (!tick.isMinorTick || this.showMinorTicks)) {
+                    if (this.name == 'xaxis' || this.name == 'x2axis') {
+                        temp = tick._elem.outerHeight(true);
+                    }
+                    else {
+                        temp = tick._elem.outerWidth(true);
+                    }
+                    if (temp > dim) {
+                        dim = temp;
+                    }
+                }
+            }
+            
+            if (lshow) {
+                w = this._label._elem.outerWidth(true);
+                h = this._label._elem.outerHeight(true); 
+            }
+            if (this.name == 'xaxis') {
+                dim = dim + h;
+                this._elem.css({'height':dim+'px', left:'0px', bottom:'0px'});
+            }
+            else if (this.name == 'x2axis') {
+                dim = dim + h;
+                this._elem.css({'height':dim+'px', left:'0px', top:'0px'});
+            }
+            else if (this.name == 'yaxis') {
+                dim = dim + w;
+                this._elem.css({'width':dim+'px', left:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+            else {
+                dim = dim + w;
+                this._elem.css({'width':dim+'px', right:'0px', top:'0px'});
+                if (lshow && this._label.constructor == $.jqplot.AxisLabelRenderer) {
+                    this._label._elem.css('width', w+'px');
+                }
+            }
+        }  
+    };    
+    
+    // called with scope of axis
+    $.jqplot.MekkoAxisRenderer.prototype.createTicks = function() {
+        // we're are operating on an axis here
+        var ticks = this._ticks;
+        var userTicks = this.ticks;
+        var name = this.name;
+        // databounds were set on axis initialization.
+        var db = this._dataBounds;
+        var dim, interval;
+        var min, max;
+        var pos1, pos2;
+        var t, tt, i, j;
+        
+        // if we already have ticks, use them.
+        // ticks must be in order of increasing value.
+        
+        if (userTicks.length) {
+            // ticks could be 1D or 2D array of [val, val, ,,,] or [[val, label], [val, label], ...] or mixed
+            for (i=0; i<userTicks.length; i++){
+                var ut = userTicks[i];
+                var t = new this.tickRenderer(this.tickOptions);
+                if (ut.constructor == Array) {
+                    t.value = ut[0];
+                    t.label = ut[1];
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(ut[0], this.name);
+                    this._ticks.push(t);
+                }
+                
+                else {
+                    t.value = ut;
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(ut, this.name);
+                    this._ticks.push(t);
+                }
+            }
+            this.numberTicks = userTicks.length;
+            this.min = this._ticks[0].value;
+            this.max = this._ticks[this.numberTicks-1].value;
+            this.tickInterval = (this.max - this.min) / (this.numberTicks - 1);
+        }
+        
+        // we don't have any ticks yet, let's make some!
+        else {
+            if (name == 'xaxis' || name == 'x2axis') {
+                dim = this._plotDimensions.width;
+            }
+            else {
+                dim = this._plotDimensions.height;
+            }
+            
+            // if min, max and number of ticks specified, user can't specify interval.
+            if (this.min != null && this.max != null && this.numberTicks != null) {
+                this.tickInterval = null;
+            }
+        
+            min = (this.min != null) ? this.min : db.min;
+            max = (this.max != null) ? this.max : db.max;
+            
+            // if min and max are same, space them out a bit.+
+            if (min == max) {
+                var adj = 0.05;
+                if (min > 0) {
+                    adj = Math.max(Math.log(min)/Math.LN10, 0.05);
+                }
+                min -= adj;
+                max += adj;
+            }
+
+            var range = max - min;
+            var rmin, rmax;
+            var temp, prev, curr;
+            var ynumticks = [3,5,6,11,21];
+            
+            // yaxis divide ticks in nice intervals from 0 to 1.
+            if (this.name == 'yaxis' || this.name == 'y2axis') { 
+                this.min = 0;
+                this.max = 100; 
+                // user didn't specify number of ticks.
+                if (!this.numberTicks){
+                    if (this.tickInterval) {
+                        this.numberTicks = 3 + Math.ceil(range / this.tickInterval);
+                    }
+                    else {
+                        temp = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
+                        for (i=0; i<ynumticks.length; i++) {
+                            curr = temp/ynumticks[i];
+                            if (curr == 1) {
+                                this.numberTicks = ynumticks[i];
+                                break;
+                            }
+                            else if (curr > 1) {
+                                prev = curr;
+                                continue;
+                            }
+                            else if (curr < 1) {
+                                // was prev or is curr closer to one?
+                                if (Math.abs(prev - 1) < Math.abs(curr - 1)) {
+                                    this.numberTicks = ynumticks[i-1];
+                                    break;
+                                }
+                                else {
+                                    this.numberTicks = ynumticks[i];
+                                    break;
+                                }
+                            }
+                            else if (i == ynumticks.length -1) {
+                                this.numberTicks = ynumticks[i];
+                            }
+                        }
+                        this.tickInterval = range / (this.numberTicks - 1);
+                    }
+                }
+                
+                // user did specify number of ticks.
+                else {
+                    this.tickInterval = range / (this.numberTicks - 1);
+                }
+
+                for (var i=0; i<this.numberTicks; i++){
+                    tt = this.min + i * this.tickInterval;
+                    t = new this.tickRenderer(this.tickOptions);
+                    // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(tt, this.name);
+                    this._ticks.push(t);
+                }
+            }
+            
+            // for x axes, have number ot ticks equal to number of series and ticks placed
+            // at sum of y values for each series.
+            else if (this.tickMode == 'bar') {
+                this.min = 0;
+                this.numberTicks = this._series.length + 1;
+                t = new this.tickRenderer(this.tickOptions);
+                if (!this.showTicks) {
+                    t.showLabel = false;
+                    t.showMark = false;
+                }
+                else if (!this.showTickMarks) {
+                    t.showMark = false;
+                }
+                t.setTick(0, this.name);
+                this._ticks.push(t);
+                
+                temp = 0;
+
+                for (i=1; i<this.numberTicks; i++){
+                    temp += this._series[i-1]._sumy;
+                    t = new this.tickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(temp, this.name);
+                    this._ticks.push(t);
+                }
+                this.max = this.max || temp;
+                
+                // if user specified a max and it is greater than sum, add a tick
+                if (this.max > temp) {
+                     t = new this.tickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(this.max, this.name);
+                    this._ticks.push(t);
+                    
+                }
+            }
+            
+            else if (this.tickMode == 'even') {
+                this.min = 0;
+                this.max = this.max || db.max;
+                // get a desired number of ticks
+                var nt = 2 + Math.ceil((dim-(this.tickSpacing-1))/this.tickSpacing);
+                range = this.max - this.min;
+                this.numberTicks = nt;
+                this.tickInterval = range / (this.numberTicks - 1);
+
+                for (i=0; i<this.numberTicks; i++){
+                    tt = this.min + i * this.tickInterval;
+                    t = new this.tickRenderer(this.tickOptions);
+                    // var t = new $.jqplot.AxisTickRenderer(this.tickOptions);
+                    if (!this.showTicks) {
+                        t.showLabel = false;
+                        t.showMark = false;
+                    }
+                    else if (!this.showTickMarks) {
+                        t.showMark = false;
+                    }
+                    t.setTick(tt, this.name);
+                    this._ticks.push(t);
+                }
+                
+            }
+        }
+    };
+    
+    // called with scope of axis
+    $.jqplot.MekkoAxisRenderer.prototype.pack = function(pos, offsets) {
+        var ticks = this._ticks;
+        var max = this.max;
+        var min = this.min;
+        var offmax = offsets.max;
+        var offmin = offsets.min;
+        var lshow = (this._label == null) ? false : this._label.show;
+        
+        for (var p in pos) {
+            this._elem.css(p, pos[p]);
+        }
+        
+        this._offsets = offsets;
+        // pixellength will be + for x axes and - for y axes becasue pixels always measured from top left.
+        var pixellength = offmax - offmin;
+        var unitlength = max - min;
+        
+        // point to unit and unit to point conversions references to Plot DOM element top left corner.
+        this.p2u = function(p){
+            return (p - offmin) * unitlength / pixellength + min;
+        };
+        
+        this.u2p = function(u){
+            return (u - min) * pixellength / unitlength + offmin;
+        };
+                
+        if (this.name == 'xaxis' || this.name == 'x2axis'){
+            this.series_u2p = function(u){
+                return (u - min) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return p * unitlength / pixellength + min;
+            };
+        }
+        
+        else {
+            this.series_u2p = function(u){
+                return (u - max) * pixellength / unitlength;
+            };
+            this.series_p2u = function(p){
+                return p * unitlength / pixellength + max;
+            };
+        }
+        
+        if (this.show) {
+            if (this.name == 'xaxis' || this.name == 'x2axis') {
+                for (var i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {
+                        var shim;
+                        
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'xaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                    if (temp * t.angle < 0) {
+                                        shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    }
+                                    // position at start
+                                    else {
+                                        shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'end':
+                                    shim = -t.getWidth() + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                case 'start':
+                                    shim = -t._textRenderer.height * Math.sin(t._textRenderer.angle) / 2;
+                                    break;
+                                case 'middle':
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                                default:
+                                    shim = -t.getWidth()/2 + t._textRenderer.height * Math.sin(-t._textRenderer.angle) / 2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getWidth()/2;
+                        }
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('left', val);
+                        t.pack();
+                    }
+                }
+                var w;
+                if (lshow) {
+                    w = this._label._elem.outerWidth(true);
+                    this._label._elem.css('left', offmin + pixellength/2 - w/2 + 'px');
+                    if (this.name == 'xaxis') {
+                        this._label._elem.css('bottom', '0px');
+                    }
+                    else {
+                        this._label._elem.css('top', '0px');
+                    }
+                    this._label.pack();
+                }
+                // now show the labels under the bars.
+                var b, l, r;
+                for (var i=0; i<this.barLabels.length; i++) {
+                    b = this._barLabels[i];
+                    if (b.show) {
+                        w = b.getWidth();
+                        l = this._ticks[i].getLeft() + this._ticks[i].getWidth();
+                        r = this._ticks[i+1].getLeft();
+                        b._elem.css('left', (r+l-w)/2+'px');
+                        b._elem.css('top', this._ticks[i]._elem.css('top'));
+                        b.pack();
+                    }
+                }
+            }
+            else {
+                for (var i=0; i<ticks.length; i++) {
+                    var t = ticks[i];
+                    if (t.show && t.showLabel) {                        
+                        var shim;
+                        if (t.constructor == $.jqplot.CanvasAxisTickRenderer && t.angle) {
+                            // will need to adjust auto positioning based on which axis this is.
+                            var temp = (this.name == 'yaxis') ? 1 : -1;
+                            switch (t.labelPosition) {
+                                case 'auto':
+                                    // position at end
+                                case 'end':
+                                    if (temp * t.angle < 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'start':
+                                    if (t.angle > 0) {
+                                        shim = -t._textRenderer.height * Math.cos(-t._textRenderer.angle) / 2;
+                                    }
+                                    else {
+                                        shim = -t.getHeight() + t._textRenderer.height * Math.cos(t._textRenderer.angle) / 2;
+                                    }
+                                    break;
+                                case 'middle':
+                                    shim = -t.getHeight()/2;
+                                    break;
+                                default:
+                                    shim = -t.getHeight()/2;
+                                    break;
+                            }
+                        }
+                        else {
+                            shim = -t.getHeight()/2;
+                        }
+                        
+                        var val = this.u2p(t.value) + shim + 'px';
+                        t._elem.css('top', val);
+                        t.pack();
+                    }
+                }
+                if (lshow) {
+                    var h = this._label._elem.outerHeight(true);
+                    this._label._elem.css('top', offmax - pixellength/2 - h/2 + 'px');
+                    if (this.name == 'yaxis') {
+                        this._label._elem.css('left', '0px');
+                    }
+                    else {
+                        this._label._elem.css('right', '0px');
+                    }   
+                    this._label.pack();
+                }
+            }
+        }
+    };
+})(jQuery);

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.mekkoAxisRenderer.min.js


+ 437 - 0
web/js/jqplot/plugins/jqplot.mekkoRenderer.js

@@ -0,0 +1,437 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.MekkoRenderer
+     * Draws a Mekko style chart which shows 3 dimensional data on a 2 dimensional graph.
+     * the <$.jqplot.MekkoAxisRenderer> should be used with mekko charts.  The mekko renderer
+     * overrides the default legend renderer with it's own $.jqplot.MekkoLegendRenderer
+     * which allows more flexibility to specify number of rows and columns in the legend.
+     * 
+     * Data is specified per bar in the chart.  You can specify data as an array of y values, or as 
+     * an array of [label, value] pairs.  Note that labels are used only on the first series.  
+     * Labels on subsequent series are ignored:
+     * 
+     * > bar1 = [['shirts', 8],['hats', 14],['shoes', 6],['gloves', 16],['dolls', 12]];
+     * > bar2 = [15,6,9,13,6];
+     * > bar3 = [['grumpy',4],['sneezy',2],['happy',7],['sleepy',9],['doc',7]];
+     * 
+     * If you want to place labels for each bar under the axis, you use the barLabels option on 
+     * the axes.  The bar labels can be styled with the ".jqplot-mekko-barLabel" css class.
+     * 
+     * > barLabels = ['Mickey Mouse', 'Donald Duck', 'Goofy'];
+     * > axes:{xaxis:{barLabels:barLabels}}
+     * 
+     */
+    
+    
+    $.jqplot.MekkoRenderer = function(){
+        this.shapeRenderer = new $.jqplot.ShapeRenderer();
+        // prop: borderColor
+        // color of the borders between areas on the chart
+        this.borderColor = null;
+        // prop: showBorders
+        // True to draw borders lines between areas on the chart.
+        // False will draw borders lines with the same color as the area.
+        this.showBorders = true;
+    };
+    
+    // called with scope of series.
+    $.jqplot.MekkoRenderer.prototype.init = function(options, plot) {
+        this.fill = false;
+        this.fillRect = true;
+        this.strokeRect = true;
+        this.shadow = false;
+        // width of bar on x axis.
+        this._xwidth = 0;
+        this._xstart = 0;
+        $.extend(true, this.renderer, options);
+        // set the shape renderer options
+        var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect};
+        this.renderer.shapeRenderer.init(opts);
+        plot.axes.x2axis._series.push(this);
+        this._type = 'mekko';
+    };
+    
+    // Method: setGridData
+    // converts the user data values to grid coordinates and stores them
+    // in the gridData array.  Will convert user data into appropriate
+    // rectangles.
+    // Called with scope of a series.
+    $.jqplot.MekkoRenderer.prototype.setGridData = function(plot) {
+        // recalculate the grid data
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var data = this._plotData;
+        this.gridData = [];
+        // figure out width on x axis.
+        // this._xwidth = this._sumy / plot._sumy * this.canvas.getWidth();
+        this._xwidth = xp(this._sumy) - xp(0);
+        if (this.index>0) {
+            this._xstart = plot.series[this.index-1]._xstart + plot.series[this.index-1]._xwidth;
+        }
+        var totheight = this.canvas.getHeight();
+        var sumy = 0;
+        var cury;
+        var curheight;
+        for (var i=0; i<data.length; i++) {
+            if (data[i] != null) {
+                sumy += data[i][1];
+                cury = totheight - (sumy / this._sumy * totheight);
+                curheight = data[i][1] / this._sumy * totheight;
+                this.gridData.push([this._xstart, cury, this._xwidth, curheight]);
+            }
+        }
+    };
+    
+    // Method: makeGridData
+    // converts any arbitrary data values to grid coordinates and
+    // returns them.  This method exists so that plugins can use a series'
+    // linerenderer to generate grid data points without overwriting the
+    // grid data associated with that series.
+    // Called with scope of a series.
+    $.jqplot.MekkoRenderer.prototype.makeGridData = function(data, plot) {
+        // recalculate the grid data
+        // figure out width on x axis.
+        var xp = this._xaxis.series_u2p;
+        var totheight = this.canvas.getHeight();
+        var sumy = 0;
+        var cury;
+        var curheight;
+        var gd = [];
+        for (var i=0; i<data.length; i++) {
+            if (data[i] != null) {
+                sumy += data[i][1];
+                cury = totheight - (sumy / this._sumy * totheight);
+                curheight = data[i][1] / this._sumy * totheight;
+                gd.push([this._xstart, cury, this._xwidth, curheight]);
+            }
+        }
+        return gd;
+    };
+    
+
+    // called within scope of series.
+    $.jqplot.MekkoRenderer.prototype.draw = function(ctx, gd, options) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
+        var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
+        ctx.save();
+        if (gd.length) {
+            if (showLine) {
+                for (i=0; i<gd.length; i++){
+                    opts.fillStyle = colorGenerator.next();
+                    if (this.renderer.showBorders) {
+                        opts.strokeStyle = this.renderer.borderColor;
+                    }
+                    else {
+                        opts.strokeStyle = opts.fillStyle;
+                    }
+                    this.renderer.shapeRenderer.draw(ctx, gd[i], opts);
+                }
+            }
+        }
+        
+        ctx.restore();
+    };  
+    
+    $.jqplot.MekkoRenderer.prototype.drawShadow = function(ctx, gd, options) {
+        // This is a no-op, no shadows on mekko charts.
+    };
+    
+    /**
+     * Class: $.jqplot.MekkoLegendRenderer
+     * Legend renderer used by mekko charts with options for 
+     * controlling number or rows and columns as well as placement
+     * outside of plot area.
+     * 
+     */
+    $.jqplot.MekkoLegendRenderer = function(){
+        //
+    };
+    
+    $.jqplot.MekkoLegendRenderer.prototype.init = function(options) {
+        // prop: numberRows
+        // Maximum number of rows in the legend.  0 or null for unlimited.
+        this.numberRows = null;
+        // prop: numberColumns
+        // Maximum number of columns in the legend.  0 or null for unlimited.
+        this.numberColumns = null;
+        // this will override the placement option on the Legend object
+        this.placement = "outside";
+        $.extend(true, this, options);
+    };
+    
+    // called with scope of legend
+    $.jqplot.MekkoLegendRenderer.prototype.draw = function() {
+        var legend = this;
+        if (this.show) {
+            var series = this._series;
+            var ss = 'position:absolute;';
+            ss += (this.background) ? 'background:'+this.background+';' : '';
+            ss += (this.border) ? 'border:'+this.border+';' : '';
+            ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
+            ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
+            ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
+            this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
+            // Mekko charts  legends don't go by number of series, but by number of data points
+            // in the series.  Refactor things here for that.
+            
+            var pad = false, 
+                reverse = true,    // mekko charts are always stacked, so reverse
+                nr, nc;
+            var s = series[0];
+            var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
+            
+            if (s.show) {
+                var pd = s.data;
+                if (this.numberRows) {
+                    nr = this.numberRows;
+                    if (!this.numberColumns){
+                        nc = Math.ceil(pd.length/nr);
+                    }
+                    else{
+                        nc = this.numberColumns;
+                    }
+                }
+                else if (this.numberColumns) {
+                    nc = this.numberColumns;
+                    nr = Math.ceil(pd.length/this.numberColumns);
+                }
+                else {
+                    nr = pd.length;
+                    nc = 1;
+                }
+                
+                var i, j, tr, td1, td2, lt, rs, color;
+                var idx = 0;    
+                
+                for (i=0; i<nr; i++) {
+                    if (reverse){
+                        tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
+                    }
+                    else{
+                        tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
+                    }
+                    for (j=0; j<nc; j++) {
+                        if (idx < pd.length) {
+                            lt = this.labels[idx] || pd[idx][0].toString();
+                            color = colorGenerator.next();
+                            if (!reverse){
+                                if (i>0){
+                                    pad = true;
+                                }
+                                else{
+                                    pad = false;
+                                }
+                            }
+                            else{
+                                if (i == nr -1){
+                                    pad = false;
+                                }
+                                else{
+                                    pad = true;
+                                }
+                            }
+                            rs = (pad) ? this.rowSpacing : '0';
+                
+                            td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
+                                '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
+                                '</div></td>');
+                            td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
+                            if (this.escapeHtml){
+                                td2.text(lt);
+                            }
+                            else {
+                                td2.html(lt);
+                            }
+                            if (reverse) {
+                                td2.prependTo(tr);
+                                td1.prependTo(tr);
+                            }
+                            else {
+                                td1.appendTo(tr);
+                                td2.appendTo(tr);
+                            }
+                            pad = true;
+                        }
+                        idx++;
+                    }   
+                }
+
+                tr = null;
+                td1 = null;
+                td2 = null;
+            }
+        }
+        return this._elem;
+    };
+    
+    $.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) {
+        if (this.show) {
+            // fake a grid for positioning
+            var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};        
+            if (this.placement == 'insideGrid') {
+                switch (this.location) {
+                    case 'nw':
+                        var a = grid._left + this.xoffset;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css('left', a);
+                        this._elem.css('top', b);
+                        break;
+                    case 'n':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css('left', a);
+                        this._elem.css('top', b);
+                        break;
+                    case 'ne':
+                        var a = offsets.right + this.xoffset;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css({right:a, top:b});
+                        break;
+                    case 'e':
+                        var a = offsets.right + this.xoffset;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({right:a, top:b});
+                        break;
+                    case 'se':
+                        var a = offsets.right + this.xoffset;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                    case 's':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({left:a, bottom:b});
+                        break;
+                    case 'sw':
+                        var a = grid._left + this.xoffset;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({left:a, bottom:b});
+                        break;
+                    case 'w':
+                        var a = grid._left + this.xoffset;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    default:  // same as 'se'
+                        var a = grid._right - this.xoffset;
+                        var b = grid._bottom + this.yoffset;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                }
+                
+            }
+            else {
+                switch (this.location) {
+                    case 'nw':
+                        var a = this._plotDimensions.width - grid._left + this.xoffset;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css('right', a);
+                        this._elem.css('top', b);
+                        break;
+                    case 'n':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = this._plotDimensions.height - grid._top + this.yoffset;
+                        this._elem.css('left', a);
+                        this._elem.css('bottom', b);
+                        break;
+                    case 'ne':
+                        var a = this._plotDimensions.width - offsets.right + this.xoffset;
+                        var b = grid._top + this.yoffset;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    case 'e':
+                        var a = this._plotDimensions.width - offsets.right + this.xoffset;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    case 'se':
+                        var a = this._plotDimensions.width - offsets.right + this.xoffset;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({left:a, bottom:b});
+                        break;
+                    case 's':
+                        var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
+                        var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
+                        this._elem.css({left:a, top:b});
+                        break;
+                    case 'sw':
+                        var a = this._plotDimensions.width - grid._left + this.xoffset;
+                        var b = offsets.bottom + this.yoffset;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                    case 'w':
+                        var a = this._plotDimensions.width - grid._left + this.xoffset;
+                        var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
+                        this._elem.css({right:a, top:b});
+                        break;
+                    default:  // same as 'se'
+                        var a = grid._right - this.xoffset;
+                        var b = grid._bottom + this.yoffset;
+                        this._elem.css({right:a, bottom:b});
+                        break;
+                }
+            }
+        } 
+    };
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = options.axesDefaults || {};
+        options.legend = options.legend || {};
+        options.seriesDefaults = options.seriesDefaults || {};
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.MekkoRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.MekkoRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.MekkoAxisRenderer;
+            options.legend.renderer = $.jqplot.MekkoLegendRenderer;
+            options.legend.preDraw = true;
+        }
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    
+})(jQuery);    

Разница между файлами не показана из-за своего большого размера
+ 1028 - 0
web/js/jqplot/plugins/jqplot.meterGaugeRenderer.js


Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.meterGaugeRenderer.min.js


+ 373 - 0
web/js/jqplot/plugins/jqplot.ohlcRenderer.js

@@ -0,0 +1,373 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.OHLCRenderer
+     * jqPlot Plugin to draw Open Hi Low Close, Candlestick and Hi Low Close charts.
+     * 
+     * To use this plugin, include the renderer js file in 
+     * your source:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.ohlcRenderer.js"></script>
+     * 
+     * You will most likely want to use a date axis renderer
+     * for the x axis also, so include the date axis render js file also:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.dateAxisRenderer.js"></script>
+     * 
+     * Then you set the renderer in the series options on your plot:
+     * 
+     * > series: [{renderer:$.jqplot.OHLCRenderer}]
+     * 
+     * For OHLC and candlestick charts, data should be specified
+     * like so:
+     * 
+     * > dat = [['07/06/2009',138.7,139.68,135.18,135.4], ['06/29/2009',143.46,144.66,139.79,140.02], ...]
+     * 
+     * If the data array has only 4 values per point instead of 5,
+     * the renderer will create a Hi Low Close chart instead.  In that case,
+     * data should be supplied like:
+     * 
+     * > dat = [['07/06/2009',139.68,135.18,135.4], ['06/29/2009',144.66,139.79,140.02], ...]
+     * 
+     * To generate a candlestick chart instead of an OHLC chart,
+     * set the "candlestick" option to true:
+     * 
+     * > series: [{renderer:$.jqplot.OHLCRenderer, rendererOptions:{candleStick:true}}],
+     * 
+     */
+    $.jqplot.OHLCRenderer = function(){
+        // subclass line renderer to make use of some of it's methods.
+        $.jqplot.LineRenderer.call(this);
+        // prop: candleStick
+        // true to render chart as candleStick.
+        // Must have an open price, cannot be a hlc chart.
+        this.candleStick = false;
+        // prop: tickLength
+        // length of the line in pixels indicating open and close price.
+        // Default will auto calculate based on plot width and 
+        // number of points displayed.
+        this.tickLength = 'auto';
+        // prop: bodyWidth
+        // width of the candlestick body in pixels.  Default will auto calculate
+        // based on plot width and number of candlesticks displayed.
+        this.bodyWidth = 'auto';
+        // prop: openColor
+        // color of the open price tick mark.  Default is series color.
+        this.openColor = null;
+        // prop: closeColor
+        // color of the close price tick mark.  Default is series color.
+        this.closeColor = null;
+        // prop: wickColor
+        // color of the hi-lo line thorugh the candlestick body.
+        // Default is the series color.
+        this.wickColor = null;
+        // prop: fillUpBody
+        // true to render an "up" day (close price greater than open price)
+        // with a filled candlestick body.
+        this.fillUpBody = false;
+        // prop: fillDownBody
+        // true to render a "down" day (close price lower than open price)
+        // with a filled candlestick body.
+        this.fillDownBody = true;
+        // prop: upBodyColor
+        // Color of candlestick body of an "up" day.  Default is series color.
+        this.upBodyColor = null;
+        // prop: downBodyColor
+        // Color of candlestick body on a "down" day.  Default is series color.
+        this.downBodyColor = null;
+        // prop: hlc
+        // true if is a hi-low-close chart (no open price).
+        // This is determined automatically from the series data.
+        this.hlc = false;
+        // prop: lineWidth
+        // Width of the hi-low line and open/close ticks.
+        // Must be set in the rendererOptions for the series.
+        this.lineWidth = 1.5;
+        this._tickLength;
+        this._bodyWidth;
+    };
+    
+    $.jqplot.OHLCRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.OHLCRenderer.prototype.constructor = $.jqplot.OHLCRenderer;
+    
+    // called with scope of series.
+    $.jqplot.OHLCRenderer.prototype.init = function(options) {
+        options = options || {};
+        // lineWidth has to be set on the series, changes in renderer
+        // constructor have no effect.  set the default here
+        // if no renderer option for lineWidth is specified.
+        this.lineWidth = options.lineWidth || 1.5;
+        $.jqplot.LineRenderer.prototype.init.call(this, options);
+        this._type = 'ohlc';
+        // set the yaxis data bounds here to account for hi and low values
+        var db = this._yaxis._dataBounds;
+        var d = this._plotData;
+        // if data points have less than 5 values, force a hlc chart.
+        if (d[0].length < 5) {
+            this.renderer.hlc = true;
+
+            for (var j=0; j<d.length; j++) { 
+                if (d[j][2] < db.min || db.min == null) {
+                    db.min = d[j][2];
+                }
+                if (d[j][1] > db.max || db.max == null) {
+                    db.max = d[j][1];
+                }             
+            }
+        }
+        else {
+            for (var j=0; j<d.length; j++) { 
+                if (d[j][3] < db.min || db.min == null) {
+                    db.min = d[j][3];
+                }
+                if (d[j][2] > db.max || db.max == null) {
+                    db.max = d[j][2];
+                }             
+            }
+        }
+        
+    };
+    
+    // called within scope of series.
+    $.jqplot.OHLCRenderer.prototype.draw = function(ctx, gd, options) {
+        var d = this.data;
+        var xmin = this._xaxis.min;
+        var xmax = this._xaxis.max;
+        // index of last value below range of plot.
+        var xminidx = 0;
+        // index of first value above range of plot.
+        var xmaxidx = d.length;
+        var xp = this._xaxis.series_u2p;
+        var yp = this._yaxis.series_u2p;
+        var i, prevColor, ops, b, h, w, a, points;
+        var o;
+        var r = this.renderer;
+        var opts = (options != undefined) ? options : {};
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke;
+        r.bodyWidth = (opts.bodyWidth != undefined) ? opts.bodyWidth : r.bodyWidth;
+        r.tickLength = (opts.tickLength != undefined) ? opts.tickLength : r.tickLength;
+        ctx.save();
+        if (this.show) {
+            var x, open, hi, low, close;
+            // need to get widths based on number of points shown,
+            // not on total number of points.  Use the results 
+            // to speed up drawing in next step.
+            for (var i=0; i<d.length; i++) {
+                if (d[i][0] < xmin) {
+                    xminidx = i;
+                }
+                else if (d[i][0] < xmax) {
+                    xmaxidx = i+1;
+                }
+            }
+
+            var dwidth = this.gridData[xmaxidx-1][0] - this.gridData[xminidx][0];
+            var nvisiblePoints = xmaxidx - xminidx;
+            try {
+                var dinterval = Math.abs(this._xaxis.series_u2p(parseInt(this._xaxis._intervalStats[0].sortedIntervals[0].interval, 10)) - this._xaxis.series_u2p(0)); 
+            }
+
+            catch (e) {
+                var dinterval = dwidth / nvisiblePoints;
+            }
+            
+            if (r.candleStick) {
+                if (typeof(r.bodyWidth) == 'number') {
+                    r._bodyWidth = r.bodyWidth;
+                }
+                else {
+                    r._bodyWidth = Math.min(20, dinterval/1.65);
+                }
+            }
+            else {
+                if (typeof(r.tickLength) == 'number') {
+                    r._tickLength = r.tickLength;
+                }
+                else {
+                    r._tickLength = Math.min(10, dinterval/3.5);
+                }
+            }
+            
+            for (var i=xminidx; i<xmaxidx; i++) {
+                x = xp(d[i][0]);
+                if (r.hlc) {
+                    open = null;
+                    hi = yp(d[i][1]);
+                    low = yp(d[i][2]);
+                    close = yp(d[i][3]);
+                }
+                else {
+                    open = yp(d[i][1]);
+                    hi = yp(d[i][2]);
+                    low = yp(d[i][3]);
+                    close = yp(d[i][4]);
+                }
+                o = {};
+                if (r.candleStick && !r.hlc) {
+                    w = r._bodyWidth;
+                    a = x - w/2;
+                    // draw candle
+                    // determine if candle up or down
+                    // up, remember grid coordinates increase downward
+                    if (close < open) {
+                        // draw wick
+                        if (r.wickColor) {
+                            o.color = r.wickColor;
+                        }
+                        else if (r.downBodyColor) {
+                            o.color = r.upBodyColor;
+                        }
+                        ops = $.extend(true, {}, opts, o);
+                        r.shapeRenderer.draw(ctx, [[x, hi], [x, close]], ops); 
+                        r.shapeRenderer.draw(ctx, [[x, open], [x, low]], ops); 
+                        o = {};
+                        b = close;
+                        h = open - close;
+                        // if color specified, use it
+                        if (r.fillUpBody) {
+                            o.fillRect = true;
+                        }
+                        else {
+                            o.strokeRect = true;
+                            w = w - this.lineWidth;
+                            a = x - w/2;
+                        }
+                        if (r.upBodyColor) {
+                            o.color = r.upBodyColor;
+                            o.fillStyle = r.upBodyColor;
+                        }
+                        points = [a, b, w, h];
+                    }
+                    // down
+                    else if (close >  open) {
+                        // draw wick
+                        if (r.wickColor) {
+                            o.color = r.wickColor;
+                        }
+                        else if (r.downBodyColor) {
+                            o.color = r.downBodyColor;
+                        }
+                        ops = $.extend(true, {}, opts, o);
+                        r.shapeRenderer.draw(ctx, [[x, hi], [x, open]], ops); 
+                        r.shapeRenderer.draw(ctx, [[x, close], [x, low]], ops);
+                         
+                        o = {};
+                        
+                        b = open;
+                        h = close - open;
+                        // if color specified, use it
+                        if (r.fillDownBody) {
+                            o.fillRect = true;
+                        }
+                        else {
+                            o.strokeRect = true;
+                            w = w - this.lineWidth;
+                            a = x - w/2;
+                        }
+                        if (r.downBodyColor) {
+                            o.color = r.downBodyColor;
+                            o.fillStyle = r.downBodyColor;
+                        }
+                        points = [a, b, w, h];
+                    }
+                    // even, open = close
+                    else  {
+                        // draw wick
+                        if (r.wickColor) {
+                            o.color = r.wickColor;
+                        }
+                        ops = $.extend(true, {}, opts, o);
+                        r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], ops); 
+                        o = {};
+                        o.fillRect = false;
+                        o.strokeRect = false;
+                        a = [x - w/2, open];
+                        b = [x + w/2, close];
+                        w = null;
+                        h = null;
+                        points = [a, b];
+                    }
+                    ops = $.extend(true, {}, opts, o);
+                    r.shapeRenderer.draw(ctx, points, ops);
+                }
+                else {
+                    prevColor = opts.color;
+                    if (r.openColor) {
+                        opts.color = r.openColor;
+                    }
+                    // draw open tick
+                    if (!r.hlc) {
+                        r.shapeRenderer.draw(ctx, [[x-r._tickLength, open], [x, open]], opts);    
+                    }
+                    opts.color = prevColor;
+                    // draw wick
+                    if (r.wickColor) {
+                        opts.color = r.wickColor;
+                    }
+                    r.shapeRenderer.draw(ctx, [[x, hi], [x, low]], opts); 
+                    opts.color  = prevColor;
+                    // draw close tick
+                    if (r.closeColor) {
+                        opts.color = r.closeColor;
+                    }
+                    r.shapeRenderer.draw(ctx, [[x, close], [x+r._tickLength, close]], opts); 
+                    opts.color = prevColor;
+                }
+            }
+        }
+        
+        ctx.restore();
+    };  
+    
+    $.jqplot.OHLCRenderer.prototype.drawShadow = function(ctx, gd, options) {
+        // This is a no-op, shadows drawn with lines.
+    };
+    
+    // called with scope of plot.
+    $.jqplot.OHLCRenderer.checkOptions = function(target, data, options) {
+        // provide some sensible highlighter options by default
+        // These aren't good for hlc, only for ohlc or candlestick
+        if (!options.highlighter) {
+            options.highlighter = {
+                showMarker:false,
+                tooltipAxes: 'y',
+                yvalues: 4,
+                formatString:'<table class="jqplot-highlighter"><tr><td>date:</td><td>%s</td></tr><tr><td>open:</td><td>%s</td></tr><tr><td>hi:</td><td>%s</td></tr><tr><td>low:</td><td>%s</td></tr><tr><td>close:</td><td>%s</td></tr></table>'
+            };
+        }
+    };
+    
+    //$.jqplot.preInitHooks.push($.jqplot.OHLCRenderer.checkOptions);
+    
+})(jQuery);    

+ 904 - 0
web/js/jqplot/plugins/jqplot.pieRenderer.js

@@ -0,0 +1,904 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    /**
+     * Class: $.jqplot.PieRenderer
+     * Plugin renderer to draw a pie chart.
+     * x values, if present, will be used as slice labels.
+     * y values give slice size.
+     * 
+     * To use this renderer, you need to include the 
+     * pie renderer plugin, for example:
+     * 
+     * > <script type="text/javascript" src="plugins/jqplot.pieRenderer.js"></script>
+     * 
+     * Properties described here are passed into the $.jqplot function
+     * as options on the series renderer.  For example:
+     * 
+     * > plot2 = $.jqplot('chart2', [s1, s2], {
+     * >     seriesDefaults: {
+     * >         renderer:$.jqplot.PieRenderer,
+     * >         rendererOptions:{
+     * >              sliceMargin: 2,
+     * >              startAngle: -90
+     * >          }
+     * >      }
+     * > });
+     * 
+     * A pie plot will trigger events on the plot target
+     * according to user interaction.  All events return the event object,
+     * the series index, the point (slice) index, and the point data for 
+     * the appropriate slice.
+     * 
+     * 'jqplotDataMouseOver' - triggered when user mouseing over a slice.
+     * 'jqplotDataHighlight' - triggered the first time user mouses over a slice,
+     * if highlighting is enabled.
+     * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
+     * a highlighted slice.
+     * 'jqplotDataClick' - triggered when the user clicks on a slice.
+     * 'jqplotDataRightClick' - tiggered when the user right clicks on a slice if
+     * the "captureRightClick" option is set to true on the plot.
+     */
+    $.jqplot.PieRenderer = function(){
+        $.jqplot.LineRenderer.call(this);
+    };
+    
+    $.jqplot.PieRenderer.prototype = new $.jqplot.LineRenderer();
+    $.jqplot.PieRenderer.prototype.constructor = $.jqplot.PieRenderer;
+    
+    // called with scope of a series
+    $.jqplot.PieRenderer.prototype.init = function(options, plot) {
+        // Group: Properties
+        //
+        // prop: diameter
+        // Outer diameter of the pie, auto computed by default
+        this.diameter = null;
+        // prop: padding
+        // padding between the pie and plot edges, legend, etc.
+        this.padding = 20;
+        // prop: sliceMargin
+        // angular spacing between pie slices in degrees.
+        this.sliceMargin = 0;
+        // prop: fill
+        // true or false, wether to fil the slices.
+        this.fill = true;
+        // prop: shadowOffset
+        // offset of the shadow from the slice and offset of 
+        // each succesive stroke of the shadow from the last.
+        this.shadowOffset = 2;
+        // prop: shadowAlpha
+        // transparency of the shadow (0 = transparent, 1 = opaque)
+        this.shadowAlpha = 0.07;
+        // prop: shadowDepth
+        // number of strokes to apply to the shadow, 
+        // each stroke offset shadowOffset from the last.
+        this.shadowDepth = 5;
+        // prop: highlightMouseOver
+        // True to highlight slice when moused over.
+        // This must be false to enable highlightMouseDown to highlight when clicking on a slice.
+        this.highlightMouseOver = true;
+        // prop: highlightMouseDown
+        // True to highlight when a mouse button is pressed over a slice.
+        // This will be disabled if highlightMouseOver is true.
+        this.highlightMouseDown = false;
+        // prop: highlightColors
+        // an array of colors to use when highlighting a slice.
+        this.highlightColors = [];
+        // prop: dataLabels
+        // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
+        // Defaults to percentage of each pie slice.
+        this.dataLabels = 'percent';
+        // prop: showDataLabels
+        // true to show data labels on slices.
+        this.showDataLabels = false;
+        // prop: dataLabelFormatString
+        // Format string for data labels.  If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
+        this.dataLabelFormatString = null;
+        // prop: dataLabelThreshold
+        // Threshhold in percentage (0-100) of pie area, below which no label will be displayed.
+        // This applies to all label types, not just to percentage labels.
+        this.dataLabelThreshold = 3;
+        // prop: dataLabelPositionFactor
+        // A Multiplier (0-1) of the pie radius which controls position of label on slice.
+        // Increasing will slide label toward edge of pie, decreasing will slide label toward center of pie.
+        this.dataLabelPositionFactor = 0.52;
+        // prop: dataLabelNudge
+        // Number of pixels to slide the label away from (+) or toward (-) the center of the pie.
+        this.dataLabelNudge = 2;
+        // prop: dataLabelCenterOn
+        // True to center the data label at its position.
+        // False to set the inside facing edge of the label at its position.
+        this.dataLabelCenterOn = true;
+        // prop: startAngle
+        // Angle to start drawing pie in degrees.  
+        // According to orientation of canvas coordinate system:
+        // 0 = on the positive x axis
+        // -90 = on the positive y axis.
+        // 90 = on the negaive y axis.
+        // 180 or - 180 = on the negative x axis.
+        this.startAngle = 0;
+        this.tickRenderer = $.jqplot.PieTickRenderer;
+        // Used as check for conditions where pie shouldn't be drawn.
+        this._drawData = true;
+        this._type = 'pie';
+        
+        // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
+        if (options.highlightMouseDown && options.highlightMouseOver == null) {
+            options.highlightMouseOver = false;
+        }
+        
+        $.extend(true, this, options);
+
+        if (this.sliceMargin < 0) {
+            this.sliceMargin = 0;
+        }
+
+        this._diameter = null;
+        this._radius = null;
+        // array of [start,end] angles arrays, one for each slice.  In radians.
+        this._sliceAngles = [];
+        // index of the currenty highlighted point, if any
+        this._highlightedPoint = null;
+        
+        // set highlight colors if none provided
+        if (this.highlightColors.length == 0) {
+            for (var i=0; i<this.seriesColors.length; i++){
+                var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
+                var newrgb = [rgba[0], rgba[1], rgba[2]];
+                var sum = newrgb[0] + newrgb[1] + newrgb[2];
+                for (var j=0; j<3; j++) {
+                    // when darkening, lowest color component can be is 60.
+                    newrgb[j] = (sum > 570) ?  newrgb[j] * 0.8 : newrgb[j] + 0.3 * (255 - newrgb[j]);
+                    newrgb[j] = parseInt(newrgb[j], 10);
+                }
+                this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
+            }
+        }
+        
+        this.highlightColorGenerator = new $.jqplot.ColorGenerator(this.highlightColors);
+        
+        plot.postParseOptionsHooks.addOnce(postParseOptions);
+        plot.postInitHooks.addOnce(postInit);
+        plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
+        plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
+        plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
+        plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
+        plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
+        plot.postDrawHooks.addOnce(postPlotDraw);
+    };
+    
+    $.jqplot.PieRenderer.prototype.setGridData = function(plot) {
+        // set gridData property.  This will hold angle in radians of each data point.
+        var stack = [];
+        var td = [];
+        var sa = this.startAngle/180*Math.PI;
+        var tot = 0;
+        // don't know if we have any valid data yet, so set plot to not draw.
+        this._drawData = false;
+        for (var i=0; i<this.data.length; i++){
+            if (this.data[i][1] != 0) {
+                // we have data, O.K. to draw.
+                this._drawData = true;
+            }
+            stack.push(this.data[i][1]);
+            td.push([this.data[i][0]]);
+            if (i>0) {
+                stack[i] += stack[i-1];
+            }
+            tot += this.data[i][1];
+        }
+        var fact = Math.PI*2/stack[stack.length - 1];
+        
+        for (var i=0; i<stack.length; i++) {
+            td[i][1] = stack[i] * fact;
+            td[i][2] = this.data[i][1]/tot;
+        }
+        this.gridData = td;
+    };
+    
+    $.jqplot.PieRenderer.prototype.makeGridData = function(data, plot) {
+        var stack = [];
+        var td = [];
+        var tot = 0;
+        var sa = this.startAngle/180*Math.PI;
+        // don't know if we have any valid data yet, so set plot to not draw.
+        this._drawData = false;
+        for (var i=0; i<data.length; i++){
+            if (this.data[i][1] != 0) {
+                // we have data, O.K. to draw.
+                this._drawData = true;
+            }
+            stack.push(data[i][1]);
+            td.push([data[i][0]]);
+            if (i>0) {
+                stack[i] += stack[i-1];
+            }
+            tot += data[i][1];
+        }
+        var fact = Math.PI*2/stack[stack.length - 1];
+        
+        for (var i=0; i<stack.length; i++) {
+            td[i][1] = stack[i] * fact;
+            td[i][2] = data[i][1]/tot;
+        }
+        return td;
+    };
+
+    function calcRadiusAdjustment(ang) {
+        return Math.sin((ang - (ang-Math.PI) / 8 / Math.PI )/2.0);
+    }
+
+    function calcRPrime(ang1, ang2, sliceMargin, fill, lineWidth) {
+        var rprime = 0;
+        var ang = ang2 - ang1;
+        var absang = Math.abs(ang);
+        var sm = sliceMargin;
+        if (fill == false) {
+            sm += lineWidth;
+        }
+
+        if (sm > 0 && absang > 0.01 && absang < 6.282) {
+            rprime = parseFloat(sm) / 2.0 / calcRadiusAdjustment(ang);
+        }
+
+        return rprime;
+    }
+    
+    $.jqplot.PieRenderer.prototype.drawSlice = function (ctx, ang1, ang2, color, isShadow) {
+        if (this._drawData) {
+            var r = this._radius;
+            var fill = this.fill;
+            var lineWidth = this.lineWidth;
+            var sm = this.sliceMargin;
+            if (this.fill == false) {
+                sm += this.lineWidth;
+            }
+            ctx.save();
+            ctx.translate(this._center[0], this._center[1]);
+            
+            var rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth);
+
+            var transx = rprime * Math.cos((ang1 + ang2) / 2.0);
+            var transy = rprime * Math.sin((ang1 + ang2) / 2.0);
+
+            if ((ang2 - ang1) <= Math.PI) {
+                r -= rprime;  
+            }
+            else {
+                r += rprime;
+            }
+
+            ctx.translate(transx, transy);
+            
+            if (isShadow) {
+                for (var i=0, l=this.shadowDepth; i<l; i++) {
+                    ctx.save();
+                    ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
+                    doDraw(r);
+                }
+                for (var i=0, l=this.shadowDepth; i<l; i++) {
+                    ctx.restore();
+                }
+            }
+    
+            else {
+                doDraw(r);
+            }
+            ctx.restore();
+        }
+    
+        function doDraw (rad) {
+            // Fix for IE and Chrome that can't seem to draw circles correctly.
+            // ang2 should always be <= 2 pi since that is the way the data is converted.
+            // 2Pi = 6.2831853, Pi = 3.1415927
+             if (ang2 > 6.282 + this.startAngle) {
+                ang2 = 6.282 + this.startAngle;
+                if (ang1 > ang2) {
+                    ang1 = 6.281 + this.startAngle;
+                }
+            }
+            // Fix for IE, where it can't seem to handle 0 degree angles.  Also avoids
+            // ugly line on unfilled pies.
+            if (ang1 >= ang2) {
+                return;
+            }            
+        
+            ctx.beginPath();  
+            ctx.fillStyle = color;
+            ctx.strokeStyle = color;
+            ctx.lineWidth = lineWidth;
+            ctx.arc(0, 0, rad, ang1, ang2, false);
+            ctx.lineTo(0,0);
+            ctx.closePath();
+        
+            if (fill) {
+                ctx.fill();
+            }
+            else {
+                ctx.stroke();
+            }
+        }
+    };
+    
+    // called with scope of series
+    $.jqplot.PieRenderer.prototype.draw = function (ctx, gd, options, plot) {
+        var i;
+        var opts = (options != undefined) ? options : {};
+        // offset and direction of offset due to legend placement
+        var offx = 0;
+        var offy = 0;
+        var trans = 1;
+        var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
+        if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
+            var li = options.legendInfo;
+            switch (li.location) {
+                case 'nw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'w':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'sw':
+                    offx = li.width + li.xoffset;
+                    break;
+                case 'ne':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'e':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'se':
+                    offx = li.width + li.xoffset;
+                    trans = -1;
+                    break;
+                case 'n':
+                    offy = li.height + li.yoffset;
+                    break;
+                case 's':
+                    offy = li.height + li.yoffset;
+                    trans = -1;
+                    break;
+                default:
+                    break;
+            }
+        }
+        
+        var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
+        var fill = (opts.fill != undefined) ? opts.fill : this.fill;
+        var cw = ctx.canvas.width;
+        var ch = ctx.canvas.height;
+        var w = cw - offx - 2 * this.padding;
+        var h = ch - offy - 2 * this.padding;
+        var mindim = Math.min(w,h);
+        var d = mindim;
+        
+        // Fixes issue #272.  Thanks hugwijst!
+        // reset slice angles array.
+        this._sliceAngles = [];
+
+        var sm = this.sliceMargin;
+        if (this.fill == false) {
+            sm += this.lineWidth;
+        }
+        
+        var rprime;
+        var maxrprime = 0;
+
+        var ang, ang1, ang2, shadowColor;
+        var sa = this.startAngle / 180 * Math.PI;
+
+        // have to pre-draw shadows, so loop throgh here and calculate some values also.
+        for (var i=0, l=gd.length; i<l; i++) {
+            ang1 = (i == 0) ? sa : gd[i-1][1] + sa;
+            ang2 = gd[i][1] + sa;
+
+            this._sliceAngles.push([ang1, ang2]);
+
+            rprime = calcRPrime(ang1, ang2, this.sliceMargin, this.fill, this.lineWidth);
+
+            if (Math.abs(ang2-ang1) > Math.PI) {
+                maxrprime = Math.max(rprime, maxrprime);  
+            }
+        }
+
+        if (this.diameter != null && this.diameter > 0) {
+            this._diameter = this.diameter - 2*maxrprime;
+        }
+        else {
+            this._diameter = d - 2*maxrprime;
+        }
+
+        // Need to check for undersized pie.  This can happen if
+        // plot area too small and legend is too big.
+        if (this._diameter < 6) {
+            $.jqplot.log('Diameter of pie too small, not rendering.');
+            return;
+        }
+
+        var r = this._radius = this._diameter/2;
+
+        this._center = [(cw - trans * offx)/2 + trans * offx + maxrprime * Math.cos(sa), (ch - trans*offy)/2 + trans * offy + maxrprime * Math.sin(sa)];
+
+        if (this.shadow) {
+            for (var i=0, l=gd.length; i<l; i++) {
+                shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
+                this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], shadowColor, true);
+            }
+        }
+        
+        for (var i=0; i<gd.length; i++) {
+                      
+            this.renderer.drawSlice.call (this, ctx, this._sliceAngles[i][0], this._sliceAngles[i][1], colorGenerator.next(), false);
+        
+            if (this.showDataLabels && gd[i][2]*100 >= this.dataLabelThreshold) {
+                var fstr, avgang = (this._sliceAngles[i][0] + this._sliceAngles[i][1])/2, label;
+            
+                if (this.dataLabels == 'label') {
+                    fstr = this.dataLabelFormatString || '%s';
+                    label = $.jqplot.sprintf(fstr, gd[i][0]);
+                }
+                else if (this.dataLabels == 'value') {
+                    fstr = this.dataLabelFormatString || '%d';
+                    label = $.jqplot.sprintf(fstr, this.data[i][1]);
+                }
+                else if (this.dataLabels == 'percent') {
+                    fstr = this.dataLabelFormatString || '%d%%';
+                    label = $.jqplot.sprintf(fstr, gd[i][2]*100);
+                }
+                else if (this.dataLabels.constructor == Array) {
+                    fstr = this.dataLabelFormatString || '%s';
+                    label = $.jqplot.sprintf(fstr, this.dataLabels[i]);
+                }
+            
+                var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
+            
+                var x = this._center[0] + Math.cos(avgang) * fact + this.canvas._offsets.left;
+                var y = this._center[1] + Math.sin(avgang) * fact + this.canvas._offsets.top;
+            
+                var labelelem = $('<div class="jqplot-pie-series jqplot-data-label" style="position:absolute;">' + label + '</div>').insertBefore(plot.eventCanvas._elem);
+                if (this.dataLabelCenterOn) {
+                    x -= labelelem.width()/2;
+                    y -= labelelem.height()/2;
+                }
+                else {
+                    x -= labelelem.width() * Math.sin(avgang/2);
+                    y -= labelelem.height()/2;
+                }
+                x = Math.round(x);
+                y = Math.round(y);
+                labelelem.css({left: x, top: y});
+            }
+        }            
+    };
+    
+    $.jqplot.PieAxisRenderer = function() {
+        $.jqplot.LinearAxisRenderer.call(this);
+    };
+    
+    $.jqplot.PieAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
+    $.jqplot.PieAxisRenderer.prototype.constructor = $.jqplot.PieAxisRenderer;
+        
+    
+    // There are no traditional axes on a pie chart.  We just need to provide
+    // dummy objects with properties so the plot will render.
+    // called with scope of axis object.
+    $.jqplot.PieAxisRenderer.prototype.init = function(options){
+        //
+        this.tickRenderer = $.jqplot.PieTickRenderer;
+        $.extend(true, this, options);
+        // I don't think I'm going to need _dataBounds here.
+        // have to go Axis scaling in a way to fit chart onto plot area
+        // and provide u2p and p2u functionality for mouse cursor, etc.
+        // for convienence set _dataBounds to 0 and 100 and
+        // set min/max to 0 and 100.
+        this._dataBounds = {min:0, max:100};
+        this.min = 0;
+        this.max = 100;
+        this.showTicks = false;
+        this.ticks = [];
+        this.showMark = false;
+        this.show = false; 
+    };
+    
+    
+    
+    
+    $.jqplot.PieLegendRenderer = function(){
+        $.jqplot.TableLegendRenderer.call(this);
+    };
+    
+    $.jqplot.PieLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
+    $.jqplot.PieLegendRenderer.prototype.constructor = $.jqplot.PieLegendRenderer;
+    
+    /**
+     * Class: $.jqplot.PieLegendRenderer
+     * Legend Renderer specific to pie plots.  Set by default
+     * when user creates a pie plot.
+     */
+    $.jqplot.PieLegendRenderer.prototype.init = function(options) {
+        // Group: Properties
+        //
+        // prop: numberRows
+        // Maximum number of rows in the legend.  0 or null for unlimited.
+        this.numberRows = null;
+        // prop: numberColumns
+        // Maximum number of columns in the legend.  0 or null for unlimited.
+        this.numberColumns = null;
+        $.extend(true, this, options);
+    };
+    
+    // called with context of legend
+    $.jqplot.PieLegendRenderer.prototype.draw = function() {
+        var legend = this;
+        if (this.show) {
+            var series = this._series;
+
+
+            this._elem = $(document.createElement('table'));
+            this._elem.addClass('jqplot-table-legend');
+
+            var ss = {position:'absolute'};
+            if (this.background) {
+                ss['background'] = this.background;
+            }
+            if (this.border) {
+                ss['border'] = this.border;
+            }
+            if (this.fontSize) {
+                ss['fontSize'] = this.fontSize;
+            }
+            if (this.fontFamily) {
+                ss['fontFamily'] = this.fontFamily;
+            }
+            if (this.textColor) {
+                ss['textColor'] = this.textColor;
+            }
+            if (this.marginTop != null) {
+                ss['marginTop'] = this.marginTop;
+            }
+            if (this.marginBottom != null) {
+                ss['marginBottom'] = this.marginBottom;
+            }
+            if (this.marginLeft != null) {
+                ss['marginLeft'] = this.marginLeft;
+            }
+            if (this.marginRight != null) {
+                ss['marginRight'] = this.marginRight;
+            }
+
+            this._elem.css(ss);
+
+            // Pie charts legends don't go by number of series, but by number of data points
+            // in the series.  Refactor things here for that.
+            
+            var pad = false, 
+                reverse = false,
+                nr, 
+                nc;
+            var s = series[0];
+            var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
+            
+            if (s.show) {
+                var pd = s.data;
+                if (this.numberRows) {
+                    nr = this.numberRows;
+                    if (!this.numberColumns){
+                        nc = Math.ceil(pd.length/nr);
+                    }
+                    else{
+                        nc = this.numberColumns;
+                    }
+                }
+                else if (this.numberColumns) {
+                    nc = this.numberColumns;
+                    nr = Math.ceil(pd.length/this.numberColumns);
+                }
+                else {
+                    nr = pd.length;
+                    nc = 1;
+                }
+                
+                var i, j;
+                var tr, td1, td2; 
+                var lt, rs, color;
+                var idx = 0; 
+                var div0, div1;   
+                
+                for (i=0; i<nr; i++) {
+                    tr = $(document.createElement('tr'));
+                    tr.addClass('jqplot-table-legend');
+                    
+                    if (reverse){
+                        tr.prependTo(this._elem);
+                    }
+                    
+                    else{
+                        tr.appendTo(this._elem);
+                    }
+                    
+                    for (j=0; j<nc; j++) {
+                        if (idx < pd.length){
+                            lt = this.labels[idx] || pd[idx][0].toString();
+                            color = colorGenerator.next();
+                            if (!reverse){
+                                if (i>0){
+                                    pad = true;
+                                }
+                                else{
+                                    pad = false;
+                                }
+                            }
+                            else{
+                                if (i == nr -1){
+                                    pad = false;
+                                }
+                                else{
+                                    pad = true;
+                                }
+                            }
+                            rs = (pad) ? this.rowSpacing : '0';
+
+
+
+                            td1 = $(document.createElement('td'));
+                            td1.addClass('jqplot-table-legend jqplot-table-legend-swatch');
+                            td1.css({textAlign: 'center', paddingTop: rs});
+
+                            div0 = $(document.createElement('div'));
+                            div0.addClass('jqplot-table-legend-swatch-outline');
+                            div1 = $(document.createElement('div'));
+                            div1.addClass('jqplot-table-legend-swatch');
+                            div1.css({backgroundColor: color, borderColor: color});
+                            td1.append(div0.append(div1));
+
+                            td2 = $(document.createElement('td'));
+                            td2.addClass('jqplot-table-legend jqplot-table-legend-label');
+                            td2.css('paddingTop', rs);
+
+                            if (this.escapeHtml){
+                                td2.text(lt);
+                            }
+                            else {
+                                td2.html(lt);
+                            }
+                            if (reverse) {
+                                td2.prependTo(tr);
+                                td1.prependTo(tr);
+                            }
+                            else {
+                                td1.appendTo(tr);
+                                td2.appendTo(tr);
+                            }
+                            pad = true;
+                        }
+                        idx++;
+                    }   
+                }
+            }
+        }
+        return this._elem;                
+    };
+    
+    $.jqplot.PieRenderer.prototype.handleMove = function(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            plot.target.trigger('jqplotDataMouseOver', ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                plot.target.trigger('jqplotDataHighlight', ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    };
+    
+    
+    // this.eventCanvas._elem.bind($.jqplot.eventListenerHooks[i][0], {plot:this}, $.jqplot.eventListenerHooks[i][1]);
+    
+    // setup default renderers for axes and legend so user doesn't have to
+    // called with scope of plot
+    function preInit(target, data, options) {
+        options = options || {};
+        options.axesDefaults = options.axesDefaults || {};
+        options.legend = options.legend || {};
+        options.seriesDefaults = options.seriesDefaults || {};
+        // only set these if there is a pie series
+        var setopts = false;
+        if (options.seriesDefaults.renderer == $.jqplot.PieRenderer) {
+            setopts = true;
+        }
+        else if (options.series) {
+            for (var i=0; i < options.series.length; i++) {
+                if (options.series[i].renderer == $.jqplot.PieRenderer) {
+                    setopts = true;
+                }
+            }
+        }
+        
+        if (setopts) {
+            options.axesDefaults.renderer = $.jqplot.PieAxisRenderer;
+            options.legend.renderer = $.jqplot.PieLegendRenderer;
+            options.legend.preDraw = true;
+            options.seriesDefaults.pointLabels = {show: false};
+        }
+    }
+    
+    function postInit(target, data, options) {
+        for (var i=0; i<this.series.length; i++) {
+            if (this.series[i].renderer.constructor == $.jqplot.PieRenderer) {
+                // don't allow mouseover and mousedown at same time.
+                if (this.series[i].highlightMouseOver) {
+                    this.series[i].highlightMouseDown = false;
+                }
+            }
+        }
+    }
+    
+    // called with scope of plot
+    function postParseOptions(options) {
+        for (var i=0; i<this.series.length; i++) {
+            this.series[i].seriesColors = this.seriesColors;
+            this.series[i].colorGenerator = $.jqplot.colorGenerator;
+        }
+    }
+    
+    function highlight (plot, sidx, pidx) {
+        var s = plot.series[sidx];
+        var canvas = plot.plugins.pieRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        s._highlightedPoint = pidx;
+        plot.plugins.pieRenderer.highlightedSeriesIndex = sidx;
+        s.renderer.drawSlice.call(s, canvas._ctx, s._sliceAngles[pidx][0], s._sliceAngles[pidx][1], s.highlightColorGenerator.get(pidx), false);
+    }
+    
+    function unhighlight (plot) {
+        var canvas = plot.plugins.pieRenderer.highlightCanvas;
+        canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
+        for (var i=0; i<plot.series.length; i++) {
+            plot.series[i]._highlightedPoint = null;
+        }
+        plot.plugins.pieRenderer.highlightedSeriesIndex = null;
+        plot.target.trigger('jqplotDataUnhighlight');
+    }
+ 
+    function handleMove(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt1 = jQuery.Event('jqplotDataMouseOver');
+            evt1.pageX = ev.pageX;
+            evt1.pageY = ev.pageY;
+            plot.target.trigger(evt1, ins);
+            if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+		evt.which = ev.which;
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    } 
+    
+    function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.pieRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
+                var evt = jQuery.Event('jqplotDataHighlight');
+		evt.which = ev.which;
+                evt.pageX = ev.pageX;
+                evt.pageY = ev.pageY;
+                plot.target.trigger(evt, ins);
+                highlight (plot, ins[0], ins[1]);
+            }
+        }
+        else if (neighbor == null) {
+            unhighlight (plot);
+        }
+    }
+    
+    function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
+        var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
+        if (idx != null && plot.series[idx].highlightMouseDown) {
+            unhighlight(plot);
+        }
+    }
+    
+    function handleClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var evt = jQuery.Event('jqplotDataClick');
+	    evt.which = ev.which;
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }
+    
+    function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
+        if (neighbor) {
+            var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
+            var idx = plot.plugins.pieRenderer.highlightedSeriesIndex;
+            if (idx != null && plot.series[idx].highlightMouseDown) {
+                unhighlight(plot);
+            }
+            var evt = jQuery.Event('jqplotDataRightClick');
+	    evt.which = ev.which;
+            evt.pageX = ev.pageX;
+            evt.pageY = ev.pageY;
+            plot.target.trigger(evt, ins);
+        }
+    }    
+    
+    // called within context of plot
+    // create a canvas which we can draw on.
+    // insert it before the eventCanvas, so eventCanvas will still capture events.
+    function postPlotDraw() {
+        // Memory Leaks patch    
+        if (this.plugins.pieRenderer && this.plugins.pieRenderer.highlightCanvas) {
+            this.plugins.pieRenderer.highlightCanvas.resetCanvas();
+            this.plugins.pieRenderer.highlightCanvas = null;
+        }
+
+        this.plugins.pieRenderer = {highlightedSeriesIndex:null};
+        this.plugins.pieRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
+        
+        // do we have any data labels?  if so, put highlight canvas before those
+        var labels = $(this.targetId+' .jqplot-data-label');
+        if (labels.length) {
+            $(labels[0]).before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this));
+        }
+        // else put highlight canvas before event canvas.
+        else {
+            this.eventCanvas._elem.before(this.plugins.pieRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-pieRenderer-highlight-canvas', this._plotDimensions, this));
+        }
+        
+        var hctx = this.plugins.pieRenderer.highlightCanvas.setContext();
+        this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
+    }
+    
+    $.jqplot.preInitHooks.push(preInit);
+    
+    $.jqplot.PieTickRenderer = function() {
+        $.jqplot.AxisTickRenderer.call(this);
+    };
+    
+    $.jqplot.PieTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
+    $.jqplot.PieTickRenderer.prototype.constructor = $.jqplot.PieTickRenderer;
+    
+})(jQuery);
+    
+    

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.pointLabels.min.js


Разница между файлами не показана из-за своего большого размера
+ 728 - 0
web/js/jqplot/plugins/jqplot.pyramidAxisRenderer.js


+ 429 - 0
web/js/jqplot/plugins/jqplot.pyramidGridRenderer.js

@@ -0,0 +1,429 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {     
+    // Class: $.jqplot.CanvasGridRenderer
+    // The default jqPlot grid renderer, creating a grid on a canvas element.
+    // The renderer has no additional options beyond the <Grid> class.
+    $.jqplot.PyramidGridRenderer = function(){
+        $.jqplot.CanvasGridRenderer.call(this);
+    };
+
+    $.jqplot.PyramidGridRenderer.prototype = new $.jqplot.CanvasGridRenderer();
+    $.jqplot.PyramidGridRenderer.prototype.constructor = $.jqplot.PyramidGridRenderer;
+    
+    // called with context of Grid object
+    $.jqplot.CanvasGridRenderer.prototype.init = function(options) {
+        this._ctx;
+        this.plotBands = {
+            show: false,
+            color: 'rgb(230, 219, 179)',
+            axis: 'y',
+            start: null,
+            interval: 10
+        };
+        $.extend(true, this, options);
+        // set the shadow renderer options
+        var sopts = {lineJoin:'miter', lineCap:'round', fill:false, isarc:false, angle:this.shadowAngle, offset:this.shadowOffset, alpha:this.shadowAlpha, depth:this.shadowDepth, lineWidth:this.shadowWidth, closePath:false, strokeStyle:this.shadowColor};
+        this.renderer.shadowRenderer.init(sopts);
+    };
+    
+    $.jqplot.PyramidGridRenderer.prototype.draw = function() {
+        this._ctx = this._elem.get(0).getContext("2d");
+        var ctx = this._ctx;
+        var axes = this._axes;
+        var xp = axes.xaxis.u2p;
+        var yp = axes.yMidAxis.u2p;
+        var xnudge = axes.xaxis.max/1000.0;
+        var xp0 = xp(0);
+        var xpn = xp(xnudge);
+        var ax = ['xaxis', 'yaxis', 'x2axis', 'y2axis','yMidAxis'];
+        // Add the grid onto the grid canvas.  This is the bottom most layer.
+        ctx.save();
+        ctx.clearRect(0, 0, this._plotDimensions.width, this._plotDimensions.height);
+        ctx.fillStyle = this.backgroundColor || this.background;
+
+        ctx.fillRect(this._left, this._top, this._width, this._height);
+
+        if (this.plotBands.show) {
+            ctx.save();
+            var pb = this.plotBands;
+            ctx.fillStyle = pb.color;
+            var axis;
+            var x, y, w, h;
+            // find axis to work with
+            if (pb.axis.charAt(0) === 'x') {
+                if (axes.xaxis.show) {
+                    axis = axes.xaxis;
+                }
+            }
+            else if (pb.axis.charAt(0) === 'y') {
+                if (axes.yaxis.show) {
+                    axis = axes.yaxis;
+                }
+                else if (axes.y2axis.show) {
+                    axis = axes.y2axis;
+                }
+                else if (axes.yMidAxis.show) {
+                    axis = axes.yMidAxis;
+                }
+            }
+
+            if (axis !== undefined) {
+                // draw some rectangles
+                var start = pb.start;
+                if (start === null) {
+                    start = axis.min;
+                }
+                for (var i = start; i < axis.max; i += 2 * pb.interval) {
+                    if (axis.name.charAt(0) === 'y') {
+                        x = this._left;
+                        if ((i + pb.interval) < axis.max) {
+                            y = axis.series_u2p(i + pb.interval) + this._top;
+                        }
+                        else {
+                            y = axis.series_u2p(axis.max) + this._top;
+                        }
+                        w = this._right - this._left;
+                        h = axis.series_u2p(start) - axis.series_u2p(start + pb.interval);
+                        ctx.fillRect(x, y, w, h);
+                    }
+                    // else {
+                    //     y = 0;
+                    //     x = axis.series_u2p(i);
+                    //     h = this._height;
+                    //     w = axis.series_u2p(start + pb.interval) - axis.series_u2p(start);
+                    // }
+
+                }
+            }
+            ctx.restore();
+        }
+        
+        ctx.save();
+        ctx.lineJoin = 'miter';
+        ctx.lineCap = 'butt';
+        ctx.lineWidth = this.gridLineWidth;
+        ctx.strokeStyle = this.gridLineColor;
+        var b, e, s, m;
+        for (var i=5; i>0; i--) {
+            var name = ax[i-1];
+            var axis = axes[name];
+            var ticks = axis._ticks;
+            var numticks = ticks.length;
+            if (axis.show) {
+                if (axis.drawBaseline) {
+                    var bopts = {};
+                    if (axis.baselineWidth !== null) {
+                        bopts.lineWidth = axis.baselineWidth;
+                    }
+                    if (axis.baselineColor !== null) {
+                        bopts.strokeStyle = axis.baselineColor;
+                    }
+                    switch (name) {
+                        case 'xaxis':
+                            if (axes.yMidAxis.show) {
+                                drawLine (this._left, this._bottom, xp0, this._bottom, bopts);
+                                drawLine (xpn, this._bottom, this._right, this._bottom, bopts);
+                            }
+                            else {
+                                drawLine (this._left, this._bottom, this._right, this._bottom, bopts);
+                            }
+                            break;
+                        case 'yaxis':
+                            drawLine (this._left, this._bottom, this._left, this._top, bopts);
+                            break;
+                        case 'yMidAxis':               
+                            drawLine(xp0, this._bottom, xp0, this._top, bopts);
+                            drawLine(xpn, this._bottom, xpn, this._top, bopts);
+                            break;
+                        case 'x2axis':
+                            if (axes.yMidAxis.show) {
+                                drawLine (this._left, this._top, xp0, this._top, bopts);
+                                drawLine (xpn, this._top, this._right, this._top, bopts);
+                            }
+                            else {
+                                drawLine (this._left, this._bottom, this._right, this._bottom, bopts);
+                            }
+                            break;
+                        case 'y2axis':
+                            drawLine (this._right, this._bottom, this._right, this._top, bopts);
+                            break;
+
+                    }
+                }
+                for (var j=numticks; j>0; j--) {
+                    var t = ticks[j-1];
+                    if (t.show) {
+                        var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                        switch (name) {
+                            case 'xaxis':
+                                // draw the grid line if we should
+                                if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    drawLine(pos, this._top, pos, this._bottom);
+                                }
+                                
+                                // draw the mark
+                                if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    s = t.markSize;
+                                    m = t.mark;
+                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                                    switch (m) {
+                                        case 'outside':
+                                            b = this._bottom;
+                                            e = this._bottom+s;
+                                            break;
+                                        case 'inside':
+                                            b = this._bottom-s;
+                                            e = this._bottom;
+                                            break;
+                                        case 'cross':
+                                            b = this._bottom-s;
+                                            e = this._bottom+s;
+                                            break;
+                                        default:
+                                            b = this._bottom;
+                                            e = this._bottom+s;
+                                            break;
+                                    }
+                                    // draw the shadow
+                                    if (this.shadow) {
+                                        this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
+                                    }
+                                    // draw the line
+                                    drawLine(pos, b, pos, e);
+                                }
+                                break;
+                            case 'yaxis':
+                                // draw the grid line
+                                if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    drawLine(this._right, pos, this._left, pos);
+                                }
+
+                                // draw the mark
+                                if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    s = t.markSize;
+                                    m = t.mark;
+                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                                    switch (m) {
+                                        case 'outside':
+                                            b = this._left-s;
+                                            e = this._left;
+                                            break;
+                                        case 'inside':
+                                            b = this._left;
+                                            e = this._left+s;
+                                            break;
+                                        case 'cross':
+                                            b = this._left-s;
+                                            e = this._left+s;
+                                            break;
+                                        default:
+                                            b = this._left-s;
+                                            e = this._left;
+                                            break;
+                                            }
+                                    // draw the shadow
+                                    if (this.shadow) {
+                                        this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
+                                    }
+                                    drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
+                                }
+                                break;
+                            case 'yMidAxis':
+                                // draw the grid line
+                                if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    drawLine(this._left, pos, xp0, pos);
+                                    drawLine(xpn, pos, this._right, pos);
+                                }
+                                // draw the mark
+                                if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    s = t.markSize;
+                                    m = t.mark;
+                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
+
+                                    b = xp0;
+                                    e = xp0 + s;
+                                    // draw the shadow
+                                    if (this.shadow) {
+                                        this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
+                                    }
+                                    drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
+
+                                    b = xpn - s;
+                                    e = xpn;
+                                    // draw the shadow
+                                    if (this.shadow) {
+                                        this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
+                                    }
+                                    drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
+                                }
+                                break;
+                            case 'x2axis':
+                                // draw the grid line
+                                if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    drawLine(pos, this._bottom, pos, this._top);
+                                }
+
+                                // draw the mark
+                                if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    s = t.markSize;
+                                    m = t.mark;
+                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                                    switch (m) {
+                                        case 'outside':
+                                            b = this._top-s;
+                                            e = this._top;
+                                            break;
+                                        case 'inside':
+                                            b = this._top;
+                                            e = this._top+s;
+                                            break;
+                                        case 'cross':
+                                            b = this._top-s;
+                                            e = this._top+s;
+                                            break;
+                                        default:
+                                            b = this._top-s;
+                                            e = this._top;
+                                            break;
+                                            }
+                                    // draw the shadow
+                                    if (this.shadow) {
+                                        this.renderer.shadowRenderer.draw(ctx, [[pos,b],[pos,e]], {lineCap:'butt', lineWidth:this.gridLineWidth, offset:this.gridLineWidth*0.75, depth:2, fill:false, closePath:false});
+                                    }
+                                    drawLine(pos, b, pos, e);
+                                }
+                                break;
+                            case 'y2axis':
+                                // draw the grid line
+                                if (t.showGridline && this.drawGridlines && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    drawLine(this._left, pos, this._right, pos);
+                                }
+
+                                // draw the mark
+                                if (t.showMark && t.mark && (!t.isMinorTick || axis.showMinorTicks)) {
+                                    s = t.markSize;
+                                    m = t.mark;
+                                    var pos = Math.round(axis.u2p(t.value)) + 0.5;
+                                    switch (m) {
+                                        case 'outside':
+                                            b = this._right;
+                                            e = this._right+s;
+                                            break;
+                                        case 'inside':
+                                            b = this._right-s;
+                                            e = this._right;
+                                            break;
+                                        case 'cross':
+                                            b = this._right-s;
+                                            e = this._right+s;
+                                            break;
+                                        default:
+                                            b = this._right;
+                                            e = this._right+s;
+                                            break;
+                                            }
+                                    // draw the shadow
+                                    if (this.shadow) {
+                                        this.renderer.shadowRenderer.draw(ctx, [[b, pos], [e, pos]], {lineCap:'butt', lineWidth:this.gridLineWidth*1.5, offset:this.gridLineWidth*0.75, fill:false, closePath:false});
+                                    }
+                                    drawLine(b, pos, e, pos, {strokeStyle:axis.borderColor});
+                                }
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                }
+                t = null;
+            }
+            axis = null;
+            ticks = null;
+        }
+        
+        ctx.restore();
+        
+        function drawLine(bx, by, ex, ey, opts) {
+            ctx.save();
+            opts = opts || {};
+            if (opts.lineWidth == null || opts.lineWidth != 0){
+                $.extend(true, ctx, opts);
+                ctx.beginPath();
+                ctx.moveTo(bx, by);
+                ctx.lineTo(ex, ey);
+                ctx.stroke();
+            }
+            ctx.restore();
+        }
+        
+        if (this.shadow) {
+            if (axes.yMidAxis.show) {
+                var points = [[this._left, this._bottom], [xp0, this._bottom]];
+                this.renderer.shadowRenderer.draw(ctx, points);
+                var points = [[xpn, this._bottom], [this._right, this._bottom], [this._right, this._top]];
+                this.renderer.shadowRenderer.draw(ctx, points);
+                var points = [[xp0, this._bottom], [xp0, this._top]];
+                this.renderer.shadowRenderer.draw(ctx, points);
+            }
+            else {
+                var points = [[this._left, this._bottom], [this._right, this._bottom], [this._right, this._top]];
+                this.renderer.shadowRenderer.draw(ctx, points);
+            }
+        }
+        // Now draw border around grid.  Use axis border definitions. start at
+        // upper left and go clockwise.
+        if (this.borderWidth != 0 && this.drawBorder) {
+            if (axes.yMidAxis.show) {
+                drawLine (this._left, this._top, xp0, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
+                drawLine (xpn, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
+                drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
+                drawLine (this._right, this._bottom, xpn, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
+                drawLine (xp0, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
+                drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
+                drawLine (xp0, this._bottom, xp0, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
+                drawLine (xpn, this._bottom, xpn, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
+            }
+            else {
+                drawLine (this._left, this._top, this._right, this._top, {lineCap:'round', strokeStyle:axes.x2axis.borderColor, lineWidth:axes.x2axis.borderWidth});
+                drawLine (this._right, this._top, this._right, this._bottom, {lineCap:'round', strokeStyle:axes.y2axis.borderColor, lineWidth:axes.y2axis.borderWidth});
+                drawLine (this._right, this._bottom, this._left, this._bottom, {lineCap:'round', strokeStyle:axes.xaxis.borderColor, lineWidth:axes.xaxis.borderWidth});
+                drawLine (this._left, this._bottom, this._left, this._top, {lineCap:'round', strokeStyle:axes.yaxis.borderColor, lineWidth:axes.yaxis.borderWidth});
+            }
+        }
+        // ctx.lineWidth = this.borderWidth;
+        // ctx.strokeStyle = this.borderColor;
+        // ctx.strokeRect(this._left, this._top, this._width, this._height);
+        
+        ctx.restore();
+        ctx =  null;
+        axes = null;
+    };
+})(jQuery); 

Разница между файлами не показана из-за своего большого размера
+ 57 - 0
web/js/jqplot/plugins/jqplot.pyramidRenderer.min.js


+ 223 - 0
web/js/jqplot/plugins/jqplot.trendline.js

@@ -0,0 +1,223 @@
+/**
+ * jqPlot
+ * Pure JavaScript plotting plugin using jQuery
+ *
+ * Version: 1.0.4
+ * Revision: 1121
+ *
+ * Copyright (c) 2009-2012 Chris Leonello
+ * jqPlot is currently available for use in all personal or commercial projects 
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL 
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can 
+ * choose the license that best suits your project and use it accordingly. 
+ *
+ * Although not required, the author would appreciate an email letting him 
+ * know of any substantial use of jqPlot.  You can reach the author at: 
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
+ *
+ * If you are feeling kind and generous, consider supporting the project by
+ * making a donation at: http://www.jqplot.com/donate.php .
+ *
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
+ *
+ *     version 2007.04.27
+ *     author Ash Searle
+ *     http://hexmen.com/blog/2007/03/printf-sprintf/
+ *     http://hexmen.com/js/sprintf.js
+ *     The author (Ash Searle) has placed this code in the public domain:
+ *     "This code is unrestricted: you are free to use it however you like."
+ * 
+ */
+(function($) {
+    
+    /**
+     * Class: $.jqplot.Trendline
+     * Plugin which will automatically compute and draw trendlines for plotted data.
+     */
+    $.jqplot.Trendline = function() {
+        // Group: Properties
+        
+        // prop: show
+        // Wether or not to show the trend line.
+        this.show = $.jqplot.config.enablePlugins;
+        // prop: color
+        // CSS color spec for the trend line.
+        // By default this wil be the same color as the primary line.
+        this.color = '#666666';
+        // prop: renderer
+        // Renderer to use to draw the trend line.
+        // The data series that is plotted may not be rendered as a line.
+        // Therefore, we use our own line renderer here to draw a trend line.
+        this.renderer = new $.jqplot.LineRenderer();
+        // prop: rendererOptions
+        // Options to pass to the line renderer.
+        // By default, markers are not shown on trend lines.
+        this.rendererOptions = {marker:{show:false}};
+        // prop: label
+        // Label for the trend line to use in the legend.
+        this.label = '';
+        // prop: type
+        // Either 'exponential', 'exp', or 'linear'.
+        this.type = 'linear';
+        // prop: shadow
+        // true or false, wether or not to show the shadow.
+        this.shadow = true;
+        // prop: markerRenderer
+        // Renderer to use to draw markers on the line.
+        // I think this is wrong.
+        this.markerRenderer = {show:false};
+        // prop: lineWidth
+        // Width of the trend line.
+        this.lineWidth = 1.5;
+        // prop: shadowAngle
+        // Angle of the shadow on the trend line.
+        this.shadowAngle = 45;
+        // prop: shadowOffset
+        // pixel offset for each stroke of the shadow.
+        this.shadowOffset = 1.0;
+        // prop: shadowAlpha
+        // Alpha transparency of the shadow.
+        this.shadowAlpha = 0.07;
+        // prop: shadowDepth
+        // number of strokes to make of the shadow.
+        this.shadowDepth = 3;
+        this.isTrendline = true;
+        
+    };
+    
+    $.jqplot.postSeriesInitHooks.push(parseTrendLineOptions);
+    $.jqplot.postDrawSeriesHooks.push(drawTrendline);
+    $.jqplot.addLegendRowHooks.push(addTrendlineLegend);
+    
+    // called witin scope of the legend object
+    // current series passed in
+    // must return null or an object {label:label, color:color}
+    function addTrendlineLegend(series) {
+        var ret = null;
+        if (series.trendline && series.trendline.show) {
+            var lt = series.trendline.label.toString();
+            if (lt) {
+                ret = {label:lt, color:series.trendline.color};
+            }
+        }
+        return ret;
+    }
+
+    // called within scope of a series
+    function parseTrendLineOptions (target, data, seriesDefaults, options, plot) {
+        if (this._type && (this._type === 'line' || this._type == 'bar')) {
+            this.trendline = new $.jqplot.Trendline();
+            options = options || {};
+            $.extend(true, this.trendline, {color:this.color}, seriesDefaults.trendline, options.trendline);
+            this.trendline.renderer.init.call(this.trendline, null);
+        }
+    }
+    
+    // called within scope of series object
+    function drawTrendline(sctx, options) {
+        // if we have options, merge trendline options in with precedence
+        options = $.extend(true, {}, this.trendline, options);
+
+        if (this.trendline && options.show) {
+            var fit;
+            // this.renderer.setGridData.call(this);
+            var data = options.data || this.data;
+            fit = fitData(data, this.trendline.type);
+            var gridData = options.gridData || this.renderer.makeGridData.call(this, fit.data);
+            this.trendline.renderer.draw.call(this.trendline, sctx, gridData, {showLine:true, shadow:this.trendline.shadow});
+        }
+    }
+    
+    function regression(x, y, typ)  {
+        var type = (typ == null) ? 'linear' : typ;
+        var N = x.length;
+        var slope;
+        var intercept;  
+        var SX = 0;
+        var SY = 0;
+        var SXX = 0;
+        var SXY = 0;
+        var SYY = 0;
+        var Y = [];
+        var X = [];
+    
+        if (type == 'linear') {
+            X = x;
+            Y = y;
+        }
+        else if (type == 'exp' || type == 'exponential') {
+            for ( var i=0; i<y.length; i++) {
+                // ignore points <= 0, log undefined.
+                if (y[i] <= 0) {
+                    N--;
+                }
+                else {
+                    X.push(x[i]);
+                    Y.push(Math.log(y[i]));
+                }
+            }
+        }
+
+        for ( var i = 0; i < N; i++) {
+            SX = SX + X[i];
+            SY = SY + Y[i];
+            SXY = SXY + X[i]* Y[i];
+            SXX = SXX + X[i]* X[i];
+            SYY = SYY + Y[i]* Y[i];
+        }
+
+        slope = (N*SXY - SX*SY)/(N*SXX - SX*SX);
+        intercept = (SY - slope*SX)/N;
+
+        return [slope, intercept];
+    }
+
+    function linearRegression(X,Y) {
+        var ret;
+        ret = regression(X,Y,'linear');
+        return [ret[0],ret[1]];
+    }
+
+    function expRegression(X,Y) {
+        var ret;
+        var x = X;
+        var y = Y;
+        ret = regression(x, y,'exp');
+        var base = Math.exp(ret[0]);
+        var coeff = Math.exp(ret[1]);
+        return [base, coeff];
+    }
+
+    function fitData(data, typ) {
+        var type = (typ == null) ?  'linear' : typ;
+        var ret;
+        var res;
+        var x = [];
+        var y = [];
+        var ypred = [];
+        
+        for (i=0; i<data.length; i++){
+            if (data[i] != null && data[i][0] != null && data[i][1] != null) {
+                x.push(data[i][0]);
+                y.push(data[i][1]);
+            }
+        }
+        
+        if (type == 'linear') {
+            ret = linearRegression(x,y);
+            for ( var i=0; i<x.length; i++){
+                res = ret[0]*x[i] + ret[1];
+                ypred.push([x[i], res]);
+            }
+        }
+        else if (type == 'exp' || type == 'exponential') {
+            ret = expRegression(x,y);
+            for ( var i=0; i<x.length; i++){
+                res = ret[1]*Math.pow(ret[0],x[i]);
+                ypred.push([x[i], res]);
+            }
+        }
+        return {data: ypred, slope: ret[0], intercept: ret[1]};
+    } 
+
+})(jQuery);

+ 47 - 0
web/js/jquery-ui-portlet/css/jquery.portlet.css

@@ -0,0 +1,47 @@
+/*
+ * jquery.portlet 1.1.2
+ *
+ * Copyright (c) 2012
+ *   咖啡兔 (http://www.kafeitu.me)
+ *
+ * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
+ * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
+ *
+ * See Detail: http://www.kafeitu.me/jquery-ui-portlet.html
+ */
+.ui-portlet-column {
+	float: left;
+	padding-bottom: 100px;
+}
+
+.ui-portlet-header {
+	padding: .4em 0.7em
+}
+
+.ui-portlet-header .ui-icon {
+	float: right;
+}
+
+.ui-portlet-item {
+	margin: 0 1em 1em 0;
+}
+
+.ui-portlet-content {
+	padding: 0.3em;
+	overflow-y:auto;
+}
+
+.ui-portlet-header .ui-portlet-header-icon {
+	float: left;
+	margin: 0 0.2em 0 -0.5em;
+}
+
+.ui-sortable-placeholder {
+	border: 1px dotted black;
+	visibility: visible !important;
+	height: 50px !important;
+}
+
+.ui-sortable-placeholder * {
+	visibility: hidden;
+}

+ 12 - 0
web/js/jquery-ui-portlet/css/jquery.portlet.min.css

@@ -0,0 +1,12 @@
+/*
+ * jquery.portlet 1.1.1
+ *
+ * Copyright (c) 2012
+ *   咖啡兔 (http://www.kafeitu.me)
+ *
+ * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
+ * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
+ *
+ * See Detail: http://www.kafeitu.me/jquery-ui-portlet.html
+ */
+.ui-portlet-column{float:left;padding-bottom:100px;}.ui-portlet-header{padding:.4em 0.7em}.ui-portlet-header .ui-icon{float:right;}.ui-portlet-item{margin:0 1em 1em 0;}.ui-portlet-content{padding:0.3em;overflow-y:auto;}.ui-portlet-header .ui-portlet-header-icon{float:left;margin:0 0.2em 0 -0.5em;}.ui-sortable-placeholder{border:1px dotted black;visibility:visible !important;height:50px !important;}.ui-sortable-placeholder *{visibility:hidden;}

+ 19 - 0
web/js/jquery-ui-portlet/demo/ajax-fixed-height.json

@@ -0,0 +1,19 @@
+[{
+	"text": "内容过高时会自动出现滚动条"
+}, {
+	"text": "关于XXXXX的111111"
+}, {
+	"text": "关于XXXXX的22222"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}]

+ 19 - 0
web/js/jquery-ui-portlet/demo/ajax.json

@@ -0,0 +1,19 @@
+[{
+	"text": "关于XXXXX的111111"
+}, {
+	"text": "关于XXXXX的22222"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}, {
+	"text": "关于XXXXX的。。。。"
+}]

+ 124 - 0
web/js/jquery-ui-portlet/demo/colspan.html

@@ -0,0 +1,124 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="utf-8" />
+    <title>jQuery UI Portlet</title>
+    <!-- jquery 1.4 and jquery ui 1.8 -->
+    <link rel="stylesheet" href="../lib/themes/1.8/start/jquery-ui-1.8.5.custom.css" />
+    <link rel="stylesheet" href="../css/jquery.portlet.css?v=1.1.3" />
+
+    <script src="../lib/jquery-1.4.4.min.js" type="text/javascript"></script>
+    <script src="../lib/jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>
+
+    <!--<link rel="stylesheet" href="../lib/themes/1.9/start/jquery-ui-1.9.1.custom.min.css" />-->
+    <!--<script src="../lib/jquery-1.8.3.js" type="text/javascript"></script>-->
+    <!--<script src="../lib/themes/jquery-ui-1.9.1.custom.min.js" type="text/javascript"></script>-->
+
+    <script src="../script/jquery.portlet.pack.js"></script>
+    <style>
+    body {font-size: 13px;}
+    .highlight-content {color: red;}
+    </style>
+    <script>
+    $(function() {
+        $('#portlet-demo').portlet({
+            sortable: true,
+            singleView: true,
+            columns: [{
+                width: 350,
+                portlets: [{
+                    attrs: {
+                        id: 'feeds'
+                    },
+                    title: function() {
+                        var d = new Date();
+                        return '正常宽度';
+                    },
+                    icon: 'ui-icon-signal-diag',
+                    content: {
+                        //设置区域内容属性
+                        style: {
+                            height: 100
+                        },
+                        type: 'text',
+                        text: '这里是内容。。。。。'
+                    }
+                }]
+            }, {
+                width: 350,
+                portlets: [{
+                    attrs: {
+                        id: 'news'
+                    },
+                    title: function() {
+                        return '内容二';
+                    },
+                    icon: 'ui-icon-document',
+                    content: {
+                        style: {
+                            height: 100
+                        },
+                        type: 'text',
+                        text: '这里是内容。。。。。'
+                    }
+                }]
+            }]
+        });
+
+        $('#portlet-demo1').portlet({
+            sortable: true,
+            singleView: true,
+            columns: [{
+                width: 700,
+                portlets: [{
+                    attrs: {
+                        id: 'aaaa'
+                    },
+                    title: function() {
+                        var d = new Date();
+                        return '跨2列';
+                    },
+                    icon: 'ui-icon-signal-diag',
+                    content: {
+                        //设置区域内容属性
+                        style: {
+                            height: 100
+                        },
+                        afterShow: function() {
+                            var top = 0;
+                            var left = 0;
+                            var index = $(this).index('.ui-portlet');
+                            var topp = $('.ui-portlet').eq(index == 0 ? 0 : index - 1).find('.ui-portlet-column');
+                            $.each(topp, function() {
+                                if (top < $(this).height()) {
+                                    top = $(this).height();
+                                }
+                            });
+                            $(this).css('position', 'absolute').css({
+                                left: 7,
+                                top: top + 10
+                            });
+                        },
+                        type: 'text',
+                        text: '我要跨列...'
+                    }
+                }]
+            }]
+        });
+    });
+    </script>
+</head>
+<body>
+    <hr class='ui-widget-content' />
+    <div id='portlet-demo'></div>
+    <div id='portlet-demo1'></div>
+
+    <!-- 模板 -->
+    <div id="newsTemplate" style="display:none">
+        <ul>
+            <li><a href='http://www.kafeitu.me/'>http://www.kafeitu.me</a></li>
+            <li><a href='https://github.com/henryyan'>https://github.com/henryyan</a></li>
+        </ul>
+    </div>
+</body>
+</html>

+ 144 - 0
web/js/jquery-ui-portlet/demo/disable-drag.html

@@ -0,0 +1,144 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="utf-8" />
+    <title>jQuery UI Portlet</title>
+    <!-- jquery 1.4 and jquery ui 1.8 -->
+    <link rel="stylesheet" href="../lib/themes/1.8/start/jquery-ui-1.8.5.custom.css" />
+    <link rel="stylesheet" href="../css/jquery.portlet.css?v=1.1.3" />
+
+    <script src="../lib/jquery-1.4.4.min.js" type="text/javascript"></script>
+    <script src="../lib/jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>
+
+    <!--<link rel="stylesheet" href="../lib/themes/1.9/start/jquery-ui-1.9.1.custom.min.css" />-->
+    <!--<script src="../lib/jquery-1.8.3.js" type="text/javascript"></script>-->
+    <!--<script src="../lib/themes/jquery-ui-1.9.1.custom.min.js" type="text/javascript"></script>-->
+
+    <script src="../script/jquery.portlet.pack.js?v=1.1.3"></script>
+    <style>
+    body {font-size: 13px;}
+    .highlight-content {color: red;}
+    </style>
+    <script>
+    $(function() {
+        $('#portlet-demo').portlet({
+            sortable: false,
+            columns: [{
+                width: 350,
+                portlets: [{
+                    attrs: {
+                        id: 'feeds'
+                    },
+                    title: function() {
+                        var d = new Date();
+                        return 'Feeds(' + (d.getMonth() + 1) + '-' + d.getDay() + '日)';
+                    },
+                    content: {
+                        //设置区域内容属性
+                        style: {
+                            height: 100
+                        },
+                        type: 'text',
+                        text: '<ul><li>Feed item 1</li><li>Feed item 2</li></ul>',
+                        beforeShow: function(aa) {
+                            //alert('before show, content is: ' + aa);
+                        },
+                        afterShow: function() {
+                            //alert('after show');
+                        }
+                    },
+                    scripts: ['loaded-by-portlet.js']
+                }, {
+                    attrs: {
+                        id: 'news'
+                    },
+                    title: function() {
+                        return 'News' + '(高度:<span class="ui-state-error ui-corner-all">100</span>)';
+                    },
+                    beforeRefresh: function() {
+                        alert("before refresh");
+                    },
+                    afterRefresh: function(data) {
+                        //alert("after refresh: " + data);
+                    },
+                    content: {
+                        style: {
+                            height: 100
+                        },
+                        type: 'text',
+                        text: function() {
+                            return $('#newsTemplate').html();
+                            //return $('#newsTemplate ul');
+                        }
+                    }
+                }]
+            }, {
+                width: 500,
+                portlets: [{
+                    title: "Ajax",
+                    content: {
+                        style: {
+                            height: 150
+                        },
+                        type: 'ajax',
+                        url: 'ajax.html'
+                    }
+                }, {
+                    title: 'Ajax Json Datas',
+                    content: {
+                        style: {
+                            height: 150
+                        },
+                        type: 'ajax',
+                        dataType: 'json',
+                        url: 'ajax-fixed-height.json',
+                        formatter: function(o, pio, data) {
+                            var ct = "<ul>";
+                            $.each(data, function() {
+                                ct += "<li>" + this.text + "</li>";
+                            });
+                            return ct + "</ul>";
+                        }
+                    }
+                }]
+            }]
+        });
+
+        // 启用拖动
+        $('#enable').button({
+            icons: {
+                primary: 'ui-icon-check'
+            }
+        }).click(function() {
+            $('#portlet-demo').portlet('option', 'sortable', true);
+        });
+
+        // 禁用拖动
+        $('#disable').button({
+            icons: {
+                primary: 'ui-icon-closethick'
+            }
+        }).click(function() {
+            $('#portlet-demo').portlet('option', 'sortable', false);
+        });
+    });
+    </script>
+</head>
+<body>
+    <div>
+        <button id='enable'>启用拖动</button>
+        <button id='disable'>禁用拖动</button>
+        <code>$('#portlet-demo').portlet('option', 'sortable', true);</code>
+    </div>
+    <hr class='ui-widget-content' />
+    <div id='portlet-demo'></div>
+
+    <!-- 模板 -->
+    <div id="newsTemplate" style="display:none">
+        <ul>
+            <li><a href='http://www.kafeitu.me/'>http://www.kafeitu.me</a></li>
+            <li><a href='https://github.com/henryyan'>https://github.com/henryyan</a></li>
+        </ul>
+    </div>
+</body>
+</html>

+ 131 - 0
web/js/jquery-ui-portlet/demo/fixed-height.html

@@ -0,0 +1,131 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="utf-8" />
+    <title>jQuery UI Portlet</title>
+    <!-- jquery 1.4 and jquery ui 1.8 -->
+    <link rel="stylesheet" href="../lib/themes/1.8/start/jquery-ui-1.8.5.custom.css" />
+    <link rel="stylesheet" href="../css/jquery.portlet.css?v=1.1.3" />
+
+    <script src="../lib/jquery-1.4.4.min.js" type="text/javascript"></script>
+    <script src="../lib/jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>
+
+    <!--<link rel="stylesheet" href="../lib/themes/1.9/start/jquery-ui-1.9.1.custom.min.css" />-->
+    <!--<script src="../lib/jquery-1.8.3.js" type="text/javascript"></script>-->
+    <!--<script src="../lib/themes/jquery-ui-1.9.1.custom.min.js" type="text/javascript"></script>-->
+
+    <script src="../script/jquery.portlet.pack.js?v=1.1.3"></script>
+    <style>
+    body {font-size: 13px;}
+    .highlight-content {color: red;}
+    </style>
+    <script>
+    $(function() {
+        $('#portlet-demo').portlet({
+            sortable: true,
+            columns: [{
+                width: 350,
+                portlets: [{
+                    attrs: {
+                        id: 'feeds'
+                    },
+                    title: function() {
+                        var d = new Date();
+                        return 'Feeds(' + (d.getMonth() + 1) + '-' + d.getDay() + '日)' + '(高度:<span class="ui-state-error ui-corner-all">100</span>)';
+                    },
+                    content: {
+                        //设置区域内容属性
+                        style: {
+                            height: 100
+                        },
+                        type: 'text',
+                        text: '<ul><li>Feed item 1</li><li>Feed item 2</li></ul>',
+                        beforeShow: function(aa) {
+                            //alert('before show, content is: ' + aa);
+                        },
+                        afterShow: function() {
+                            //alert('after show');
+                        }
+                    },
+                    scripts: ['loaded-by-portlet.js']
+                }, {
+                    attrs: {
+                        id: 'news'
+                    },
+                    title: function() {
+                        return 'News' + '(高度:<span class="ui-state-error ui-corner-all">100</span>)';
+                    },
+                    beforeRefresh: function() {
+                        alert("before refresh");
+                    },
+                    afterRefresh: function(data) {
+                        //alert("after refresh: " + data);
+                    },
+                    content: {
+                        style: {
+                            height: 100
+                        },
+                        type: 'text',
+                        text: function() {
+                            return $('#newsTemplate').html();
+                            //return $('#newsTemplate ul');
+                        }
+                    }
+                }]
+            }, {
+                width: 500,
+                portlets: [{
+                    title: function() {
+                        return 'Ajax' + '(高度:<span class="ui-state-error ui-corner-all">150</span>)';
+                    },
+                    content: {
+                        style: {
+                            height: 150
+                        },
+                        type: 'ajax',
+                        url: 'ajax.html'
+                    }
+                }, {
+                    title: function() {
+                        return 'Ajax Json Datas' + '(高度:<span class="ui-state-error ui-corner-all">150</span>)';
+                    },
+                    content: {
+                        style: {
+                            height: 150
+                        },
+                        type: 'ajax',
+                        dataType: 'json',
+                        url: 'ajax-fixed-height.json',
+                        formatter: function(o, pio, data) {
+                            var ct = "<ul>";
+                            $.each(data, function() {
+                                ct += "<li>" + this.text + "</li>";
+                            });
+                            return ct + "</ul>";
+                        }
+                    }
+                }]
+            }]
+        });
+    });
+    </script>
+</head>
+<body>
+    <div class="ui-state-highlight ui-corner-all" style="padding: 0 .7em;">
+        <p>
+            <span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>
+            <strong>提示:</strong>设置content: {style: {height: 150}}固定高度
+        </p>
+    </div>
+    <hr class='ui-widget-content' />
+    <div id='portlet-demo'></div>
+
+    <!-- 模板 -->
+    <div id="newsTemplate" style="display:none">
+        <ul>
+            <li><a href='http://www.kafeitu.me/'>http://www.kafeitu.me</a></li>
+            <li><a href='https://github.com/henryyan'>https://github.com/henryyan</a></li>
+        </ul>
+    </div>
+</body>
+</html>

+ 130 - 0
web/js/jquery-ui-portlet/demo/icon.html

@@ -0,0 +1,130 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="utf-8" />
+    <title>jQuery UI Portlet</title>
+    <!-- jquery 1.4 and jquery ui 1.8 -->
+    <link rel="stylesheet" href="../lib/themes/1.8/start/jquery-ui-1.8.5.custom.css" />
+    <link rel="stylesheet" href="../css/jquery.portlet.css?v=1.1.3" />
+
+    <script src="../lib/jquery-1.4.4.min.js" type="text/javascript"></script>
+    <script src="../lib/jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>
+
+    <!--<link rel="stylesheet" href="../lib/themes/1.9/start/jquery-ui-1.9.1.custom.min.css" />-->
+    <!--<script src="../lib/jquery-1.8.3.js" type="text/javascript"></script>-->
+    <!--<script src="../lib/themes/jquery-ui-1.9.1.custom.min.js" type="text/javascript"></script>-->
+
+    <script src="../script/jquery.portlet.pack.js?v=1.1.3"></script>
+    <style>
+    body {font-size: 13px;}
+    .highlight-content {color: red;}
+    </style>
+    <script>
+    $(function() {
+        $('#portlet-demo').portlet({
+            sortable: true,
+            singleView: true,
+            columns: [{
+                width: 350,
+                portlets: [{
+                    attrs: {
+                        id: 'feeds'
+                    },
+                    title: function() {
+                        var d = new Date();
+                        return 'Feeds(' + (d.getMonth() + 1) + '-' + d.getDay() + '日)' + '(高度:<span class="ui-state-error ui-corner-all">100</span>)';
+                    },
+                    icon: 'ui-icon-signal-diag',
+                    content: {
+                        //设置区域内容属性
+                        style: {
+                            height: 100
+                        },
+                        type: 'text',
+                        text: '<ul><li>Feed item 1</li><li>Feed item 2</li></ul>',
+                        beforeShow: function(aa) {
+                            //alert('before show, content is: ' + aa);
+                        },
+                        afterShow: function() {
+                            //alert('after show');
+                        }
+                    },
+                    scripts: ['loaded-by-portlet.js']
+                }, {
+                    attrs: {
+                        id: 'news'
+                    },
+                    title: function() {
+                        return 'News' + '(高度:<span class="ui-state-error ui-corner-all">100</span>)';
+                    },
+                    icon: 'ui-icon-document',
+                    content: {
+                        style: {
+                            height: 100
+                        },
+                        type: 'text',
+                        text: function() {
+                            return $('#newsTemplate').html();
+                            //return $('#newsTemplate ul');
+                        }
+                    }
+                }]
+            }, {
+                width: 500,
+                portlets: [{
+                    title: function() {
+                        return 'Ajax' + '(高度:<span class="ui-state-error ui-corner-all">150</span>)';
+                    },
+                    icon: 'ui-icon-info',
+                    content: {
+                        style: {
+                            height: 150
+                        },
+                        type: 'ajax',
+                        url: 'ajax.html'
+                    }
+                }, {
+                    title: function() {
+                        return 'Ajax Json Datas' + '(高度:<span class="ui-state-error ui-corner-all">150</span>)';
+                    },
+                    icon: 'ui-icon-comment',
+                    content: {
+                        style: {
+                            height: 150
+                        },
+                        type: 'ajax',
+                        dataType: 'json',
+                        url: 'ajax-fixed-height.json',
+                        formatter: function(o, pio, data) {
+                            var ct = "<ul>";
+                            $.each(data, function() {
+                                ct += "<li>" + this.text + "</li>";
+                            });
+                            return ct + "</ul>";
+                        }
+                    }
+                }]
+            }]
+        });
+    });
+    </script>
+</head>
+<body>
+    <div class="ui-state-highlight ui-corner-all" style="padding: 0 .7em;">
+        <p>
+            <span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>
+            <strong>提示:</strong>和title属性同级,设置:<code>icon: 'ui-icon-document'</code>
+        </p>
+    </div>
+    <hr class='ui-widget-content' />
+    <div id='portlet-demo'></div>
+
+    <!-- 模板 -->
+    <div id="newsTemplate" style="display:none">
+        <ul>
+            <li><a href='http://www.kafeitu.me/'>http://www.kafeitu.me</a></li>
+            <li><a href='https://github.com/henryyan'>https://github.com/henryyan</a></li>
+        </ul>
+    </div>
+</body>
+</html>

+ 147 - 0
web/js/jquery-ui-portlet/demo/mix.html

@@ -0,0 +1,147 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="utf-8" />
+    <title>jQuery UI Portlet</title>
+    <!-- jquery 1.4 and jquery ui 1.8 -->
+    <link rel="stylesheet" href="../lib/themes/1.8/start/jquery-ui-1.8.5.custom.css" />
+    <link rel="stylesheet" href="../css/jquery.portlet.css?v=1.1.3" />
+
+    <script src="../lib/jquery-1.4.4.min.js" type="text/javascript"></script>
+    <script src="../lib/jquery-ui-1.8.14.custom.min.js" type="text/javascript"></script>
+
+    <!--<link rel="stylesheet" href="../lib/themes/1.9/start/jquery-ui-1.9.1.custom.min.css" />-->
+    <!--<script src="../lib/jquery-1.8.3.js" type="text/javascript"></script>-->
+    <!--<script src="../lib/themes/jquery-ui-1.9.1.custom.min.js" type="text/javascript"></script>-->
+
+    <script src="../script/jquery.portlet.pack.js?v=1.1.3"></script>
+    <style>
+    body {font-size: 13px;}
+    .highlight-content {color: red;}
+    </style>
+    <script>
+    $(function() {
+        $('#portlet-demo').portlet({
+            sortable: true,
+            create: function() {
+                //alert('created portlet.');
+            },
+            removeItem: function() {
+                alert('after remove');
+            },
+            columns: [{
+                width: 250,
+                portlets: [{
+                    attrs: {
+                        id: 'feeds'
+                    },
+                    title: function() {
+                        var d = new Date();
+                        return 'Feeds(' + (d.getMonth() + 1) + '-' + d.getDay() + '日)';
+                    },
+                    icon: 'ui-icon-signal-diag',
+                    content: {
+                        //设置区域内容属性
+                        style: {
+                            height: '200px'
+                        },
+                        type: 'text',
+                        text: '<ul><li>Feed item 1</li><li>Feed item 2</li></ul>',
+                        beforeShow: function(aa) {
+                            //alert('before show, content is: ' + aa);
+                        },
+                        afterShow: function() {
+                            //alert('after show');
+                        }
+                    },
+                    scripts: ['loaded-by-portlet.js']
+                }, {
+                    attrs: {
+                        id: 'news'
+                    },
+                    title: 'News',
+                    beforeRefresh: function() {
+                        alert("before refresh");
+                    },
+                    afterRefresh: function(data) {
+                        //alert("after refresh: " + data);
+                    },
+                    content: {
+                        style: {
+                            height: '200'
+                        },
+                        type: 'text',
+                        text: function() {
+                            return $('#newsTemplate').html();
+                            //return $('#newsTemplate ul');
+                        }
+                    }
+                }]
+            }, {
+                width: 200,
+                portlets: [{
+                    title: 'Shopping',
+                    content: {
+                        attrs: {
+                            'class': 'highlight-content'
+                        },
+                        type: 'text',
+                        text: 'Shopping contens<br/>Shopping contens<br/>Shopping contens<br/>Shopping contens<br/>'
+                    }
+                }]
+            }, {
+                width: 350,
+                portlets: [{
+                    title: 'Ajax',
+                    content: {
+                        type: 'ajax',
+                        url: 'ajax.html'
+                    }
+                }, {
+                    title: 'Ajax 错误',
+                    content: {
+                        type: 'ajax',
+                        url: 'noexsit.html',
+                        error: function() {
+                            $(this).append('<br/><br/>捕获到ajax错误');
+                            // alert('出错了');
+                        }
+                    }
+                }, {
+                    title: 'Ajax Json Datas',
+                    content: {
+                        type: 'ajax',
+                        dataType: 'json',
+                        url: 'ajax.json',
+                        formatter: function(o, pio, data) {
+                            var ct = "<ul>";
+                            $.each(data, function() {
+                                ct += "<li>" + this.text + "</li>";
+                            });
+                            return ct + "</ul>";
+                        }
+                    }
+                }]
+            }]
+        });
+    });
+    </script>
+</head>
+<body>
+    <div class="ui-state-highlight ui-corner-all" style="padding: 0 .7em;">
+        <p>
+            插件混合演示
+        </p>
+    </div>
+    <hr class='ui-widget-content' />
+    <div id='portlet-demo'></div>
+
+    <!-- 模板 -->
+    <div id="newsTemplate" style="display:none">
+        <ul>
+            <li><a href='http://www.kafeitu.me/'>http://www.kafeitu.me</a></li>
+            <li><a href='https://github.com/henryyan'>https://github.com/henryyan</a></li>
+        </ul>
+    </div>
+</body>
+</html>

+ 3 - 0
web/js/jquery-ui-portlet/demo/portlet.html

@@ -0,0 +1,3 @@
+<script>
+location.href = 'index.html';
+</script>

+ 0 - 0
web/js/jquery-ui-portlet/demo/single-view.html


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