Merge pull request #1340 from jsbin/fix/1339-tern-loading

Fix/1339 tern loading
This commit is contained in:
Remy Sharp 2014-04-03 11:51:56 +01:00
commit a3915b2739
5 changed files with 223 additions and 383 deletions

View File

@ -275,171 +275,6 @@ div.CodeMirror-cursors {
}
}
/* Addon: codemirror/addon/hint/show-hint.css
========================================================================== */
.CodeMirror-hints {
position: absolute;
z-index: 10;
overflow: hidden;
list-style: none;
margin: 0;
padding: 2px;
-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);
border-radius: 3px;
border: 1px solid silver;
background: white;
font-size: 90%;
font-family: monospace;
max-height: 20em;
overflow-y: auto;
}
.CodeMirror-hint {
margin: 0;
padding: 0 4px;
border-radius: 2px;
max-width: 19em;
overflow: hidden;
white-space: pre;
color: black;
cursor: pointer;
}
.CodeMirror-hint-active {
background: #08f;
color: white;
}
/* codemirror/addon/dialog/dialog.css */
/* Addon: codemirror/addon/hint/show-hint.css
========================================================================== */
.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%;
}
/* Addon: codemirror/addon/tern/tern.css
========================================================================== */
.CodeMirror-Tern-completion {
padding-left: 22px;
position: relative;
}
.CodeMirror-Tern-completion:before {
position: absolute;
left: 2px;
bottom: 2px;
border-radius: 50%;
font-size: 12px;
font-weight: bold;
height: 15px;
width: 15px;
line-height: 16px;
text-align: center;
color: white;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.CodeMirror-Tern-completion-unknown:before {
content: "?";
background: #4bb;
}
.CodeMirror-Tern-completion-object:before {
content: "O";
background: #77c;
}
.CodeMirror-Tern-completion-fn:before {
content: "F";
background: #7c7;
}
.CodeMirror-Tern-completion-array:before {
content: "A";
background: #c66;
}
.CodeMirror-Tern-completion-number:before {
content: "1";
background: #999;
}
.CodeMirror-Tern-completion-string:before {
content: "S";
background: #999;
}
.CodeMirror-Tern-completion-bool:before {
content: "B";
background: #999;
}
.CodeMirror-Tern-completion-guess {
color: #999;
}
.CodeMirror-Tern-tooltip {
border: 1px solid silver;
border-radius: 3px;
color: #444;
padding: 2px 5px;
font-size: 90%;
font-family: monospace;
background-color: white;
white-space: pre-wrap;
max-width: 40em;
position: absolute;
z-index: 10;
-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);
transition: opacity 1s;
-moz-transition: opacity 1s;
-webkit-transition: opacity 1s;
-o-transition: opacity 1s;
-ms-transition: opacity 1s;
}
.CodeMirror-Tern-hint-doc {
max-width: 25em;
}
.CodeMirror-Tern-fname { color: black; }
.CodeMirror-Tern-farg { color: #70a; }
.CodeMirror-Tern-farg-current { text-decoration: underline; }
.CodeMirror-Tern-type { color: #07c; }
.CodeMirror-Tern-fhint-guess { opacity: .7; }
/* CodeMirror themes */
@ -3081,8 +2916,8 @@ input {
padding-left: 0;
}
.CodeMirror-Tern-tooltip,
.CodeMirror-hints {
.editbox .CodeMirror-Tern-tooltip,
.editbox .CodeMirror-hints {
border: 1px solid #aaa;
border: 1px solid rgb(191, 191, 191);
border: 1px solid hsl(0, 0%, 75%);
@ -3093,14 +2928,14 @@ input {
font-family: SourceCodeProRegular, Menlo, Monaco, consolas, monospace;
font-size: 100%;
}
.CodeMirror-Tern-tooltip {
.editbox .CodeMirror-Tern-tooltip {
padding-top: 5px;
}
.CodeMirror-hint {
.editbox .CodeMirror-hint {
font-size: 1.1em;
line-height: 1.4em;
}
.CodeMirror-Tern-completion:before {
.editbox .CodeMirror-Tern-completion:before {
bottom: auto;
top: 1px;
}

View File

@ -1,192 +1,203 @@
(function(){
'use strict';
'use strict';
/*globals $, CodeMirror */
/*globals $, CodeMirror, jsbin, reloadAddons */
// create fake jsbin object
window.jsbin = {
static: jsbin.static,
version: jsbin.version,
panels: {
panels: {
javascript: {
editor: null
},
html: {
editor: null
}
},
allEditors: function(fn) {
fn(jsbin.panels.panels.javascript);
}
// create fake jsbin object
$.extend(jsbin, {
panels: {
panels: {
javascript: {
editor: null
},
html: {
editor: null
}
},
allEditors: function(fn) {
fn(jsbin.panels.panels.javascript);
}
}
});
// create fake template object, used by Tern to search in the html content
// for javascript definitions to load
window.template = {
html: null
};
// needed for the keymaps
$.browser = {};
// work out the browser platform
var ua = navigator.userAgent;
if (ua.indexOf(' Mac ') !== -1) {
$.browser.platform = 'mac';
} else if (/windows|win32/.test(ua)) {
$.browser.platform = 'win';
} else if (/linux/.test(ua)) {
$.browser.platform = 'linux';
} else {
$.browser.platform = '';
}
// abstract this to a function so i can easily implement the TODO below;
function getCurrentSettings(){
// TODO do something here with a server sent object, merge it into localStorage...
// We do now want to lose sync;
return JSON.parse(localStorage.settings || '{}') || {
};
var template = {
html: null
};
$.browser = {};
// work out the browser platform
var ua = navigator.userAgent;
if (ua.indexOf(' Mac ') !== -1) {
$.browser.platform = 'mac';
} else if (/windows|win32/.test(ua)) {
$.browser.platform = 'win';
} else if (/linux/.test(ua)) {
$.browser.platform = 'linux';
} else {
$.browser.platform = '';
}
function pick(obj, keys) {
var copy = {};
for (var i = 0, len = keys.length; i < len; i++) {
copy[keys[i]] = obj[keys[i]];
}
return copy;
}
var settingsKeys = [
'indentWithTabs',
'indentUnit',
'theme',
'tabSize',
'lineWrapping',
'lineNumbers'
];
// abstract this to a function so i can easily implement the TODO below;
function getCurrentSettings(){
// TODO do something here with a server sent object, merge it into localStorage...
// We do now want to lose sync;
return JSON.parse(localStorage.settings || '{}') || {
};
}
function pick(obj, keys) {
var copy = {};
for (var i = 0, len = keys.length; i < len; i++) {
copy[keys[i]] = obj[keys[i]];
}
return copy;
}
var settingsKeys = [
'indentWithTabs',
'indentUnit',
'theme',
'tabSize',
'lineWrapping',
'lineNumbers'
];
var addonsKeys = [
'closebrackets',
'highlight',
'matchtags',
'trailingspace',
'fold',
'vim',
'emacs',
'sublime',
'tern'
];
var $addons = {};
var addonsKeys = [
'closebrackets',
'highlight',
'matchtags',
'trailingspace',
'fold',
'vim',
'emacs',
'sublime',
'tern'
];
var $addons = {};
// setup variables;
var $textarea = $('textarea');
var currentSettings = getCurrentSettings();
if (typeof currentSettings.editor === 'undefined') {
currentSettings.editor = {};
}
if (typeof currentSettings.addons === 'undefined') {
currentSettings.addons = {};
}
jsbin.settings = $.extend({}, currentSettings);
// setup variables;
var $textarea = $('textarea');
var currentSettings = getCurrentSettings();
if (currentSettings.editor === undefined) {
currentSettings.editor = {};
}
if (currentSettings.addons === undefined) {
currentSettings.addons = {};
}
jsbin.settings = $.extend({}, currentSettings);
var editor = window.editor = CodeMirror.fromTextArea($textarea[0], $.extend({
mode: 'text/html'
}, currentSettings.editor));
jsbin.panels.panels.javascript.editor = editor;
jsbin.panels.panels.html.editor = editor;
template.html = editor.getValue();
var editor = window.editor = CodeMirror.fromTextArea($textarea[0], $.extend({
mode: 'text/html'
}, currentSettings.editor));
jsbin.panels.panels.javascript.editor = editor;
jsbin.panels.panels.html.editor = editor;
window.template.html = editor.getValue();
var $CodeMirror = $('.CodeMirror');
var $CodeMirror = $('.CodeMirror');
// setup font-size this has a little more work behind it.
if (currentSettings.font) {
$CodeMirror.css('font-size', currentSettings.font + 'px');
editor.refresh();
}
var $fontsize = $('#font-size');
if (!$fontsize.val()) {
$fontsize.val(parseInt($CodeMirror.css('font-size')));
}
// setup font-size this has a little more work behind it.
if (currentSettings.font) {
$CodeMirror.css('font-size', currentSettings.font + 'px');
editor.refresh();
}
var $fontsize = $('#font-size');
if (!$fontsize.val()) {
$fontsize.val(parseInt($CodeMirror.css('font-size'), 10));
}
// addons
for (var i = 0; i < addonsKeys.length; i++) {
$addons[ addonsKeys[i] ] = $('#' + addonsKeys[i]).prop('checked', currentSettings.addons[ addonsKeys[i] ] || false);
}
if ($('[name=keymap]:checked').length === 0) {
$('#defaultKeymap').prop('checked', true);
}
// addons
for (var i = 0; i < addonsKeys.length; i++) {
$addons[ addonsKeys[i] ] = $('#' + addonsKeys[i]).prop('checked', currentSettings.addons[ addonsKeys[i] ] || false);
}
if ($('[name=keymap]:checked').length === 0) {
$('#defaultKeymap').prop('checked', true);
}
// Setup the rest
var $theme = $('#theme').val(editor.getOption('theme'));
var $tabSize = $('#tabSize').val(editor.getOption('tabSize'));
var $lineWrapping = $('#lineWrapping').prop('checked', editor.getOption('lineWrapping'));
var $lineNumbers = $('#lineNumbers').prop('checked', editor.getOption('lineNumbers'));
var tabs = editor.getOption('indentWithTabs');
// Setup the rest
var $theme = $('#theme').val(editor.getOption('theme'));
var $tabSize = $('#tabSize').val(editor.getOption('tabSize'));
var $lineWrapping = $('#lineWrapping').prop('checked', editor.getOption('lineWrapping'));
var $lineNumbers = $('#lineNumbers').prop('checked', editor.getOption('lineNumbers'));
var tabs = editor.getOption('indentWithTabs');
var $indentWithTabs = $('#indentWithTabs').prop('checked', tabs);
$('#indentWithSpaces').prop('checked', !tabs);
var $csrf = $('#_csrf');
var $csrf = $('#_csrf');
// Listeners
$('input[type="number"], input[type="text"]').on('input', saveSettings);
$(':checkbox, :radio').on('change', saveSettings);
$('select').on('change', saveSettings);
// Listeners
$('input[type="number"], input[type="text"]').on('input', saveSettings);
$(':checkbox, :radio').on('change', saveSettings);
$('select').on('change', saveSettings);
function saveSettings() {
// First we update the codemirror instance on page
editor.setOption('lineWrapping', $lineWrapping.prop('checked'));
editor.setOption('lineNumbers', $lineNumbers.prop('checked'));
editor.setOption('indentWithTabs', $indentWithTabs.prop('checked'));
editor.setOption('tabSize', $tabSize.val());
editor.setOption('theme', $theme.val());
$CodeMirror.css('font-size', $fontsize.val()+'px');
editor.refresh();
function saveSettings() {
// First we update the codemirror instance on page
editor.setOption('lineWrapping', $lineWrapping.prop('checked'));
editor.setOption('lineNumbers', $lineNumbers.prop('checked'));
editor.setOption('indentWithTabs', $indentWithTabs.prop('checked'));
editor.setOption('tabSize', $tabSize.val());
editor.setOption('theme', $theme.val());
$CodeMirror.css('font-size', $fontsize.val()+'px');
editor.refresh();
// Merge all our settings together
var localStorageSettings = JSON.parse(localStorage.settings || '{}');
var codemirrorSettings = pick(editor.options, settingsKeys);
var newSettingsEditor = $.extend(localStorageSettings.editor, codemirrorSettings);
// Merge all our settings together
var localStorageSettings = JSON.parse(localStorage.settings || '{}');
var codemirrorSettings = pick(editor.options, settingsKeys);
var newSettingsEditor = $.extend(localStorageSettings.editor, codemirrorSettings);
var addonsSettings = {};
for (var i = 0; i < addonsKeys.length; i++) {
addonsSettings[ addonsKeys[i] ] = $addons[ addonsKeys[i] ].prop('checked');
var addonsSettings = {};
for (var i = 0; i < addonsKeys.length; i++) {
addonsSettings[ addonsKeys[i] ] = $addons[ addonsKeys[i] ].prop('checked');
}
var newSettingsAddons = $.extend(localStorageSettings.addons, addonsSettings);
// Save locally
localStorageSettings.editor = newSettingsEditor;
localStorageSettings.font = $fontsize.val();
localStorageSettings.addons = newSettingsAddons;
localStorage.settings = JSON.stringify(localStorageSettings);
$.extend(jsbin.settings.addons, newSettingsAddons);
// destroy and recreate CodeMirror and load the addons
editor.toTextArea();
editor = CodeMirror.fromTextArea($textarea[0], $.extend({
mode: 'text/html'
}, newSettingsEditor));
jsbin.panels.panels.javascript.editor = editor;
jsbin.panels.panels.html.editor = editor;
window.template.html = editor.getValue();
$CodeMirror = $('.CodeMirror');
$CodeMirror.css('font-size', $fontsize.val()+'px');
editor.refresh();
// Do not load Tern files
var tempAddonsKeys = addonsKeys.slice(0);
var tempAddonsKeysTern = tempAddonsKeys.indexOf('tern');
if (tempAddonsKeysTern !== -1) {
tempAddonsKeys.splice(tempAddonsKeysTern, 1);
}
reloadAddons(tempAddonsKeys);
// Save on server
$.ajax({
type: 'POST',
dataType: 'json',
data: {
settings: JSON.stringify(localStorageSettings),
_csrf: $csrf.val()
},
success: function() {
if (console && console.log) {
console.log('Success on saving settings');
}
var newSettingsAddons = $.extend(localStorageSettings.addons, addonsSettings);
},
error: function(xhr, status) {
if (console && console.log) {
console.log('Error: ' + status);
}
}
});
// Save locally
localStorageSettings.editor = newSettingsEditor;
localStorageSettings.font = $fontsize.val();
localStorageSettings.addons = newSettingsAddons;
localStorage.settings = JSON.stringify(localStorageSettings);
$.extend(jsbin.settings.addons, newSettingsAddons);
// destroy and recreate CodeMirror and load the addons
editor.toTextArea();
editor = CodeMirror.fromTextArea($textarea[0], $.extend({
mode: 'text/html'
}, newSettingsEditor));
jsbin.panels.panels.javascript.editor = editor;
jsbin.panels.panels.html.editor = editor;
template.html = editor.getValue();
$CodeMirror = $('.CodeMirror');
$CodeMirror.css('font-size', $fontsize.val()+'px');
editor.refresh();
reloadAddons();
// Save on server
$.ajax({
type: 'POST',
dataType: 'json',
data: {
settings: JSON.stringify(localStorageSettings),
_csrf: $csrf.val()
},
success: function() {
// console.log('success');
},
error: function() {
// console.log('there was an error saving');
}
});
}
}
})();

View File

@ -132,6 +132,9 @@
},
tern: {
url: [
'/js/vendor/codemirror4/addon/dialog/dialog.css',
'/js/vendor/codemirror4/addon/hint/show-hint.css',
'/js/vendor/codemirror4/addon/tern/tern.css',
'/js/vendor/codemirror4/addon/hint/show-hint.js',
'/js/vendor/codemirror4/addon/dialog/dialog.js',
'/js/vendor/cm_addons/tern.js',
@ -151,10 +154,11 @@
test: function () {
return jsbin.panels.panels.javascript.editor.openDialog &&
CodeMirror.showHint &&
CodeMirror.TernServer;
CodeMirror.TernServer &&
CodeMirror.startTern;
},
done: function (cm) {
//
done: function () {
CodeMirror.startTern();
}
}
};
@ -240,8 +244,12 @@
options.forEach(loadAddon);
// External method to realod all the addons
window.reloadAddons = function() {
options.forEach(loadAddon);
window.reloadAddons = function(arr) {
if (arr) {
arr.forEach(loadAddon);
} else {
options.forEach(loadAddon);
}
};
})();

View File

@ -1,9 +1,8 @@
(function () {
'use strict';
/*globals $, jsbin, CodeMirror, simpleJsHint, template */
/*globals $, jsbin, CodeMirror, template, ternDefinitions, ternBasicDefs */
// jsbin.settings.addons.tern = true
var ternServer;
var ternLoaded = {};
@ -71,54 +70,46 @@
};
var searchTernDefinition = function(htmlCode) {
if (jsbin.settings.addons.tern === true) {
for (var i = 0; i < ternDefinitions.length; i++) {
if (ternDefinitions[i].match.test(htmlCode)) {
if (ternDefinitions[i].type === 'def') {
loadTernDefinition(ternDefinitions[i].name, ternDefinitions[i].file);
}
else {
loadTernFile(ternDefinitions[i].name, ternDefinitions[i].file);
}
for (var i = 0; i < ternDefinitions.length; i++) {
if (ternDefinitions[i].match.test(htmlCode)) {
if (ternDefinitions[i].type === 'def') {
loadTernDefinition(ternDefinitions[i].name, ternDefinitions[i].file);
} else {
loadTernFile(ternDefinitions[i].name, ternDefinitions[i].file);
}
}
}
};
// Overwrite the autocomplete function to use tern
if (jsbin.settings.addons.tern === true) {
CodeMirror.commands.autocomplete = function(cm) {
if (CodeMirror.snippets(cm) === CodeMirror.Pass) {
var pos = cm.getCursor();
var tok = cm.getTokenAt(pos);
var indent = '';
if (cm.options.indentWithTabs) {
indent = '\t';
} else {
indent = new Array(cm.options.indentUnit + 1).join(' ');
}
CodeMirror.commands.autocomplete = function(cm) {
if (CodeMirror.snippets(cm) === CodeMirror.Pass) {
var pos = cm.getCursor();
var tok = cm.getTokenAt(pos);
var indent = '';
if (cm.options.indentWithTabs) {
indent = '\t';
} else {
indent = new Array(cm.options.indentUnit + 1).join(' ');
}
if (tok.string === ';') {
return cm.replaceRange(indent, pos);
}
if (tok.string.trim() !== '') {
return ternServer.complete(cm);
}
if (tok.string === ';') {
return cm.replaceRange(indent, pos);
}
};
} else {
// Use the simple autocompletion
CodeMirror.commands.autocomplete = simpleJsHint;
}
if (tok.string.trim() !== '') {
return ternServer.complete(cm);
}
return cm.replaceRange(indent, pos);
}
};
if (jsbin.settings.addons.tern === true) {
CodeMirror.startTern = function() {
loadTern(jsbin.panels.panels.javascript.editor);
searchTernDefinition(template.html);
$('#library').bind('change', function () {
searchTernDefinition(jsbin.panels.panels.html.getCode());
});
}
};
})();

View File

@ -107,11 +107,6 @@ if (jsbin.settings.codemirror) {
if (jsbin.settings.editor.theme) {
$(document.documentElement).addClass('cm-s-' + jsbin.settings.editor.theme.split(' ')[0]);
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = jsbin['static'] + '/js/vendor/codemirror4/theme/' + jsbin.settings.editor.theme.split(' ')[0] + '.css?' + jsbin.version;
document.getElementsByTagName('head')[0].appendChild(link);
}
// Add a pre-filter to all ajax requests to add a CSRF header to prevent