View | Details | Raw Unified | Return to bug 32613
Collapse All | Expand All

(-)a/koha-tmpl/intranet-tmpl/lib/codemirror/highlight.css (+6 lines)
Line 0 Link Here
1
.cm-style1 {
2
  color: maroon !important;
3
}
4
.cm-style2 {
5
  color: limegreen !important;
6
}
(-)a/koha-tmpl/intranet-tmpl/lib/codemirror/highlight.js (+24 lines)
Line 0 Link Here
1
(function(mod) {
2
  if (typeof exports == "object" && typeof module == "object") // CommonJS
3
    mod(require("../../lib/codemirror"));
4
  else if (typeof define == "function" && define.amd) // AMD
5
    define(["../../lib/codemirror"], mod);
6
  else // Plain browser env
7
    mod(CodeMirror);
8
})(function(CodeMirror) {
9
  CodeMirror.defineOption("keyword", {}, function(cm, val, prev) {
10
    if (prev == CodeMirror.Init) prev = false;
11
    if (prev && !val)
12
      cm.removeOverlay("keyword");
13
    else if (!prev && val)
14
      cm.addOverlay({
15
        token: function(stream) {
16
          for (var key in cm.options.keyword) {
17
            if (stream.match(new RegExp(key))) return cm.options.keyword[key];
18
          }
19
          stream.next();
20
        },
21
        name: "keyword"
22
      });
23
  });
24
});
(-)a/koha-tmpl/intranet-tmpl/lib/codemirror/show-hint.css (+36 lines)
Line 0 Link Here
1
.CodeMirror-hints {
2
  position: absolute;
3
  z-index: 10;
4
  overflow: hidden;
5
  list-style: none;
6
7
  margin: 0;
8
  padding: 2px;
9
10
  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
11
  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
12
  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
13
  border-radius: 3px;
14
  border: 1px solid silver;
15
16
  background: white;
17
  font-size: 90%;
18
  font-family: monospace;
19
20
  max-height: 20em;
21
  overflow-y: auto;
22
}
23
24
.CodeMirror-hint {
25
  margin: 0;
26
  padding: 0 4px;
27
  border-radius: 2px;
28
  white-space: pre;
29
  color: black;
30
  cursor: pointer;
31
}
32
33
li.CodeMirror-hint-active {
34
  background: #08f;
35
  color: white;
36
}
(-)a/koha-tmpl/intranet-tmpl/lib/codemirror/show-hint.js (+479 lines)
Line 0 Link Here
1
// CodeMirror, copyright (c) by Marijn Haverbeke and others
2
// Distributed under an MIT license: https://codemirror.net/LICENSE
3
4
(function(mod) {
5
  if (typeof exports == "object" && typeof module == "object") // CommonJS
6
    mod(require("../../lib/codemirror"));
7
  else if (typeof define == "function" && define.amd) // AMD
8
    define(["../../lib/codemirror"], mod);
9
  else // Plain browser env
10
    mod(CodeMirror);
11
})(function(CodeMirror) {
12
  "use strict";
13
14
  var HINT_ELEMENT_CLASS        = "CodeMirror-hint";
15
  var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
16
17
  // This is the old interface, kept around for now to stay
18
  // backwards-compatible.
19
  CodeMirror.showHint = function(cm, getHints, options) {
20
    if (!getHints) return cm.showHint(options);
21
    if (options && options.async) getHints.async = true;
22
    var newOpts = {hint: getHints};
23
    if (options) for (var prop in options) newOpts[prop] = options[prop];
24
    return cm.showHint(newOpts);
25
  };
26
27
  CodeMirror.defineExtension("showHint", function(options) {
28
    options = parseOptions(this, this.getCursor("start"), options);
29
    var selections = this.listSelections()
30
    if (selections.length > 1) return;
31
    // By default, don't allow completion when something is selected.
32
    // A hint function can have a `supportsSelection` property to
33
    // indicate that it can handle selections.
34
    if (this.somethingSelected()) {
35
      if (!options.hint.supportsSelection) return;
36
      // Don't try with cross-line selections
37
      for (var i = 0; i < selections.length; i++)
38
        if (selections[i].head.line != selections[i].anchor.line) return;
39
    }
40
41
    if (this.state.completionActive) this.state.completionActive.close();
42
    var completion = this.state.completionActive = new Completion(this, options);
43
    if (!completion.options.hint) return;
44
45
    CodeMirror.signal(this, "startCompletion", this);
46
    completion.update(true);
47
  });
48
49
  CodeMirror.defineExtension("closeHint", function() {
50
    if (this.state.completionActive) this.state.completionActive.close()
51
  })
52
53
  function Completion(cm, options) {
54
    this.cm = cm;
55
    this.options = options;
56
    this.widget = null;
57
    this.debounce = 0;
58
    this.tick = 0;
59
    this.startPos = this.cm.getCursor("start");
60
    this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
61
62
    var self = this;
63
    cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
64
  }
65
66
  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
67
    return setTimeout(fn, 1000/60);
68
  };
69
  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
70
71
  Completion.prototype = {
72
    close: function() {
73
      if (!this.active()) return;
74
      this.cm.state.completionActive = null;
75
      this.tick = null;
76
      this.cm.off("cursorActivity", this.activityFunc);
77
78
      if (this.widget && this.data) CodeMirror.signal(this.data, "close");
79
      if (this.widget) this.widget.close();
80
      CodeMirror.signal(this.cm, "endCompletion", this.cm);
81
    },
82
83
    active: function() {
84
      return this.cm.state.completionActive == this;
85
    },
86
87
    pick: function(data, i) {
88
      var completion = data.list[i], self = this;
89
      this.cm.operation(function() {
90
        if (completion.hint)
91
          completion.hint(self.cm, data, completion);
92
        else
93
          self.cm.replaceRange(getText(completion), completion.from || data.from,
94
                               completion.to || data.to, "complete");
95
        CodeMirror.signal(data, "pick", completion);
96
        self.cm.scrollIntoView();
97
      })
98
      this.close();
99
    },
100
101
    cursorActivity: function() {
102
      if (this.debounce) {
103
        cancelAnimationFrame(this.debounce);
104
        this.debounce = 0;
105
      }
106
107
      var identStart = this.startPos;
108
      if(this.data) {
109
        identStart = this.data.from;
110
      }
111
112
      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
113
      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
114
          pos.ch < identStart.ch || this.cm.somethingSelected() ||
115
          (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
116
        this.close();
117
      } else {
118
        var self = this;
119
        this.debounce = requestAnimationFrame(function() {self.update();});
120
        if (this.widget) this.widget.disable();
121
      }
122
    },
123
124
    update: function(first) {
125
      if (this.tick == null) return
126
      var self = this, myTick = ++this.tick
127
      fetchHints(this.options.hint, this.cm, this.options, function(data) {
128
        if (self.tick == myTick) self.finishUpdate(data, first)
129
      })
130
    },
131
132
    finishUpdate: function(data, first) {
133
      if (this.data) CodeMirror.signal(this.data, "update");
134
135
      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
136
      if (this.widget) this.widget.close();
137
138
      this.data = data;
139
140
      if (data && data.list.length) {
141
        if (picked && data.list.length == 1) {
142
          this.pick(data, 0);
143
        } else {
144
          this.widget = new Widget(this, data);
145
          CodeMirror.signal(data, "shown");
146
        }
147
      }
148
    }
149
  };
150
151
  function parseOptions(cm, pos, options) {
152
    var editor = cm.options.hintOptions;
153
    var out = {};
154
    for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
155
    if (editor) for (var prop in editor)
156
      if (editor[prop] !== undefined) out[prop] = editor[prop];
157
    if (options) for (var prop in options)
158
      if (options[prop] !== undefined) out[prop] = options[prop];
159
    if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
160
    return out;
161
  }
162
163
  function getText(completion) {
164
    if (typeof completion == "string") return completion;
165
    else return completion.text;
166
  }
167
168
  function buildKeyMap(completion, handle) {
169
    var baseMap = {
170
      Up: function() {handle.moveFocus(-1);},
171
      Down: function() {handle.moveFocus(1);},
172
      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
173
      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
174
      Home: function() {handle.setFocus(0);},
175
      End: function() {handle.setFocus(handle.length - 1);},
176
      Enter: handle.pick,
177
      Tab: handle.pick,
178
      Esc: handle.close
179
    };
180
181
    var mac = /Mac/.test(navigator.platform);
182
183
    if (mac) {
184
      baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);};
185
      baseMap["Ctrl-N"] = function() {handle.moveFocus(1);};
186
    }
187
188
    var custom = completion.options.customKeys;
189
    var ourMap = custom ? {} : baseMap;
190
    function addBinding(key, val) {
191
      var bound;
192
      if (typeof val != "string")
193
        bound = function(cm) { return val(cm, handle); };
194
      // This mechanism is deprecated
195
      else if (baseMap.hasOwnProperty(val))
196
        bound = baseMap[val];
197
      else
198
        bound = val;
199
      ourMap[key] = bound;
200
    }
201
    if (custom)
202
      for (var key in custom) if (custom.hasOwnProperty(key))
203
        addBinding(key, custom[key]);
204
    var extra = completion.options.extraKeys;
205
    if (extra)
206
      for (var key in extra) if (extra.hasOwnProperty(key))
207
        addBinding(key, extra[key]);
208
    return ourMap;
209
  }
210
211
  function getHintElement(hintsElement, el) {
212
    while (el && el != hintsElement) {
213
      if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
214
      el = el.parentNode;
215
    }
216
  }
217
218
  function Widget(completion, data) {
219
    this.completion = completion;
220
    this.data = data;
221
    this.picked = false;
222
    var widget = this, cm = completion.cm;
223
    var ownerDocument = cm.getInputField().ownerDocument;
224
    var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;
225
226
    var hints = this.hints = ownerDocument.createElement("ul");
227
    var theme = completion.cm.options.theme;
228
    hints.className = "CodeMirror-hints " + theme;
229
    this.selectedHint = data.selectedHint || 0;
230
231
    var completions = data.list;
232
    for (var i = 0; i < completions.length; ++i) {
233
      var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i];
234
      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
235
      if (cur.className != null) className = cur.className + " " + className;
236
      elt.className = className;
237
      if (cur.render) cur.render(elt, data, cur);
238
      else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)));
239
      elt.hintId = i;
240
    }
241
242
    var container = completion.options.container || ownerDocument.body;
243
    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
244
    var left = pos.left, top = pos.bottom, below = true;
245
    var offsetLeft = 0, offsetTop = 0;
246
    if (container !== ownerDocument.body) {
247
      // We offset the cursor position because left and top are relative to the offsetParent's top left corner.
248
      var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1;
249
      var offsetParent = isContainerPositioned ? container : container.offsetParent;
250
      var offsetParentPosition = offsetParent.getBoundingClientRect();
251
      var bodyPosition = ownerDocument.body.getBoundingClientRect();
252
      offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft);
253
      offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop);
254
    }
255
    hints.style.left = (left - offsetLeft) + "px";
256
    hints.style.top = (top - offsetTop) + "px";
257
258
    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
259
    var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth);
260
    var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight);
261
    container.appendChild(hints);
262
    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
263
    var scrolls = hints.scrollHeight > hints.clientHeight + 1
264
    var startScroll = cm.getScrollInfo();
265
266
    if (overlapY > 0) {
267
      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
268
      if (curTop - height > 0) { // Fits above cursor
269
        hints.style.top = (top = pos.top - height - offsetTop) + "px";
270
        below = false;
271
      } else if (height > winH) {
272
        hints.style.height = (winH - 5) + "px";
273
        hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px";
274
        var cursor = cm.getCursor();
275
        if (data.from.ch != cursor.ch) {
276
          pos = cm.cursorCoords(cursor);
277
          hints.style.left = (left = pos.left - offsetLeft) + "px";
278
          box = hints.getBoundingClientRect();
279
        }
280
      }
281
    }
282
    var overlapX = box.right - winW;
283
    if (overlapX > 0) {
284
      if (box.right - box.left > winW) {
285
        hints.style.width = (winW - 5) + "px";
286
        overlapX -= (box.right - box.left) - winW;
287
      }
288
      hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px";
289
    }
290
    if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
291
      node.style.paddingRight = cm.display.nativeBarWidth + "px"
292
293
    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
294
      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
295
      setFocus: function(n) { widget.changeActive(n); },
296
      menuSize: function() { return widget.screenAmount(); },
297
      length: completions.length,
298
      close: function() { completion.close(); },
299
      pick: function() { widget.pick(); },
300
      data: data
301
    }));
302
303
    if (completion.options.closeOnUnfocus) {
304
      var closingOnBlur;
305
      cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
306
      cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
307
    }
308
309
    cm.on("scroll", this.onScroll = function() {
310
      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
311
      var newTop = top + startScroll.top - curScroll.top;
312
      var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop);
313
      if (!below) point += hints.offsetHeight;
314
      if (point <= editor.top || point >= editor.bottom) return completion.close();
315
      hints.style.top = newTop + "px";
316
      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
317
    });
318
319
    CodeMirror.on(hints, "dblclick", function(e) {
320
      var t = getHintElement(hints, e.target || e.srcElement);
321
      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
322
    });
323
324
    CodeMirror.on(hints, "click", function(e) {
325
      var t = getHintElement(hints, e.target || e.srcElement);
326
      if (t && t.hintId != null) {
327
        widget.changeActive(t.hintId);
328
        if (completion.options.completeOnSingleClick) widget.pick();
329
      }
330
    });
331
332
    CodeMirror.on(hints, "mousedown", function() {
333
      setTimeout(function(){cm.focus();}, 20);
334
    });
335
    this.scrollToActive()
336
337
    CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
338
    return true;
339
  }
340
341
  Widget.prototype = {
342
    close: function() {
343
      if (this.completion.widget != this) return;
344
      this.completion.widget = null;
345
      this.hints.parentNode.removeChild(this.hints);
346
      this.completion.cm.removeKeyMap(this.keyMap);
347
348
      var cm = this.completion.cm;
349
      if (this.completion.options.closeOnUnfocus) {
350
        cm.off("blur", this.onBlur);
351
        cm.off("focus", this.onFocus);
352
      }
353
      cm.off("scroll", this.onScroll);
354
    },
355
356
    disable: function() {
357
      this.completion.cm.removeKeyMap(this.keyMap);
358
      var widget = this;
359
      this.keyMap = {Enter: function() { widget.picked = true; }};
360
      this.completion.cm.addKeyMap(this.keyMap);
361
    },
362
363
    pick: function() {
364
      this.completion.pick(this.data, this.selectedHint);
365
    },
366
367
    changeActive: function(i, avoidWrap) {
368
      if (i >= this.data.list.length)
369
        i = avoidWrap ? this.data.list.length - 1 : 0;
370
      else if (i < 0)
371
        i = avoidWrap ? 0  : this.data.list.length - 1;
372
      if (this.selectedHint == i) return;
373
      var node = this.hints.childNodes[this.selectedHint];
374
      if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
375
      node = this.hints.childNodes[this.selectedHint = i];
376
      node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
377
      this.scrollToActive()
378
      CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
379
    },
380
381
    scrollToActive: function() {
382
      var margin = this.completion.options.scrollMargin || 0;
383
      var node1 = this.hints.childNodes[Math.max(0, this.selectedHint - margin)];
384
      var node2 = this.hints.childNodes[Math.min(this.data.list.length - 1, this.selectedHint + margin)];
385
      var firstNode = this.hints.firstChild;
386
      if (node1.offsetTop < this.hints.scrollTop)
387
        this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop;
388
      else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
389
        this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop;
390
    },
391
392
    screenAmount: function() {
393
      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
394
    }
395
  };
396
397
  function applicableHelpers(cm, helpers) {
398
    if (!cm.somethingSelected()) return helpers
399
    var result = []
400
    for (var i = 0; i < helpers.length; i++)
401
      if (helpers[i].supportsSelection) result.push(helpers[i])
402
    return result
403
  }
404
405
  function fetchHints(hint, cm, options, callback) {
406
    if (hint.async) {
407
      hint(cm, callback, options)
408
    } else {
409
      var result = hint(cm, options)
410
      if (result && result.then) result.then(callback)
411
      else callback(result)
412
    }
413
  }
414
415
  function resolveAutoHints(cm, pos) {
416
    var helpers = cm.getHelpers(pos, "hint"), words
417
    if (helpers.length) {
418
      var resolved = function(cm, callback, options) {
419
        var app = applicableHelpers(cm, helpers);
420
        function run(i) {
421
          if (i == app.length) return callback(null)
422
          fetchHints(app[i], cm, options, function(result) {
423
            if (result && result.list.length > 0) callback(result)
424
            else run(i + 1)
425
          })
426
        }
427
        run(0)
428
      }
429
      resolved.async = true
430
      resolved.supportsSelection = true
431
      return resolved
432
    } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
433
      return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
434
    } else if (CodeMirror.hint.anyword) {
435
      return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
436
    } else {
437
      return function() {}
438
    }
439
  }
440
441
  CodeMirror.registerHelper("hint", "auto", {
442
    resolve: resolveAutoHints
443
  });
444
445
  CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
446
    var cur = cm.getCursor(), token = cm.getTokenAt(cur)
447
    var term, from = CodeMirror.Pos(cur.line, token.start), to = cur
448
    if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) {
449
      term = token.string.substr(0, cur.ch - token.start)
450
    } else {
451
      term = ""
452
      from = cur
453
    }
454
    var found = [];
455
    for (var i = 0; i < options.words.length; i++) {
456
      var word = options.words[i];
457
      if (word.slice(0, term.length) == term)
458
        found.push(word);
459
    }
460
461
    if (found.length) return {list: found, from: from, to: to};
462
  });
463
464
  CodeMirror.commands.autocomplete = CodeMirror.showHint;
465
466
  var defaultOptions = {
467
    hint: CodeMirror.hint.auto,
468
    completeSingle: true,
469
    alignWithWord: true,
470
    closeCharacters: /[\s()\[\]{};:>,]/,
471
    closeOnUnfocus: true,
472
    completeOnSingleClick: true,
473
    container: null,
474
    customKeys: null,
475
    extraKeys: null
476
  };
477
478
  CodeMirror.defineOption("hintOptions", null);
479
});
(-)a/koha-tmpl/intranet-tmpl/lib/codemirror/sql-hint.js (+304 lines)
Line 0 Link Here
1
// CodeMirror, copyright (c) by Marijn Haverbeke and others
2
// Distributed under an MIT license: https://codemirror.net/LICENSE
3
4
(function(mod) {
5
  if (typeof exports == "object" && typeof module == "object") // CommonJS
6
    mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
7
  else if (typeof define == "function" && define.amd) // AMD
8
    define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
9
  else // Plain browser env
10
    mod(CodeMirror);
11
})(function(CodeMirror) {
12
  "use strict";
13
14
  var tables;
15
  var defaultTable;
16
  var keywords;
17
  var identifierQuote;
18
  var CONS = {
19
    QUERY_DIV: ";",
20
    ALIAS_KEYWORD: "AS"
21
  };
22
  var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
23
24
  function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
25
26
  function getKeywords(editor) {
27
    var mode = editor.doc.modeOption;
28
    if (mode === "sql") mode = "text/x-sql";
29
    return CodeMirror.resolveMode(mode).keywords;
30
  }
31
32
  function getIdentifierQuote(editor) {
33
    var mode = editor.doc.modeOption;
34
    if (mode === "sql") mode = "text/x-sql";
35
    return CodeMirror.resolveMode(mode).identifierQuote || "`";
36
  }
37
38
  function getText(item) {
39
    return typeof item == "string" ? item : item.text;
40
  }
41
42
  function wrapTable(name, value) {
43
    if (isArray(value)) value = {columns: value}
44
    if (!value.text) value.text = name
45
    return value
46
  }
47
48
  function parseTables(input) {
49
    var result = {}
50
    if (isArray(input)) {
51
      for (var i = input.length - 1; i >= 0; i--) {
52
        var item = input[i]
53
        result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
54
      }
55
    } else if (input) {
56
      for (var name in input)
57
        result[name.toUpperCase()] = wrapTable(name, input[name])
58
    }
59
    return result
60
  }
61
62
  function getTable(name) {
63
    return tables[name.toUpperCase()]
64
  }
65
66
  function shallowClone(object) {
67
    var result = {};
68
    for (var key in object) if (object.hasOwnProperty(key))
69
      result[key] = object[key];
70
    return result;
71
  }
72
73
  function match(string, word) {
74
    var len = string.length;
75
    var sub = getText(word).substr(0, len);
76
    return string.toUpperCase() === sub.toUpperCase();
77
  }
78
79
  function addMatches(result, search, wordlist, formatter) {
80
    if (isArray(wordlist)) {
81
      for (var i = 0; i < wordlist.length; i++)
82
        if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
83
    } else {
84
      for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
85
        var val = wordlist[word]
86
        if (!val || val === true)
87
          val = word
88
        else
89
          val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
90
        if (match(search, val)) result.push(formatter(val))
91
      }
92
    }
93
  }
94
95
  function cleanName(name) {
96
    // Get rid name from identifierQuote and preceding dot(.)
97
    if (name.charAt(0) == ".") {
98
      name = name.substr(1);
99
    }
100
    // replace doublicated identifierQuotes with single identifierQuotes
101
    // and remove single identifierQuotes
102
    var nameParts = name.split(identifierQuote+identifierQuote);
103
    for (var i = 0; i < nameParts.length; i++)
104
      nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
105
    return nameParts.join(identifierQuote);
106
  }
107
108
  function insertIdentifierQuotes(name) {
109
    var nameParts = getText(name).split(".");
110
    for (var i = 0; i < nameParts.length; i++)
111
      nameParts[i] = identifierQuote +
112
        // doublicate identifierQuotes
113
        nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
114
        identifierQuote;
115
    var escaped = nameParts.join(".");
116
    if (typeof name == "string") return escaped;
117
    name = shallowClone(name);
118
    name.text = escaped;
119
    return name;
120
  }
121
122
  function nameCompletion(cur, token, result, editor) {
123
    // Try to complete table, column names and return start position of completion
124
    var useIdentifierQuotes = false;
125
    var nameParts = [];
126
    var start = token.start;
127
    var cont = true;
128
    while (cont) {
129
      cont = (token.string.charAt(0) == ".");
130
      useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
131
132
      start = token.start;
133
      nameParts.unshift(cleanName(token.string));
134
135
      token = editor.getTokenAt(Pos(cur.line, token.start));
136
      if (token.string == ".") {
137
        cont = true;
138
        token = editor.getTokenAt(Pos(cur.line, token.start));
139
      }
140
    }
141
142
    // Try to complete table names
143
    var string = nameParts.join(".");
144
    addMatches(result, string, tables, function(w) {
145
      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
146
    });
147
148
    // Try to complete columns from defaultTable
149
    addMatches(result, string, defaultTable, function(w) {
150
      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
151
    });
152
153
    // Try to complete columns
154
    string = nameParts.pop();
155
    var table = nameParts.join(".");
156
157
    var alias = false;
158
    var aliasTable = table;
159
    // Check if table is available. If not, find table by Alias
160
    if (!getTable(table)) {
161
      var oldTable = table;
162
      table = findTableByAlias(table, editor);
163
      if (table !== oldTable) alias = true;
164
    }
165
166
    var columns = getTable(table);
167
    if (columns && columns.columns)
168
      columns = columns.columns;
169
170
    if (columns) {
171
      addMatches(result, string, columns, function(w) {
172
        var tableInsert = table;
173
        if (alias == true) tableInsert = aliasTable;
174
        if (typeof w == "string") {
175
          w = tableInsert + "." + w;
176
        } else {
177
          w = shallowClone(w);
178
          w.text = tableInsert + "." + w.text;
179
        }
180
        return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
181
      });
182
    }
183
184
    return start;
185
  }
186
187
  function eachWord(lineText, f) {
188
    var words = lineText.split(/\s+/)
189
    for (var i = 0; i < words.length; i++)
190
      if (words[i]) f(words[i].replace(/[,;]/g, ''))
191
  }
192
193
  function findTableByAlias(alias, editor) {
194
    var doc = editor.doc;
195
    var fullQuery = doc.getValue();
196
    var aliasUpperCase = alias.toUpperCase();
197
    var previousWord = "";
198
    var table = "";
199
    var separator = [];
200
    var validRange = {
201
      start: Pos(0, 0),
202
      end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
203
    };
204
205
    //add separator
206
    var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
207
    while(indexOfSeparator != -1) {
208
      separator.push(doc.posFromIndex(indexOfSeparator));
209
      indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
210
    }
211
    separator.unshift(Pos(0, 0));
212
    separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
213
214
    //find valid range
215
    var prevItem = null;
216
    var current = editor.getCursor()
217
    for (var i = 0; i < separator.length; i++) {
218
      if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
219
        validRange = {start: prevItem, end: separator[i]};
220
        break;
221
      }
222
      prevItem = separator[i];
223
    }
224
225
    if (validRange.start) {
226
      var query = doc.getRange(validRange.start, validRange.end, false);
227
228
      for (var i = 0; i < query.length; i++) {
229
        var lineText = query[i];
230
        eachWord(lineText, function(word) {
231
          var wordUpperCase = word.toUpperCase();
232
          if (wordUpperCase === aliasUpperCase && getTable(previousWord))
233
            table = previousWord;
234
          if (wordUpperCase !== CONS.ALIAS_KEYWORD)
235
            previousWord = word;
236
        });
237
        if (table) break;
238
      }
239
    }
240
    return table;
241
  }
242
243
  CodeMirror.registerHelper("hint", "sql", function(editor, options) {
244
    tables = parseTables(options && options.tables)
245
    var defaultTableName = options && options.defaultTable;
246
    var disableKeywords = options && options.disableKeywords;
247
    defaultTable = defaultTableName && getTable(defaultTableName);
248
    keywords = getKeywords(editor);
249
    identifierQuote = getIdentifierQuote(editor);
250
251
    if (defaultTableName && !defaultTable)
252
      defaultTable = findTableByAlias(defaultTableName, editor);
253
254
    defaultTable = defaultTable || [];
255
256
    if (defaultTable.columns)
257
      defaultTable = defaultTable.columns;
258
259
    var cur = editor.getCursor();
260
    var result = [];
261
    var token = editor.getTokenAt(cur), start, end, search;
262
    if (token.end > cur.ch) {
263
      token.end = cur.ch;
264
      token.string = token.string.slice(0, cur.ch - token.start);
265
    }
266
267
    if (token.string.match(/^[.`"'\w@][\w$#]*$/g)) {
268
      search = token.string;
269
      start = token.start;
270
      end = token.end;
271
    } else {
272
      start = end = cur.ch;
273
      search = "";
274
    }
275
    if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
276
      start = nameCompletion(cur, token, result, editor);
277
    } else {
278
      var objectOrClass = function(w, className) {
279
        if (typeof w === "object") {
280
          w.className = className;
281
        } else {
282
          w = { text: w, className: className };
283
        }
284
        return w;
285
      };
286
    addMatches(result, search, defaultTable, function(w) {
287
        return objectOrClass(w, "CodeMirror-hint-table CodeMirror-hint-default-table");
288
    });
289
    addMatches(
290
        result,
291
        search,
292
        tables, function(w) {
293
          return objectOrClass(w, "CodeMirror-hint-table");
294
        }
295
    );
296
    if (!disableKeywords)
297
      addMatches(result, search, keywords, function(w) {
298
          return objectOrClass(w.toUpperCase(), "CodeMirror-hint-keyword");
299
      });
300
  }
301
302
    return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
303
  });
304
});
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt (-39 / +83 lines)
Lines 7-12 Link Here
7
[% USE JSON.Escape %]
7
[% USE JSON.Escape %]
8
[% PROCESS 'i18n.inc' %]
8
[% PROCESS 'i18n.inc' %]
9
[% SET footerjs = 1 %]
9
[% SET footerjs = 1 %]
10
[% USE To %]
10
11
11
[%- BLOCK area_name -%]
12
[%- BLOCK area_name -%]
12
    [%- SWITCH area -%]
13
    [%- SWITCH area -%]
Lines 1259-1265 Link Here
1259
                            <legend>SQL:</legend>
1260
                            <legend>SQL:</legend>
1260
                            <div style="margin:1em;">
1261
                            <div style="margin:1em;">
1261
                                [% PROCESS insert_runtime_parameter  %]
1262
                                [% PROCESS insert_runtime_parameter  %]
1262
                                <textarea id="sql" name="sql" class="required" required="required" cols="50" rows="10">[% sql | html %]</textarea>
1263
                                <textarea id="sql" name="sql" class="required" required="required" cols="50" rows="10" >[% sql | html %]</textarea>
1263
                                <span class="required">Required</span>
1264
                                <span class="required">Required</span>
1264
                            </div>
1265
                            </div>
1265
                        </fieldset>
1266
                        </fieldset>
Lines 1495-1501 Link Here
1495
    [% Asset.js( "lib/codemirror/codemirror.min.js" ) | $raw %]
1496
    [% Asset.js( "lib/codemirror/codemirror.min.js" ) | $raw %]
1496
    [% Asset.js( "lib/codemirror/overlay.min.js" ) | $raw %]
1497
    [% Asset.js( "lib/codemirror/overlay.min.js" ) | $raw %]
1497
    [% Asset.js( "lib/codemirror/sql.min.js" ) | $raw %]
1498
    [% Asset.js( "lib/codemirror/sql.min.js" ) | $raw %]
1499
    [% Asset.js( "lib/codemirror/show-hint.js" ) | $raw %]
1500
    [% Asset.css("lib/codemirror/show-hint.css") | $raw %]
1501
    [% Asset.js( "lib/codemirror/sql-hint.js" ) | $raw %]
1502
    [% Asset.js( "lib/codemirror/highlight.js" ) | $raw %]
1503
    [% Asset.css("lib/codemirror/highlight.css") | $raw %]
1498
    [% Asset.js( "js/mana.js" ) | $raw %]
1504
    [% Asset.js( "js/mana.js" ) | $raw %]
1505
1499
    <script>
1506
    <script>
1500
1507
1501
        function hide_bar_element() {
1508
        function hide_bar_element() {
Lines 1526-1563 Link Here
1526
            }
1533
            }
1527
        }
1534
        }
1528
1535
1529
        /* overlay a syntax-highlighting definition on top of the existing sql one */
1530
        CodeMirror.defineMode("sqlPlaceholders", function(config, parserConfig) {
1531
            var sqlPlaceholdersOverlay = {
1532
                token: function(stream, state) {
1533
                    var ch;
1534
1535
                    if (stream.match("<<")) {
1536
                        while ((ch = stream.next()) != null) {
1537
                            if (ch == ">" && stream.next() == ">") {
1538
                                stream.eat(">");
1539
                                return "sqlParams";
1540
                            }
1541
                        }
1542
                    }
1543
1544
                    if (stream.match("[[")) {
1545
                        while ((ch = stream.next()) != null) {
1546
                            if (ch == "]" && stream.next() == "]") {
1547
                                stream.eat("]");
1548
                                return "columnPlaceholder";
1549
                            }
1550
                        }
1551
                    }
1552
1553
                    else if (stream.next() != null) {
1554
                        return null;
1555
                    }
1556
                }
1557
            };
1558
            return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "text/x-sql"), sqlPlaceholdersOverlay);
1559
        });
1560
1561
        var MSG_CONFIRM_DELETE = _("Are you sure you want to delete this report? This cannot be undone.");
1536
        var MSG_CONFIRM_DELETE = _("Are you sure you want to delete this report? This cannot be undone.");
1562
        var group_subgroups = {};
1537
        var group_subgroups = {};
1563
        [% FOREACH group IN groups_with_subgroups %]
1538
        [% FOREACH group IN groups_with_subgroups %]
Lines 1574-1585 Link Here
1574
1549
1575
            var editor = CodeMirror.fromTextArea(sql, {
1550
            var editor = CodeMirror.fromTextArea(sql, {
1576
                lineNumbers: true,
1551
                lineNumbers: true,
1577
                mode: "sqlPlaceholders", /* text/x-sql plus custom sqlPlaceholders configuration */
1552
                mode: "text/x-sql",
1578
                lineWrapping: true,
1553
                lineWrapping: true,
1579
                smartIndent: false
1554
                smartIndent: false,
1555
                keyword: { // Custom highlighting for items bounded in [[ ]] or << >>.
1556
                    "\\[\\[(.*?)\\]\\]":"style1",
1557
                    "<<(.*?)>>":"style1"
1558
                },
1559
                extraKeys: {"Tab": "autocomplete"}, //enable tab to accept auto-complete
1560
                hint: CodeMirror.hint.sql,
1580
            });
1561
            });
1562
            var ExcludedTriggerKeys = { //key-code combinations of keys that will not fire the auto-complete script
1563
                "8": "backspace",
1564
                "9": "tab",
1565
                "13": "enter",
1566
                "16": "shift",
1567
                "17": "ctrl",
1568
                "18": "alt",
1569
                "19": "pause",
1570
                "20": "capslock",
1571
                "27": "escape",
1572
                "33": "pageup",
1573
                "32": "spacebar",
1574
                "34": "pagedown",
1575
                "35": "end",
1576
                "36": "home",
1577
                "37": "left",
1578
                "38": "up",
1579
                "39": "right",
1580
                "40": "down",
1581
                "45": "insert",
1582
                "46": "delete",
1583
                "91": "left window key",
1584
                "92": "right window key",
1585
                "93": "select",
1586
                "106": "asterisk",
1587
                "107": "add",
1588
                "109": "subtract",
1589
                "110": "decimal point",
1590
                "111": "divide",
1591
                "112": "f1",
1592
                "113": "f2",
1593
                "114": "f3",
1594
                "115": "f4",
1595
                "116": "f5",
1596
                "117": "f6",
1597
                "118": "f7",
1598
                "119": "f8",
1599
                "120": "f9",
1600
                "121": "f10",
1601
                "122": "f11",
1602
                "123": "f12",
1603
                "144": "numlock",
1604
                "145": "scrolllock",
1605
                "186": "semicolon",
1606
                "187": "equalsign",
1607
                "188": "comma",
1608
                "189": "dash",
1609
                "190": "period",
1610
                "191": "slash",
1611
                "192": "graveaccent",
1612
                "220": "backslash",
1613
                "222": "quote"
1614
            }
1615
            //Trigger auto-complete on all keys not in dictionary of exclusions above.
1616
            editor.on("keyup", function(cm, e) {
1617
                if (ExcludedTriggerKeys[e.keyCode] == undefined) {
1618
                         CodeMirror.commands.autocomplete(editor, null, { completeSingle: false });
1619
                }
1620
            })
1581
1621
1582
            // https://stackoverflow.com/questions/2086287/how-to-clear-jquery-validation-error-messages#answer-16025232
1622
           // https://stackoverflow.com/questions/2086287/how-to-clear-jquery-validation-error-messages#answer-16025232
1583
            function clearValidation( formElement ){
1623
            function clearValidation( formElement ){
1584
                // formElement should be a jQuery object
1624
                // formElement should be a jQuery object
1585
                var validator = formElement.validate();
1625
                var validator = formElement.validate();
Lines 1594-1604 Link Here
1594
        [% END %]
1634
        [% END %]
1595
1635
1596
        [% IF ( showsql ) %]
1636
        [% IF ( showsql ) %]
1637
1597
            var editor = CodeMirror.fromTextArea(sql, {
1638
            var editor = CodeMirror.fromTextArea(sql, {
1598
                lineNumbers: false,
1639
                lineNumbers: false,
1599
                mode: "sqlPlaceholders", /* text/x-sql plus custom sqlPlaceholders configuration */
1640
                mode: "text/x-sql",
1600
                lineWrapping: true,
1641
                lineWrapping: true,
1601
                readOnly: true
1642
                readOnly: true,
1643
                keyword: { // Custom highlighting for items bounded in [[ ]] or << >>.
1644
                    "\\[\\[(.*?)\\]\\]":"style2",
1645
                    "<<(.*?)>>":"style1"
1646
               },
1602
            });
1647
            });
1603
        [% END %]
1648
        [% END %]
1604
1649
1605
- 

Return to bug 32613