PageSourceSearch

https://json-ld.org/playground/jsonld-hint.js

js json-ld.org collected 2026-10-01 06:57:38 UTC 6,768 bytes, 269 lines download raw bytes

1;(function (CodeMirror, Pos) {
2
3  var ldKeywords = [];
4
5  var previousHint;
6
7  function getToken(e, cur){ return e.getTokenAt(cur); }
8
9  function accum(arr, fn, result){
10    result = result || [];
11    var len = arr.length,
12      _ = function(val){ result.push(val); };
13    for(var i=0; i < len; i++){
14      fn(_, arr[i], i);
15    }
16    return result;
17  }
18
19  function keywordsLike(str){
20    str = str ? String(str).trim() : "";
21    var result = accum(ldKeywords, function(_, kw){
22      !str || ~kw.key.indexOf(str) ? _(kw.key) : null;
23    });
24
25    if(str){ result.sort(relevanceComparator(str)); }
26
27    return accum(result, function(_, kw){
28      _({
29        text: kw,
30        className: "cm-meta"
31      });
32    });
33  }
34
35  function contextLike(str, doc){
36    var ctx, key,
37      result = [];
38
39    str = str ? String(str).trim() : "";
40
41    if(doc && (ctx = doc["@context"])){
42      for(key in ctx){
43        if(!ctx.hasOwnProperty(key)){ return; }
44        !str || ~key.indexOf(str) ? result.push({text: key}) : null;
45      }
46    }
47    return result;
48  }
49
50  function relevanceComparator(str){
51    return function(a, b){
52      var result = a.indexOf(str) - b.indexOf(str);
53      if(!result){
54        return a.localeCompare(b);
55      }
56      return result;
57    };
58  }
59
60  function hidePopover(){
61    previousHint && previousHint.popover("destroy");
62    previousHint = null;
63  }
64
65  function showPopover(doc){
66    return function(hint){
67
68      hidePopover();
69
70      // sigh.
71      previousHint = $(".CodeMirror-hint-active");
72
73      var ctx,
74        hintObj,
75        prefixed,
76        val,
77        content = "",
78        i;
79
80      for(i = 0; i < ldKeywords.length; i++){
81        if(ldKeywords[i].key === hint.text){
82          hintObj = ldKeywords[i];
83        }
84      }
85
86      if(!hintObj && doc && (ctx = doc["@context"])){
87        prefixed = hint.text.split(":");
88
89
90        if(ctx.hasOwnProperty(hint.text)){
91          val = ctx[hint.text];
92          if(prefixed.length === 2){
93            // some kind of prefix?
94            hintObj = {
95              type: val["@type"] ? [val["@type"]] : undefined,
96              description: (ctx[prefixed[0]] || "") + prefixed[1]
97            };
98          }else if(typeof val === "object"){
99            // some complex thing?
100            hintObj = {
101              type: val["@type"] ? [val["@type"]] : undefined,
102              description: val["@id"]
103            };
104          }else{
105            // some kind of naked value?
106            hintObj = {
107              description: val
108            };
109          }
110        }else if(ctx.hasOwnProperty(prefixed[0])){
111          hintObj = {
112            description: (ctx[prefixed[0]] || "") + prefixed[1]
113          };
114        }
115      }
116
117      if(!hintObj){ return; }
118
119      if(hintObj.description){
120        content += '<p><em>' +
121          hintObj.description.replace(/<.*>/g, '') +
122          '</em></p>';
123      }
124
125      if(hintObj.type && hintObj.type.length){
126        content += "<p>Expects ";
127        for(i = 0; i < hintObj.type.length; i++){
128          content += label(hintObj.type[i]);
129        }
130        content += "</p>";
131      }
132
133      if(hintObj.enum && hintObj.enum.length){
134        content += "<p>Can be ";
135        for(i = 0; i < hintObj.enum.length; i++){
136          content += label(hintObj.enum[i]);
137        }
138        content += "</p>";
139      }
140
141      if(hintObj.format){
142        content += "<p>Format " + label(hintObj.format) + "</p>";
143      }
144
145      previousHint.popover({
146        content: content,
147        container: "body",
148        html: true
149      });
150      previousHint.popover("show");
151    };
152  }
153
154  function label(value, cls){
155    return '<span class="label ' + (cls || "") + '">' +
156      value.replace(/<.*>/g, '') +
157      '</span>';
158  }
159
160  function findLdKeywords(schemata){
161    var i,
162      def_key,
163      def,
164      prop_key,
165      prop;
166    // pull out the @ keywords
167    for(i = 0; i < schemata.length; i++){
168      var schema = schemata[0];
169      for(def_key in schema.definitions){
170        def = schema.definitions[def_key];
171        for(prop_key in def.properties){
172          prop = def.properties[prop_key];
173          if(prop_key.indexOf("@") === 0){
174            ldKeywords.push({
175              key: prop_key,
176              description: prop.description,
177              type: typeof prop.type === "string" ?
178                [prop.type] : (prop.type || []),
179              format: prop.format,
180              enum: prop.enum || []
181            });
182          }
183        }
184      }
185    }
186  }
187
188  CodeMirror.registerHelper("hint", "jsonld", function(editor, options){
189
190     // Find the token at the cursor
191    var cur = editor.getCursor(),
192      token = getToken(editor, cur),
193      tprop = token,
194
195      // was this started by pressing "@"
196      isAt = options.isAt,
197      lastParsed = options.lastParsed,
198
199      schemata = typeof options.schemata === "function" ?
200        options.schemata() :
201        options.schemata,
202
203      word = token.string,
204      start = token.start,
205      end = token.end + -1 * (word.slice(-1) === '"'),
206
207      match,
208      stripped = false,
209
210      result;
211
212    if(!ldKeywords.length && schemata.length){
213      findLdKeywords(schemata);
214    }
215
216    function suggest(suggestions){
217      var hints = {
218        list: suggestions,
219        from: Pos(cur.line, start + 1),
220        to: Pos(cur.line, end)
221      };
222
223      // various hint events
224      CodeMirror.on(hints, "select", showPopover(lastParsed));
225      CodeMirror.on(hints, "close", hidePopover);
226
227      return hints;
228    }
229
230    token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;
231
232    function finish(word){
233      // clean up words, move pointers
234      if(word.match(/^[\{\[]/)){
235        // i just made an empty list and typed "@"...
236        word = "";
237        start++;
238        stripped = true;
239      }else if(word.match(/^"/)){
240        // i just started a quoted string...
241        word = word.replace(/(^"|"$)/g, "");
242        stripped = true;
243      }
244
245      if(isAt){
246        // this was started by pressing @..
247        if(!~word.indexOf("@")){
248          // and the user is expecting a @
249          editor.replaceSelection("@", "end", "+input");
250        }else if(!stripped){
251          start--;
252        }
253        return suggest(keywordsLike(word.replace("@", "")));
254      }else if(match = word.match(/^"?@(.*)/)){
255        return suggest(keywordsLike(match[1]));
256      }
257
258      return suggest(contextLike(word, lastParsed).concat(keywordsLike(word)));
259    }
260
261    result = finish(word);
262    if(!result.list.length && word.indexOf(":") !== -1 && word.indexOf("://") === -1){
263      // try again with the prefix
264      result = finish(word.split(":")[0]);
265      result.list.unshift({text: word.replace(/"/g, "")});
266    }
267    return result;
268  });
269}).call(this, CodeMirror, CodeMirror.Pos, $);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.