--- a/playground/index.html Tue Oct 22 15:53:58 2013 -0400
+++ b/playground/index.html Wed Oct 23 16:05:21 2013 -0400
@@ -25,6 +25,8 @@
<!-- CodeMirror -->
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/codemirror.css">
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/addon/lint/lint.css">
+ <link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/addon/hint/show-hint.css">
+
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/theme/elegant.css" id="theme-stylesheet">
<link rel="shortcut icon" href="../favicon.ico" />
@@ -107,7 +109,7 @@
</div>
<div class="pull-right">
- <div class="dropdown">
+ <span class="dropdown">
<button class="btn dropdown-toggle" data-toggle="dropdown" tabindex="-1">
<i class="icon-eye-open"></i> <b id="theme-name">elegant</b> <b class="caret"></b>
</button>
@@ -131,8 +133,8 @@
<li><a>paraiso-dark</a></li>
<li><a>paraiso-light</a></li>
<li><a>rubyblue</a></li>
- <li><a>solarized dark</a></li>
- <li><a>solarized light</a></li>
+ <li><a title="solarized">solarized dark</a></li>
+ <li><a title="solarized">solarized light</a></li>
<li><a>the-matrix</a></li>
<li><a>tomorrow-night-eighties</a></li>
<li><a>twilight</a></li>
@@ -140,6 +142,17 @@
<li><a>xq-dark</a></li>
<li><a>xq-light</a></li>
</ul>
+ </span>
+ <button class="btn popover-info" title="Keyboard Shortcuts">
+ <i class="icon-question-sign"></i>
+ </button>
+ <div class="popover-info-content hide">
+ <dl>
+ <dt><label class="label">@</label></dt>
+ <dd>all of the <b>@</b> keywords</dd>
+ <dt><label class="label">Ctrl+Space</label></dt>
+ <dd>available keys in <b>@context</b></dd>
+ </dl>
</div>
</div>
@@ -261,7 +274,10 @@
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/addon/lint/lint.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/addon/lint/json-lint.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/addon/edit/matchbrackets.js"></script>
+ <script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/addon/edit/closebrackets.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/addon/display/placeholder.js"></script>
+ <script src="//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/addon/hint/show-hint.js"></script>
+ <script src="./jsonld-hint.js"></script>
<script src="./codemirror.jsonld.js"></script>
<script type="text/javascript">
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/playground/jsonld-hint.js Wed Oct 23 16:05:21 2013 -0400
@@ -0,0 +1,117 @@
+;(function (CodeMirror, Pos) {
+
+ var ldKeywords = [
+ "context",
+ "id",
+ "value",
+ "language",
+ "type",
+ "container",
+ "list",
+ "set",
+ "reverse",
+ "index",
+ "base",
+ "vocab",
+ "graph"
+ ];
+
+ function getToken(e, cur){ return e.getTokenAt(cur); }
+
+ function accum(arr, fn, result){
+ result = result || [];
+ var len = arr.length,
+ _ = function(val){ result.push(val); };
+ for(var i=0; i < len; i++){
+ fn(_, arr[i], i);
+ }
+ return result;
+ }
+
+ function keywordsLike(str){
+ str = str ? String(str).trim() : "";
+ var result = accum(ldKeywords, function(_, kw, i){
+ !str || ~kw.indexOf(str) ? _('"@' + kw + '"') : null;
+ });
+
+ if(str){ result.sort(relevanceComparator(str)); }
+ return result;
+ }
+
+ function contextLike(str, doc){
+ var ctx, key,
+ result = [];
+
+ str = str ? String(str).trim() : "";
+
+ if(doc && (ctx = doc["@context"])){
+ for(key in ctx){
+ if(!ctx.hasOwnProperty(key)){ return; }
+ !str || ~key.indexOf(str) ? result.push("\"" + key + "\"") : null;
+ }
+ }
+ return result;
+ }
+
+ function relevanceComparator(str){
+ return function(a, b){
+ var result = a.indexOf(str) - b.indexOf(str);
+ if(!result){
+ return a.localeCompare(b);
+ }
+ return result;
+ };
+ }
+
+ CodeMirror.registerHelper("hint", "jsonld", function(editor, options){
+
+ // Find the token at the cursor
+ var cur = editor.getCursor(),
+ token = getToken(editor, cur),
+ tprop = token,
+
+ // was this started by pressing "@"
+ isAt = options.isAt,
+ lastParsed = options.lastParsed,
+
+ word = token.string,
+ start = token.start,
+ end = token.end,
+
+ match;
+
+ function suggest(suggestions){
+ return {
+ list: suggestions,
+ from: Pos(cur.line, start),
+ to: Pos(cur.line, end)
+ };
+ }
+
+ token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;
+
+ // clean up words, move pointers
+ if(word.match(/^[\{\[]/)){
+ // i just made an empty list and typed "@"...
+ word = "";
+ start++;
+ }else if(word.match(/^"/)){
+ // i just started a quoted string...
+ word = word.replace(/(^"|"$)/g, "");
+ }
+
+ if(isAt){
+ // this was started by pressing @..
+ if(!~word.indexOf("@")){
+ // and the user is expecting a @
+ editor.replaceSelection("@", "end", "+input");
+ }
+ return suggest(keywordsLike(word.replace("@", "")));
+ }else if(match = word.match(/^"?@(.*)/)){
+ return suggest(keywordsLike(match[1]));
+ }
+
+ return suggest(keywordsLike(word).concat(contextLike(word, lastParsed)));
+
+ });
+}).call(this, CodeMirror, CodeMirror.Pos);
--- a/playground/playground.js Tue Oct 22 15:53:58 2013 -0400
+++ b/playground/playground.js Wed Oct 23 16:05:21 2013 -0400
@@ -16,6 +16,13 @@
frame: null,
context: null
};
+
+ // the last parsed version of same
+ playground.lastParsed = {
+ markup: null,
+ frame: null,
+ context: null
+ };
// set the active tab to the compacted view
playground.activeTab = 'tab-compacted';
@@ -167,26 +174,52 @@
playground.processQueryParameters();
}
- var processTimer = null;
+ $('.popover-info').popover({
+ placement: "bottom",
+ html: true,
+ content: $(".popover-info-content").html()
+ });
+
+ var processTimer;
+
+ CodeMirror.commands.autocomplete = function(cm) {
+ CodeMirror.showHint(cm, CodeMirror.hint.jsonld, {
+ lastParsed: playground.lastParsed[cm.options._playground_key]
+ });
+ };
+
+ CodeMirror.commands.at_autocomplete = function(cm){
+ CodeMirror.showHint(cm, CodeMirror.hint.jsonld, {
+ isAt: true,
+ lastParsed: playground.lastParsed[cm.options._playground_key]
+ });
+ };
$.each(playground.editors, function(key){
- playground.editors[key] = CodeMirror.fromTextArea(document.getElementById(key), {
- lineNumbers: true,
- matchBrackets: true,
- lineWrapping: true,
- mode: "application/ld+json",
- gutters: ["CodeMirror-lint-markers"],
- theme: "elegant",
- lint: true
- });
+ var editor = playground.editors[key] = CodeMirror.fromTextArea(
+ $("#" + key)[0], {
+ lineNumbers: true,
+ matchBrackets: true,
+ autoCloseBrackets: true,
+ lineWrapping: true,
+ mode: "application/ld+json",
+ gutters: ["CodeMirror-lint-markers"],
+ theme: "elegant",
+ lint: true,
+ extraKeys: {
+ "Ctrl-Space": "autocomplete",
+ "Shift-2": "at_autocomplete"
+ },
+ _playground_key: key
+ });
- // set up 'process' areas to process JSON-LD after typing
- playground.editors[key].on("change",
- function() {
- clearTimeout(processTimer);
- processTimer = setTimeout(playground.process, 500);
- });
- });
+ // set up 'process' areas to process JSON-LD after typing
+ editor
+ .on("change", function() {
+ clearTimeout(processTimer);
+ processTimer = setTimeout(playground.process, 500);
+ });
+ }); // each
};
/**
@@ -317,6 +350,7 @@
// check to see if the JSON-LD markup is valid JSON
try {
var input = JSON.parse(markup);
+ playground.lastParsed.markup = input;
}
catch(e) {
$('#markup-errors').text('JSON markup - ' + e);
@@ -327,20 +361,24 @@
var needParam = false;
var param = null;
var jsonParam = null;
+ var paramType = null;
if(playground.activeTab === 'tab-compacted' ||
playground.activeTab === 'tab-flattened') {
jsonParam = playground.editors.context.getValue();
needParam = true;
+ paramType = "context";
}
else if(playground.activeTab === 'tab-framed') {
jsonParam = playground.editors.frame.getValue();
needParam = true;
+ paramType = "frame";
}
if(needParam) {
try {
param = JSON.parse(jsonParam);
+ playground.lastParsed[paramType] = param;
}
catch(e) {
$('#param-errors').text($('#param-type').text() + ' - ' + e);
@@ -538,13 +576,14 @@
});
$('#theme-select a').click(function(evt){
- var theme = evt.currentTarget.text;
+ var theme = evt.currentTarget.text,
+ file = evt.currentTarget.title ? evt.currentTarget.title : theme;
$("#theme-name").text(theme);
$('#theme-stylesheet').prop("href",
"//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/theme/" +
- theme + ".css"
+ file + ".css"
);
for(var key in playground.editors){