mirror of
https://github.com/jsbin/jsbin.git
synced 2026-01-18 15:18:04 +00:00
Merge branch 'master' into feature/url-revisionless
Conflicts: lib/features.js lib/models/bin.js public/css/style.css public/js/chrome/save.js public/js/editors/editors.js public/js/editors/keycontrol.js public/js/jsbin.js scripts.json views/index.html
This commit is contained in:
commit
56222dd394
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,8 +2,8 @@ logs
|
||||
logs/*
|
||||
config.*.json
|
||||
public/js/jsbin*.js
|
||||
public/js/prod/*
|
||||
.nodemonignore
|
||||
public/js/prod
|
||||
!config.default.json
|
||||
statsd.js
|
||||
.allow-devtools-edit
|
||||
|
||||
19
Gruntfile.js
19
Gruntfile.js
@ -1,3 +1,4 @@
|
||||
'use strict';
|
||||
/*global module:false*/
|
||||
module.exports = function (grunt) {
|
||||
var fs = require('fs'),
|
||||
@ -32,7 +33,7 @@ module.exports = function (grunt) {
|
||||
}
|
||||
}
|
||||
|
||||
child = exec(cmd, function (err, stdout, stderr) {
|
||||
child = exec(cmd, function (err) {
|
||||
if (err) {
|
||||
grunt.log.writeln(err.message);
|
||||
process.exit(err.code);
|
||||
@ -49,7 +50,7 @@ module.exports = function (grunt) {
|
||||
|
||||
var distpaths = {
|
||||
script: 'public/js/prod/<%= pkg.name %>-<%= pkg.version %>.js',
|
||||
map: 'public/js/prod/<%= pkg.name %>.map.json', // don't version this so we overwrite
|
||||
// map: 'public/js/prod/<%= pkg.name %>.map.json', // don't version this so we overwrite
|
||||
min: 'public/js/prod/<%= pkg.name %>-<%= pkg.version %>.min.js',
|
||||
runner: 'public/js/prod/runner-<%= pkg.version %>.js',
|
||||
runnermin: 'public/js/prod/runner-<%= pkg.version %>.min.js'
|
||||
@ -96,9 +97,9 @@ module.exports = function (grunt) {
|
||||
},
|
||||
dist: {
|
||||
src: [
|
||||
'public/js/intro.js',
|
||||
'public/js/intro-start.js',
|
||||
'<%= scriptsRelative %>',
|
||||
'public/js/outro.js'
|
||||
'public/js/outro-start.js'
|
||||
],
|
||||
dest: distpaths.script
|
||||
},
|
||||
@ -119,12 +120,12 @@ module.exports = function (grunt) {
|
||||
options: {
|
||||
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
|
||||
'<%= grunt.template.today("yyyy-mm-dd") %> */\n',
|
||||
sourceMap: distpaths.map,
|
||||
sourceMappingURL: '/js/prod/jsbin.map.json',
|
||||
sourceMapPrefix: 2,
|
||||
sourceMapRoot: '/js',
|
||||
// sourceMap: distpaths.map,
|
||||
// sourceMappingURL: '/js/prod/jsbin.map.json',
|
||||
// sourceMapPrefix: 2,
|
||||
// sourceMapRoot: '/js',
|
||||
},
|
||||
src: '<%= scriptsRelative %>',
|
||||
src: distpaths.script, //'<%= scriptsRelative %>',
|
||||
dest: distpaths.min
|
||||
},
|
||||
runner: {
|
||||
|
||||
@ -4,7 +4,7 @@ JS Bin is an open source collaborative web development debugging tool.
|
||||
|
||||
## If you use JS Bin locally...
|
||||
|
||||
It likely means you're not going to subscribe a pro user - which is how we're sustaining our project, which is cool, but [please consider donating via gittip here](https://www.gittip.com/js_bin/).
|
||||
It likely means you're not going to subscribe as a pro user - which is how we're sustaining our project, which is cool, but [please consider donating via gittip here](https://www.gittip.com/js_bin/).
|
||||
|
||||
## What can JS Bin do?
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
-- MySQL dump 10.13 Distrib 5.5.9, for osx10.6 (i386)
|
||||
-- MySQL dump 10.13 Distrib 5.5.29, for debian-linux-gnu (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: jsbin
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 5.5.9
|
||||
-- Server version 5.5.29-0ubuntu0.12.04.1
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
@ -15,11 +15,31 @@
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `customers`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `customers` (
|
||||
`stripe_id` char(255) NOT NULL,
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) DEFAULT NULL,
|
||||
`name` char(255) NOT NULL,
|
||||
`expiry` datetime DEFAULT NULL,
|
||||
`active` tinyint(1) DEFAULT '1',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `stripe_id` (`stripe_id`),
|
||||
KEY `name` (`name`),
|
||||
KEY `user_id` (`user_id`),
|
||||
KEY `expired` (`expiry`,`active`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `forgot_tokens`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `forgot_tokens`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `forgot_tokens` (
|
||||
@ -32,16 +52,34 @@ CREATE TABLE `forgot_tokens` (
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `owner_bookmarks`
|
||||
--
|
||||
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `owner_bookmarks` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` char(255) NOT NULL,
|
||||
`url` char(255) NOT NULL,
|
||||
`revision` int(11) NOT NULL,
|
||||
`type` char(50) NOT NULL,
|
||||
`created` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `name` (`name`,`type`,`created`),
|
||||
KEY `revision` (`url`(191),`revision`)
|
||||
) ENGINE=InnoDB CHARSET=utf8;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `owners`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `owners`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `owners` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` char(255) NOT NULL,
|
||||
`name` char(75) NOT NULL,
|
||||
`url` char(255) NOT NULL,
|
||||
`revision` int(11) DEFAULT '1',
|
||||
`last_updated` datetime NOT NULL,
|
||||
@ -50,41 +88,47 @@ CREATE TABLE `owners` (
|
||||
`css` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`javascript` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`archive` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`visibility` ENUM('public', 'unlisted', 'private') DEFAULT 'public' NOT NULL,
|
||||
`visibility` enum('public','unlisted','private') NOT NULL DEFAULT 'public',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `name_url` (`name`,`url`,`revision`),
|
||||
KEY `last_updated` (`name`,`last_updated`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=289 DEFAULT CHARSET=utf8;
|
||||
KEY `last_updated` (`name`,`last_updated`),
|
||||
KEY `url` (`url`,`revision`)
|
||||
) ENGINE=InnoDB CHARSET=utf8;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `ownership`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `ownership`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `ownership` (
|
||||
`name` char(255) NOT NULL,
|
||||
`name` char(75) NOT NULL,
|
||||
`key` char(255) NOT NULL,
|
||||
`email` varchar(255) NOT NULL DEFAULT '',
|
||||
`api_key` VARCHAR(255) NULL,
|
||||
`github_token` VARCHAR(255),
|
||||
`github_id` int(11),
|
||||
`last_login` datetime NOT NULL,
|
||||
`created` datetime NOT NULL,
|
||||
`updated` datetime NOT NULL,
|
||||
PRIMARY KEY (`name`),
|
||||
`api_key` varchar(255) DEFAULT NULL,
|
||||
`github_token` varchar(255) DEFAULT NULL,
|
||||
`github_id` int(11) DEFAULT NULL,
|
||||
`verified` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`pro` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`settings` text,
|
||||
`dropbox_token` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`),
|
||||
KEY `name_key` (`name`,`key`),
|
||||
KEY `created` (`created`),
|
||||
KEY `ownership_api_key` (`api_key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
) ENGINE=InnoDB CHARSET=utf8;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `sandbox`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `sandbox`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `sandbox` (
|
||||
@ -110,7 +154,7 @@ CREATE TABLE `sandbox` (
|
||||
KEY `streaming_key` (`streaming_key`),
|
||||
KEY `spam` (`created`,`last_viewed`),
|
||||
KEY `revision` (`url`(191),`revision`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1585463 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
@ -122,4 +166,4 @@ CREATE TABLE `sandbox` (
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2013-01-11 9:26:38
|
||||
-- Dump completed on 2014-06-05 7:45:41
|
||||
|
||||
@ -43,6 +43,9 @@
|
||||
"server": {
|
||||
"logger": "default"
|
||||
},
|
||||
"security": {
|
||||
"allowAnonymousPreview": false
|
||||
},
|
||||
"client": {
|
||||
"user": true
|
||||
},
|
||||
@ -96,6 +99,8 @@
|
||||
"devices",
|
||||
"download",
|
||||
"downloads",
|
||||
"edit",
|
||||
"embed",
|
||||
"faq",
|
||||
"favorites",
|
||||
"favs",
|
||||
@ -152,8 +157,10 @@
|
||||
"translate",
|
||||
"trends",
|
||||
"unarchive",
|
||||
"upgrade",
|
||||
"user",
|
||||
"users",
|
||||
"watch",
|
||||
"widgets",
|
||||
"tutorials",
|
||||
"video",
|
||||
|
||||
@ -75,19 +75,27 @@ module.exports = function(app, connection) {
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
return next();
|
||||
next();
|
||||
}
|
||||
|
||||
// We're basically yaking the same idea that connects cookieSessions use
|
||||
// from here: http://www.senchalabs.org/connect/cookieSession.html
|
||||
res.on('header', function () {
|
||||
var oldSessionUser = undefsafe(req, '_sessionBeforeBlacklist.user');
|
||||
if (oldSessionUser) {
|
||||
// FIXME - this is hacky
|
||||
if (JSON.stringify(oldSessionUser) !== req._userJSONCopy) {
|
||||
memcached.set(oldSessionUser.name, oldSessionUser);
|
||||
var sessionUser = undefsafe(req, '_sessionBeforeBlacklist.user');
|
||||
// If there's no user on the session, there's nothing to save
|
||||
if (!sessionUser) {
|
||||
return;
|
||||
}
|
||||
// If we have a copy of the user on the "way in" and it's the
|
||||
// same as the user on the session now, there's no need to save
|
||||
if (req._userJSONCopy) {
|
||||
// FIXME - this is hacky (use a compare/equals function?)
|
||||
if (JSON.stringify(sessionUser) === req._userJSONCopy) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// For everything else, we save them to memcache
|
||||
memcached.set(sessionUser.name, sessionUser);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
'use strict';
|
||||
var Memcached = require('memcached'),
|
||||
replify = require('replify'),
|
||||
Promise = require('rsvp').Promise;
|
||||
var net = require('net');
|
||||
var Memcached = require('memcached');
|
||||
var replify = require('replify');
|
||||
var Promise = require('rsvp').Promise;
|
||||
|
||||
var testForMemcachedServer = function (connection) {
|
||||
(new net.Socket())
|
||||
.connect(connection.split(':')[1])
|
||||
.on('error', console.log.bind(console, 'Couldn\'t connect to memcached server - is it running?'))
|
||||
.destroy();
|
||||
};
|
||||
|
||||
module.exports = function(connection) {
|
||||
|
||||
testForMemcachedServer(connection);
|
||||
|
||||
var memcachedstore = new Memcached(connection);
|
||||
|
||||
var API = {
|
||||
|
||||
@ -218,7 +218,7 @@ app.connect = function (callback) {
|
||||
app.emit('before:cookies', { app: app });
|
||||
|
||||
app.use(express.cookieParser(app.get('session secret') || generateSessionSecret()));
|
||||
app.use(filteredCookieSession(['user.settings', 'passport', 'user.email', 'user.github_id', 'user.github_token', 'user.bincount', 'user.api_key', 'flashCache', ]));
|
||||
app.use(filteredCookieSession(['user.settings', 'passport', 'user.email', 'user.github_id', 'user.github_token', 'user.bincount', 'user.api_key']));
|
||||
app.use(express.cookieSession({
|
||||
key: 'jsbin',
|
||||
cookie: {
|
||||
@ -259,6 +259,7 @@ app.connect = function (callback) {
|
||||
app.use(middleware.cors());
|
||||
app.use(middleware.jsonp());
|
||||
app.use(middleware.protocolCheck);
|
||||
app.use(middleware.decompressBody);
|
||||
app.use(middleware.notices());
|
||||
app.use(mount, app.router);
|
||||
|
||||
|
||||
@ -1,19 +1,36 @@
|
||||
'use strict';
|
||||
var blacklist = require('./config').blacklist || {};
|
||||
|
||||
module.exports.validate = function (bin) {
|
||||
var type, keywords, content, index, length;
|
||||
var okay = true;
|
||||
|
||||
for (type in blacklist) {
|
||||
if (blacklist.hasOwnProperty(type)) {
|
||||
content = bin[type] || '';
|
||||
keywords = blacklist[type] || [];
|
||||
['html', 'javascript', 'css'].forEach(function (type) {
|
||||
var content = bin[type] || '';
|
||||
var keywords = blacklist[type] || [];
|
||||
|
||||
for (index = 0, length = keywords.length; index < length; index += 1) {
|
||||
if (content.indexOf(keywords[index]) > -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (okay) { // then keep checking
|
||||
okay = keywords.filter(function (keyword) {
|
||||
// return if found
|
||||
return content.indexOf(keyword) !== -1;
|
||||
}).length === 0;
|
||||
}
|
||||
});
|
||||
|
||||
// now test regexp
|
||||
if (blacklist.re) {
|
||||
['html', 'javascript', 'css'].forEach(function (type) {
|
||||
var content = bin[type] || '';
|
||||
|
||||
if (okay) { // then keep checking
|
||||
okay = blacklist.re.filter(function (re) {
|
||||
// convert the string regexp to a real reggie expession.
|
||||
var reg = new RegExp(re);
|
||||
// return if found
|
||||
return content.match(reg) !== null;
|
||||
}).length === 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
|
||||
return okay;
|
||||
};
|
||||
|
||||
7
lib/data/backers.json
Normal file
7
lib/data/backers.json
Normal file
@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "Librato",
|
||||
"url": "http://metrics.librato.com",
|
||||
"image": "http://data.bigdatastartups.netdna-cdn.com/wp-content/uploads/2013/08/Librato-logo.jpg"
|
||||
}
|
||||
]
|
||||
50
lib/data/features.json
Normal file
50
lib/data/features.json
Normal file
@ -0,0 +1,50 @@
|
||||
[
|
||||
{
|
||||
"name": "Vanity URLs",
|
||||
"user": "pro"
|
||||
},
|
||||
{
|
||||
"name": "Private Bins",
|
||||
"user": "pro"
|
||||
},
|
||||
{
|
||||
"name": "Dropbox Sync",
|
||||
"user": "pro"
|
||||
},
|
||||
{
|
||||
"name": "Read & write API access",
|
||||
"user": "pro"
|
||||
},
|
||||
{
|
||||
"name": "Direct support",
|
||||
"user": "pro"
|
||||
},
|
||||
{
|
||||
"name": "Asset hosting (coming soon)",
|
||||
"user": "pro"
|
||||
},
|
||||
{
|
||||
"name": "Save bins to Gist",
|
||||
"user": "free"
|
||||
},
|
||||
{
|
||||
"name": "Codecasting (unlimited connections)",
|
||||
"user": "anon"
|
||||
},
|
||||
{
|
||||
"name": "Live reload (unlimited connections)",
|
||||
"user": "anon"
|
||||
},
|
||||
{
|
||||
"name": "Custom starting code",
|
||||
"user": "anon"
|
||||
},
|
||||
{
|
||||
"name": "Unlimited external resources",
|
||||
"user": "anon"
|
||||
},
|
||||
{
|
||||
"name": "Custom libraries",
|
||||
"user": "anon"
|
||||
}
|
||||
]
|
||||
81
lib/data/welcome-panel.json
Normal file
81
lib/data/welcome-panel.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
"features": {
|
||||
"title": "JS Bin features",
|
||||
"link": "http://jsbin.com/help/features",
|
||||
"data": [
|
||||
{
|
||||
"title": "Getting started with JS Bin",
|
||||
"link": "http://jsbin.com/help/getting-started"
|
||||
},
|
||||
{
|
||||
"title": "Keyboard Shortcuts",
|
||||
"link": "http://jsbin.com/help/keyboard-shortcuts"
|
||||
},
|
||||
{
|
||||
"title": "Test out code on other devices",
|
||||
"link": "http://jsbin.com/help/test-code-on-other-devices"
|
||||
},
|
||||
{
|
||||
"title": "Exporting/importing gist",
|
||||
"link": "http://jsbin.com/help/export-gist"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pro-features": {
|
||||
"title": "Pro features",
|
||||
"link": "http://jsbin.com/help/features",
|
||||
"data": [
|
||||
{
|
||||
"title": "Sync with Dropbox",
|
||||
"link": "http://jsbin.com/help/features"
|
||||
},
|
||||
{
|
||||
"title": "Private bins",
|
||||
"link": "http://jsbin.com/help/features"
|
||||
},
|
||||
{
|
||||
"title": "Vanity URLs",
|
||||
"link": "http://jsbin.com/help/features"
|
||||
},
|
||||
{
|
||||
"title": "Read & write API",
|
||||
"link": "http://jsbin.com/help/features"
|
||||
}
|
||||
]
|
||||
},
|
||||
"blog": {
|
||||
"title": "Blog",
|
||||
"link": "http://jsbin.com/blog",
|
||||
"data": [
|
||||
{
|
||||
"title": "JSHint, line highlighting and more",
|
||||
"link": "http://jsbin.com/blog/twdtw-10-jshint-highlighting-pro"
|
||||
}
|
||||
]
|
||||
},
|
||||
"help": {
|
||||
"title": "Help",
|
||||
"link": "http://jsbin.com/help",
|
||||
"data": [
|
||||
{
|
||||
"title": "Versions: processors & more",
|
||||
"link": "http://jsbin.com/help/versions"
|
||||
},
|
||||
{
|
||||
"title": "Delete a bin",
|
||||
"link": "http://jsbin.com/help/delete-a-bin"
|
||||
}
|
||||
]
|
||||
},
|
||||
"twitter": {
|
||||
"title": "Recently on Twitter",
|
||||
"link": "http://twitter.com/js_bin",
|
||||
"data": [
|
||||
{
|
||||
"title": "We’re expecting to hit our 10 __millionth__ bin in around a week’s time. Wow.",
|
||||
"link": "https://twitter.com/js_bin/status/479668198384865281"
|
||||
}
|
||||
]
|
||||
},
|
||||
"quote": "“Everyone should learn how to program a computer because it teaches you how to think” — <span class=\"toppanel-quote-author\">Steve Jobs</span>"
|
||||
}
|
||||
@ -17,7 +17,9 @@ module.exports = function (options) {
|
||||
return !!zmq ? {
|
||||
initialize: function () {
|
||||
socket.bind(options.dropbox.port, function(err) {
|
||||
console.error(err);
|
||||
if (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
passport.use(new DropboxOAuth2Strategy({
|
||||
clientID: options.dropbox.id,
|
||||
|
||||
@ -48,7 +48,7 @@ function team(req) {
|
||||
}
|
||||
|
||||
function pro(req) {
|
||||
return flags.alpha(req) || undefsafe(req, 'session.user.pro');
|
||||
return alpha(req) || undefsafe(req, 'session.user.pro');
|
||||
}
|
||||
|
||||
/* End: user types */
|
||||
@ -67,23 +67,22 @@ function percentage(n, req) {
|
||||
|
||||
var flags = {
|
||||
/* Begin: actual features */
|
||||
admin: team,
|
||||
|
||||
pro: pro,
|
||||
|
||||
github: function () {
|
||||
return options.github && options.github.id;
|
||||
},
|
||||
|
||||
// private bins
|
||||
private: function (req) {
|
||||
return alpha(req); // pro
|
||||
},
|
||||
private: pro, // live June 16, 2014-05-27
|
||||
|
||||
// whether user can delete bins
|
||||
delete: true, // live 25 Feb 2014
|
||||
|
||||
// allows for sandbox play in a bin without actually saving
|
||||
sandbox: function (req) {
|
||||
return alpha(req); // pro
|
||||
},
|
||||
sandbox: pro, // live June 16, 2014-05-27
|
||||
|
||||
// info/hover card with details of bin and streaming info
|
||||
infocard: function (req) {
|
||||
@ -91,9 +90,7 @@ var flags = {
|
||||
},
|
||||
|
||||
// seperate account management pages
|
||||
accountPages: function (req) {
|
||||
return alpha(req) || percentage(10, req);
|
||||
},
|
||||
accountPages: true, // live 2014-05-27
|
||||
|
||||
// use SSL for sign in
|
||||
sslLogin: true,
|
||||
@ -101,38 +98,28 @@ var flags = {
|
||||
// using memcache for sessions
|
||||
serverSession: true,
|
||||
|
||||
// code-analysis engine for javascript panel
|
||||
tern: function (req) {
|
||||
return alpha(req);
|
||||
},
|
||||
vanity: pro, // live June 16, 2014-05-27
|
||||
|
||||
stripe: function (req) {
|
||||
return team(req);
|
||||
},
|
||||
dropbox: pro, // live June 20, 2014
|
||||
|
||||
vanity: function (req) {
|
||||
return alpha(req); // return pro(req);
|
||||
},
|
||||
|
||||
dropbox: function (req) {
|
||||
return alpha(req);
|
||||
},
|
||||
|
||||
assets: function (req) {
|
||||
return alpha(req);
|
||||
},
|
||||
assets: false, // disabled June 20, 2014
|
||||
|
||||
revisionless: function (req) {
|
||||
return team(req);
|
||||
},
|
||||
|
||||
fileMenuTest: function (req) {
|
||||
return team(req) || percentage(50, req);
|
||||
// allows the user to use jsbin entirely through SSL
|
||||
sslForAll: function (req) {
|
||||
return alpha(req) && undefsafe(req, 'session.user.settings.ssl');
|
||||
},
|
||||
|
||||
upgrade: function (req) {
|
||||
return team(req); // return !pro(req);
|
||||
},
|
||||
fileMenuTest: true, // live 2014-05-27 - #1414
|
||||
|
||||
// ability to upgrade to a pro user
|
||||
upgrade: team,
|
||||
|
||||
// top introduction view with help and features of JS Bin
|
||||
welcomePanel: alpha,
|
||||
};
|
||||
|
||||
var features = module.exports = new Features(flags);
|
||||
|
||||
@ -10,7 +10,9 @@ var async = require('asyncjs'),
|
||||
undefsafe = require('undefsafe'),
|
||||
metrics = require('../metrics'),
|
||||
features = require('../features'),
|
||||
config = require('../config'),
|
||||
Promise = require('rsvp').Promise,
|
||||
welcomePanel = require('../welcome-panel'),
|
||||
Observable = utils.Observable;
|
||||
|
||||
module.exports = Observable.extend({
|
||||
@ -76,6 +78,8 @@ module.exports = Observable.extend({
|
||||
data.settings.processors = processorSettings;
|
||||
}
|
||||
|
||||
data.post = true;
|
||||
|
||||
this.render(req, res, data);
|
||||
},
|
||||
getCustom: function (req, res, next) {
|
||||
@ -146,6 +150,40 @@ module.exports = Observable.extend({
|
||||
req.embed = true;
|
||||
next();
|
||||
},
|
||||
testPreviewAllowed: function (req, res, next) {
|
||||
/**
|
||||
* if the bin does not have a user who create it
|
||||
* and it was made 2 hours ago
|
||||
* then redirect to the /edit url
|
||||
*/
|
||||
var user = undefsafe(req, 'bin.metadata.name');
|
||||
if (config.security.allowAnonymousPreview !== true && user === 'anonymous' || !user) {
|
||||
var created = req.bin.created;
|
||||
|
||||
// this is hard coded for production jsbin.com - it means that all bins
|
||||
// created before we released this change won't be affected by the limit
|
||||
if (config.url.host === 'jsbin.com') {
|
||||
if (req.bin.id < 10786492) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
// test the time created, and if it's older than 90 minutes, then redirect
|
||||
// to the edit view. Details as to why on our blog
|
||||
if ((Date.now() - created.getTime()) / 1000 / 60 > 90) {
|
||||
var msg = 'This bin was created anonymously and its free preview time has expired.';
|
||||
|
||||
if (!req.session.user) {
|
||||
msg += ' <a href="/register">Get a free unrestricted account</a>';
|
||||
} else {
|
||||
msg += ' <a href="/clone">Clone it create to enable the full preview</a>';
|
||||
}
|
||||
res.flash(req.flash.NOTIFICATION, msg);
|
||||
return res.redirect(this.helpers.urlForBin(req.bin) + '/edit');
|
||||
}
|
||||
}
|
||||
next();
|
||||
},
|
||||
getBinPreview: function (req, res, next) {
|
||||
// if we're loading a username/last(-:n)? and no 'n' is present
|
||||
// it breaks quiet, leaving it as undefined, here we manually
|
||||
@ -185,7 +223,7 @@ module.exports = Observable.extend({
|
||||
resolve();
|
||||
}
|
||||
}).then(function () {
|
||||
this.formatPreview(req.bin, options, function (err, formatted) {
|
||||
this.formatPreview(req, req.bin, options, function (err, formatted) {
|
||||
if (err) {
|
||||
next(err);
|
||||
}
|
||||
@ -215,7 +253,7 @@ module.exports = Observable.extend({
|
||||
output = 'var template = ' + output;
|
||||
}
|
||||
res.send(output);
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
getBinSourceFile: function (req, res) {
|
||||
this.protectVisibility(req.session.user, req.bin, function(err, bin){
|
||||
@ -520,7 +558,7 @@ module.exports = Observable.extend({
|
||||
// Notify interested parties (spike) that a new revision was created
|
||||
var oldBin = req.bin;
|
||||
handler.once('created', function (newBin) {
|
||||
handler.emit('new-revision', oldBin, newBin);
|
||||
handler.emit('new-revision', bin, newBin);
|
||||
});
|
||||
|
||||
handler.completeCreateBin(result, req, res, next);
|
||||
@ -636,7 +674,7 @@ module.exports = Observable.extend({
|
||||
var filename = ['jsbin', bin.url, bin.revision, 'html'].join('.'),
|
||||
options = {analytics: false, edit: false, silent: true};
|
||||
|
||||
this.formatPreview(bin, options, function (err, formatted) {
|
||||
this.formatPreview(req, bin, options, function (err, formatted) {
|
||||
if (err) {
|
||||
next(err);
|
||||
}
|
||||
@ -703,7 +741,7 @@ module.exports = Observable.extend({
|
||||
_this.render(req, res, results);
|
||||
} else {
|
||||
var options = {edit: true, silent: true, csrf: req.session._csrf};
|
||||
_this.formatPreview(results, options, function (err, formatted) {
|
||||
_this.formatPreview(req, results, options, function (err, formatted) {
|
||||
if (err) {
|
||||
next(err);
|
||||
}
|
||||
@ -796,7 +834,9 @@ module.exports = Observable.extend({
|
||||
helpers = this.helpers,
|
||||
version = helpers.set('version'),
|
||||
created = req.flash('checksum') || {},
|
||||
root = helpers.url('', true),
|
||||
sslForAll = features('sslForAll', req),
|
||||
ssl = req.embed ? req.secure && sslForAll : sslForAll,
|
||||
root = helpers.url('', true, ssl),
|
||||
_this = this,
|
||||
production = (req.cookies && req.cookies.debug) ? false : helpers.production,
|
||||
jsbin;
|
||||
@ -808,6 +848,8 @@ module.exports = Observable.extend({
|
||||
root = root.replace('://', '://' + req.subdomain + '.');
|
||||
}
|
||||
|
||||
var statik = helpers.urlForStatic(undefined, ssl);
|
||||
|
||||
jsbin = this.jsbin(bin, {
|
||||
version: version,
|
||||
token: req.session._csrf,
|
||||
@ -815,7 +857,7 @@ module.exports = Observable.extend({
|
||||
shareRoot: features('vanity', req) ? 'http://' + req.session.user.name + '.' + req.app.get('url host') : root,
|
||||
metadata: bin.metadata,
|
||||
runner: helpers.runner,
|
||||
static: helpers.urlForStatic(),
|
||||
static: statik,
|
||||
settings: !bin.url ? config && config.settings : {},
|
||||
// If we've pulled a just created bin out of the flash messages object
|
||||
// then we check to see if the previously created bin is the one we're
|
||||
@ -873,6 +915,14 @@ module.exports = Observable.extend({
|
||||
}
|
||||
}
|
||||
|
||||
var embedURL = null;
|
||||
if (req.bin) {
|
||||
embedURL = helpers.editUrlForBin(req.bin, true).replace(/\/edit/, '/embed');
|
||||
if (req.secure) {
|
||||
embedURL = embedURL.replace(/http:/, 'https:');
|
||||
}
|
||||
}
|
||||
|
||||
if (info) {tipType = 'info';}
|
||||
if (notification) {tipType = 'notification';}
|
||||
if (error) {tipType = 'error';}
|
||||
@ -903,20 +953,25 @@ module.exports = Observable.extend({
|
||||
isProduction: production,
|
||||
concat: req.cookies && req.cookies.debug ? req.cookies.debug === 'concat' : false,
|
||||
root: root,
|
||||
static: helpers.urlForStatic(),
|
||||
static: statik,
|
||||
bincount: user.bincount,
|
||||
url: url,
|
||||
embedURL: embedURL,
|
||||
vanity: features('vanity', req) ? 'http://' + req.session.user.name + '.' + req.app.get('url host') : root,
|
||||
live: req.live,
|
||||
embed: req.embed,
|
||||
code_id: bin.url,
|
||||
code_id_path: url,
|
||||
code_id_domain: helpers.urlForBin(bin, true).replace(/^https?:\/\//, '')
|
||||
code_id_domain: helpers.urlForBin(bin, true).replace(/^https?:\/\//, ''),
|
||||
welcomePanel: welcomePanel.getData()
|
||||
});
|
||||
};
|
||||
|
||||
if (user) {
|
||||
_this.models.user.getBinCount(user.name, function (err, result) {
|
||||
if (err || !result) {
|
||||
return done(err);
|
||||
}
|
||||
user.bincount = result.total;
|
||||
done();
|
||||
});
|
||||
@ -1038,11 +1093,20 @@ module.exports = Observable.extend({
|
||||
// this value isn't always present in anonymous metadata
|
||||
options.metadata.last_updated = bin.created;
|
||||
|
||||
var statik = options.static || options.root;
|
||||
var runner = this.helpers.runner;
|
||||
if (statik.indexOf('https') === 0) {
|
||||
// then ensure the runner is also https
|
||||
if (runner.indexOf('https') === -1) {
|
||||
runner = runner.replace(/http/, 'https');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
root: options.root,
|
||||
shareRoot: options.shareRoot,
|
||||
runner: this.helpers.runner,
|
||||
static: options.static || options.root,
|
||||
runner: runner,
|
||||
static: statik,
|
||||
version: options.version,
|
||||
state: {
|
||||
token: options.token,
|
||||
@ -1065,6 +1129,10 @@ module.exports = Observable.extend({
|
||||
template[panel] = utils.cleanForRender(template[panel] || '');
|
||||
});
|
||||
|
||||
if (bin.post) {
|
||||
template.post = bin.post;
|
||||
}
|
||||
|
||||
template.url = this.helpers.jsbinURL(bin); //.permalink;
|
||||
return template;
|
||||
},
|
||||
@ -1107,7 +1175,7 @@ module.exports = Observable.extend({
|
||||
|
||||
// nothing returned as it updates the bin object
|
||||
},
|
||||
formatPreview: function (bin, options, fn) {
|
||||
formatPreview: function (req, bin, options, fn) {
|
||||
metrics.increment('bin.rendered');
|
||||
this.applyProcessors(bin);
|
||||
|
||||
@ -1135,8 +1203,8 @@ module.exports = Observable.extend({
|
||||
|
||||
// Include 'Edit in JS Bin' button
|
||||
if (options.edit) {
|
||||
var data = {static: helpers.urlForStatic(''), root: helpers.url('/', true), csrf: options.csrf};
|
||||
insert.push('<script src="' + helpers.urlForStatic('js/render/edit.js?' + helpers.set('version')) + '"></script>');
|
||||
var data = {static: helpers.urlForStatic('', req.secure), root: helpers.url('/', true, req.secure), csrf: options.csrf};
|
||||
insert.push('<script src="' + helpers.urlForStatic('js/render/edit.js?' + helpers.set('version'), req.secure) + '"></script>');
|
||||
insert.push('<script>jsbinShowEdit(' + JSON.stringify(data) + ');</script>');
|
||||
}
|
||||
|
||||
@ -1145,7 +1213,7 @@ module.exports = Observable.extend({
|
||||
if (!options.silent && _this.models.bin.isStreaming(bin)) { // jshint ignore:line
|
||||
_this.emit('render-scripts', scripts);
|
||||
insert = insert.concat(scripts.map(function (script) {
|
||||
script = script.indexOf('http') === 0 ? script : helpers.urlForStatic(script);
|
||||
script = script.indexOf('http') === 0 ? script : helpers.urlForStatic(script, req.secure);
|
||||
return '<script src="' + script + '"></script>';
|
||||
}));
|
||||
}
|
||||
@ -1193,14 +1261,26 @@ module.exports = Observable.extend({
|
||||
|
||||
context = {
|
||||
domain: helpers.set('url host'),
|
||||
permalink: helpers.editUrlForBin(bin, true)
|
||||
permalink: helpers.editUrlForBin(bin, true),
|
||||
user: undefsafe(bin, 'metadata.name') || false,
|
||||
year: (new Date()).getYear() + 1900
|
||||
};
|
||||
|
||||
// Append attribution comment to header.
|
||||
helpers.render('comment', context, function (err, comment) {
|
||||
formatted = formatted.replace(/<html[^>]*>/, function ($0) {
|
||||
var done = false;
|
||||
formatted = formatted.replace(/<meta.*?charset.*?[^>]*>/, function ($0) {
|
||||
if ($0) {
|
||||
done = true;
|
||||
}
|
||||
return $0 + '\n' + (comment || '').trim();
|
||||
});
|
||||
|
||||
if (!done) {
|
||||
formatted = formatted.replace(/<html[^>]*>/, function ($0) {
|
||||
return $0 + '\n' + (comment || '').trim();
|
||||
});
|
||||
}
|
||||
return fn(err || null, err ? undefined : formatted);
|
||||
});
|
||||
}
|
||||
@ -1335,12 +1415,15 @@ module.exports = Observable.extend({
|
||||
var owner = undefsafe(req, 'bin.metadata.name');
|
||||
var streamingKey = undefsafe(req, 'bin.streaming_key');
|
||||
|
||||
// if the user doesn't own this bin...and
|
||||
if (user !== owner) {
|
||||
// the checksum doesn't match the key in the database
|
||||
if (req.body.checksum !== streamingKey) {
|
||||
// then it's not theirs to delete
|
||||
return res.send(403, {error: 'Not authorised.'});
|
||||
// Only check if they're not admin
|
||||
if (features('admin', req) === false) {
|
||||
// if the user doesn't own this bin...and
|
||||
if (user !== owner) {
|
||||
// the checksum doesn't match the key in the database
|
||||
if (req.body.checksum !== streamingKey) {
|
||||
// then it's not theirs to delete
|
||||
return res.send(403, {error: 'Not authorised.'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -523,7 +523,7 @@ module.exports = Observable.extend({
|
||||
// Was a name matching user found
|
||||
if (jsbinUserFromGithubName) {
|
||||
// Uh oh, that user exists already
|
||||
res.flash(res.flash.ERROR, 'Your GitHub username is already used in JS Bin. If this is you, please sign into JS Bin and "link your GitHub account" otherwise, register, then link your account.');
|
||||
res.flash(res.flash.ERROR, 'Your GitHub username is taken on JS Bin. If this is you, please register with JS Bin and link your GitHub account. <a target="_blank" href="http://jsbin.com/help/github-username">Read this for more help</a>.');
|
||||
return res.redirect(redirect);
|
||||
}
|
||||
// No matching user was found, create an account!
|
||||
|
||||
13
lib/hbs.js
13
lib/hbs.js
@ -13,8 +13,21 @@ hbs.registerHelper('feature', function(request, flag, options) {
|
||||
}
|
||||
});
|
||||
|
||||
hbs.registerHelper('equal', function(lvalue, rvalue, options) {
|
||||
if (arguments.length < 3) {
|
||||
return false;
|
||||
}
|
||||
if (lvalue !== rvalue) {
|
||||
return options.inverse(this);
|
||||
} else {
|
||||
return options.fn(this);
|
||||
}
|
||||
});
|
||||
|
||||
hbs.registerHelper('dump', function(obj) {
|
||||
return JSON.stringify(obj, null, 2);
|
||||
});
|
||||
|
||||
hbs.registerPartial('welcome_panel', __dirname + '/../views/partials/welcome-panel.html');
|
||||
|
||||
module.exports = hbs;
|
||||
@ -5,6 +5,8 @@ var utils = require('./utils'),
|
||||
models = require('./models'),
|
||||
config = require('./config'),
|
||||
features = require('./features'),
|
||||
LZString = require('lz-string'),
|
||||
undefsafe = require('undefsafe'),
|
||||
parse = require('url').parse;
|
||||
|
||||
// Custom middleware used by the application.
|
||||
@ -152,12 +154,17 @@ module.exports = {
|
||||
// Checks for a subdomain in the current url, if found it sets the
|
||||
// req.subdomain property. This supports existing behaviour that allows
|
||||
// subdomains to load custom config files.
|
||||
subdomain: function (app) {
|
||||
subdomain: function () {
|
||||
return function (req, res, next) {
|
||||
var apphost = app.set('url host').split(':')[0],
|
||||
var apphost = config.url.host,
|
||||
outputHost = undefsafe(config, 'security.preview'),
|
||||
host = req.header('Host', ''),
|
||||
offset = host.indexOf(apphost);
|
||||
|
||||
if (host === outputHost) {
|
||||
offset = host.indexOf(outputHost);
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
// Slice the host from the subdomain and subtract 1 for
|
||||
// trailing . on the subdomain.
|
||||
@ -276,20 +283,34 @@ module.exports = {
|
||||
if (!features('sslLogin', req)) {
|
||||
return next();
|
||||
}
|
||||
var pathname = parse(req.url).pathname;
|
||||
var pathShouldBeSecure = pathIsSSL(parse(req.url).pathname);
|
||||
var forceSSL = features('sslForAll', req);
|
||||
|
||||
// var useSSL = pathShouldBeSecure || forceSSL;
|
||||
|
||||
if (!config.url.ssl) {
|
||||
if (req.secure) {
|
||||
// if the request is https and we don't have a https host available in our config
|
||||
return res.redirect('http://' + req.headers.host.replace(/:.*/, '') + req.url);
|
||||
}
|
||||
} else {
|
||||
if (!req.secure) {
|
||||
if (pathIsSSL(pathname)) {
|
||||
return res.redirect('https://' + req.headers.host.replace(/:.*/, '') + req.url);
|
||||
// we do have https host in our config
|
||||
if (req.secure) {
|
||||
// we are using SSL...
|
||||
if (forceSSL && pathShouldBeSecure === false) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (pathShouldBeSecure === false) {
|
||||
req.shouldNotBeSecure = true;
|
||||
}
|
||||
} else {
|
||||
if (!pathIsSSL(pathname)) {
|
||||
return res.redirect('http://' + req.headers.host.replace(/:.*/, '') + req.url);
|
||||
// if (forceSSL) {
|
||||
// return next();
|
||||
// }
|
||||
if (pathShouldBeSecure) {
|
||||
// if the request is not https and _should be_, redirect to https
|
||||
return res.redirect('https://' + req.headers.host.replace(/:.*/, '') + req.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -332,10 +353,18 @@ module.exports = {
|
||||
next();
|
||||
};
|
||||
},
|
||||
decompressBody: function (req, res, next) {
|
||||
if (req.body && req.body.compressed) {
|
||||
req.body.compressed.split(',').forEach(function (key) {
|
||||
req.body[key] = LZString.decompressFromUTF16(req.body[key]);
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
function pathIsSSL (route) {
|
||||
return config.url.ssl.paths.reduce(function (bool, path) {
|
||||
return (undefsafe(config, 'url.ssl.paths') || []).reduce(function (bool, path) {
|
||||
if (route.indexOf(path) === 0) {
|
||||
bool = true;
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ var Observable = require('../utils').Observable,
|
||||
crypto = require('crypto'),
|
||||
dropbox = require('../dropbox'),
|
||||
processors = require('../processors'),
|
||||
blacklist = require('../blacklist'),
|
||||
readFileSync = require('fs').readFileSync,
|
||||
compileTemplate = require('handlebars').compile,
|
||||
binFileTemplate = compileTemplate(readFileSync(__dirname + '/../dropbox/bin-template.hbs').toString()),
|
||||
@ -19,25 +20,7 @@ var model = {
|
||||
this.store = store;
|
||||
},
|
||||
load: function (params, fn) {
|
||||
this.store.getBin(params, function loadedGetBin(err, bin) {
|
||||
if (err || !bin) {
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
this.getBinMetadata(bin, function loadBinMetadata(err, metadata) {
|
||||
if (err) {
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
bin.metadata = metadata;
|
||||
|
||||
if (this.isVisible(bin, params.username)) {
|
||||
fn(null, bin);
|
||||
} else {
|
||||
fn(401);
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
this.store.getBin(params, this.binLoadCallback(params, fn));
|
||||
},
|
||||
isVisible: function (bin, username) {
|
||||
if (!username) {
|
||||
@ -68,13 +51,21 @@ var model = {
|
||||
return false;
|
||||
},
|
||||
latest: function (params, fn) {
|
||||
this.store.getLatestBin(params, function loadedGetLatestBin(err, bin) {
|
||||
this.store.getLatestBin(params, this.binLoadCallback(params, fn));
|
||||
},
|
||||
binLoadCallback: function (params, fn) {
|
||||
|
||||
return function (err, bin) {
|
||||
if (err || !bin) {
|
||||
return fn(err, null);
|
||||
}
|
||||
|
||||
bin.latest = true;
|
||||
|
||||
if (blacklist.validate(bin) === false) {
|
||||
return fn(410);
|
||||
}
|
||||
|
||||
this.getBinMetadata(bin, function loadedGetMetadata(err, metadata) {
|
||||
bin.metadata = metadata;
|
||||
|
||||
@ -85,8 +76,8 @@ var model = {
|
||||
fn(401);
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this);
|
||||
|
||||
}.bind(this));
|
||||
},
|
||||
// Create a new bin.
|
||||
create: function (data, fn) {
|
||||
@ -200,7 +191,7 @@ Object.keys(model).forEach(function (key) {
|
||||
};
|
||||
|
||||
args.push(fn);
|
||||
method.apply(this, args);
|
||||
return method.apply(this, args);
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
183
lib/routes.js
183
lib/routes.js
@ -1,3 +1,4 @@
|
||||
'use strict';
|
||||
var express = require('express'),
|
||||
handlers = require('./handlers'),
|
||||
models = require('./models'),
|
||||
@ -8,7 +9,9 @@ var express = require('express'),
|
||||
features = require('./features'),
|
||||
metrics = require('./metrics'),
|
||||
scripts = require('../scripts.json'),
|
||||
Promise = require('rsvp').Promise,
|
||||
undefsafe = require('undefsafe'),
|
||||
Promise = require('rsvp').Promise, // jshint ignore:line
|
||||
config = require('./config'),
|
||||
reBin = null; // created when the module is imported
|
||||
|
||||
function tag(label) {
|
||||
@ -19,7 +22,6 @@ function tag(label) {
|
||||
}
|
||||
|
||||
function time(label) {
|
||||
'use strict';
|
||||
return function (req, res, next) {
|
||||
res.on('header', function () {
|
||||
metrics.timing(label, Date.now() - req.start);
|
||||
@ -39,17 +41,28 @@ module.exports = function (app) {
|
||||
models: models,
|
||||
mailer: app.mailer,
|
||||
helpers: helpers.createHelpers(app)
|
||||
}, binHandler, sessionHandler, errorHandler, userHandler;
|
||||
};
|
||||
|
||||
// Create handlers for accepting incoming requests.
|
||||
var binHandler = new handlers.BinHandler(sandbox);
|
||||
var sessionHandler = new handlers.SessionHandler(sandbox);
|
||||
var errorHandler = new handlers.ErrorHandler(sandbox);
|
||||
var userHandler = new handlers.UserHandler(sandbox);
|
||||
|
||||
var root = app.get('url full');
|
||||
|
||||
reBin = new RegExp(root + '/(.*?)/(\\d+)/?');
|
||||
reBin = new RegExp(root.replace(/^http.?:/, '') + '/(.*?)/(\\d+)/?');
|
||||
|
||||
function binParamFromReferer(req, res, next) {
|
||||
reBin.lastIndex = 0; // reset position
|
||||
|
||||
var r = root;
|
||||
if (features('sslForAll', req)) {
|
||||
r = r.replace(/http:/, 'https:');
|
||||
}
|
||||
|
||||
// only allow cloning via url if it came from jsbin
|
||||
if (req.headers.referer && req.headers.referer.indexOf(root) === 0) {
|
||||
if (req.headers.referer && req.headers.referer.indexOf(r) === 0) {
|
||||
var match = req.headers.referer.match(reBin) || [];
|
||||
if (match.length) {
|
||||
req.params.bin = match[1];
|
||||
@ -68,11 +81,20 @@ module.exports = function (app) {
|
||||
};
|
||||
}
|
||||
|
||||
// Create handlers for accepting incoming requests.
|
||||
binHandler = new handlers.BinHandler(sandbox);
|
||||
sessionHandler = new handlers.SessionHandler(sandbox);
|
||||
errorHandler = new handlers.ErrorHandler(sandbox);
|
||||
userHandler = new handlers.UserHandler(sandbox);
|
||||
function shouldNotBeSecure(req, res, next) {
|
||||
// otherwise redirect to the http version
|
||||
if (req.shouldNotBeSecure) {
|
||||
return res.redirect('http://' + req.headers.host.replace(/:.*/, '') + req.url);
|
||||
}
|
||||
|
||||
// if the flag isn't present, then skip on
|
||||
next();
|
||||
}
|
||||
|
||||
function noframes(req, res, next) {
|
||||
res.setHeader('X-Frame-Options', 'DENY');
|
||||
next();
|
||||
}
|
||||
|
||||
// Redirects
|
||||
|
||||
@ -140,9 +162,31 @@ module.exports = function (app) {
|
||||
});
|
||||
|
||||
// Set up the routes.
|
||||
app.get(/(?:.*\/(edit|watch|download|source)|^\/$)$/, function (req, res, next) {
|
||||
var ssl = features('sslForAll', req);
|
||||
|
||||
if ( (!req.secure && ssl) || // a) request *should* be secure
|
||||
(req.secure && !ssl) ) { // b) request is secure and *should not* be
|
||||
var url = sandbox.helpers.url(req.url, true, ssl);
|
||||
return res.redirect(url);
|
||||
}
|
||||
|
||||
next('route');
|
||||
});
|
||||
|
||||
// secure the following paths from being iframed, note that it's also applied
|
||||
// to full bin output
|
||||
app.get('/auth/*', noframes, function (req, res, next) {
|
||||
next('route');
|
||||
});
|
||||
app.get('/account/*', noframes, function (req, res, next) {
|
||||
next('route');
|
||||
});
|
||||
|
||||
|
||||
app.get('/', time('request.root'), userHandler.loadVanityURL, binHandler.loadBin, binHandler.getBinPreview);
|
||||
app.get('/', binHandler.getDefault);
|
||||
app.get('/gist/*', binHandler.getDefault);
|
||||
app.get('/gist/*', shouldNotBeSecure, binHandler.getDefault);
|
||||
app.post('/', binHandler.getFromPost);
|
||||
|
||||
// sandbox
|
||||
@ -150,9 +194,10 @@ module.exports = function (app) {
|
||||
|
||||
// Runner
|
||||
app.get('/runner', function (req, res) {
|
||||
var statik = sandbox.helpers.urlForStatic(undefined, req.secure && features('sslForAll', req));
|
||||
res.render('runner', {
|
||||
scripts: app.get('is_production') ? false : scripts.runner,
|
||||
static: sandbox.helpers.urlForStatic()
|
||||
static: statik
|
||||
});
|
||||
});
|
||||
|
||||
@ -160,13 +205,41 @@ module.exports = function (app) {
|
||||
app.post('/api/save', binHandler.apiCreateBin);
|
||||
app.post('/api/:bin/save', binHandler.apiCreateRevision);
|
||||
|
||||
app.get('/account/upgrade', features.route('upgrade'), function (req, res) {
|
||||
var featureList = require('./data/features.json');
|
||||
var backersList = require('./data/backers.json');
|
||||
var root = sandbox.helpers.url('', true, req.secure);
|
||||
var statik = sandbox.helpers.urlForStatic('', req.secure);
|
||||
var referrer = req.get('referer');
|
||||
var stripeKey = undefsafe(config, 'payment.stripe.public');
|
||||
var stripeProMonthURL = undefsafe(config, 'payment.stripe.urls.month');
|
||||
var showCoupon = false;
|
||||
if (req.query.coupon === 'true') {
|
||||
showCoupon = true;
|
||||
}
|
||||
|
||||
res.render('upgrade', {
|
||||
layout: 'sub/layout',
|
||||
root: root,
|
||||
static: statik,
|
||||
referrer: referrer,
|
||||
featureList: featureList,
|
||||
backersList: backersList,
|
||||
cachebust: app.set('is_production') ? '?' + app.set('version') : '',
|
||||
stripeKey: stripeKey,
|
||||
stripeProMonthURL: stripeProMonthURL,
|
||||
showCoupon: showCoupon
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Account settings
|
||||
var renderAccountSettings = (function(){
|
||||
var pages = ['editor', 'profile', 'delete', 'preferences'];
|
||||
|
||||
return function renderAccountSettings (req, res) {
|
||||
var root = sandbox.helpers.url('', true, req.secure);
|
||||
var static = sandbox.helpers.urlForStatic('', req.secure);
|
||||
var statik = sandbox.helpers.urlForStatic('', req.secure);
|
||||
var referrer = req.get('referer');
|
||||
|
||||
var page = pages.indexOf(req.param('page')) === -1 ? false : req.param('page') + '.html';
|
||||
@ -180,6 +253,16 @@ module.exports = function (app) {
|
||||
}
|
||||
}
|
||||
|
||||
var info = req.flash(req.flash.INFO),
|
||||
error = req.flash(req.flash.ERROR),
|
||||
notification = req.flash(req.flash.NOTIFICATION);
|
||||
|
||||
var flash = error || notification || info;
|
||||
var flashType = '';
|
||||
if (info) {flashType = 'info';}
|
||||
if (notification) {flashType = 'notification';}
|
||||
if (error) {flashType = 'error';}
|
||||
|
||||
if (!page) {
|
||||
return res.redirect('back');
|
||||
}
|
||||
@ -188,12 +271,14 @@ module.exports = function (app) {
|
||||
}
|
||||
|
||||
res.render('account/' + page, {
|
||||
flash_tip: flash, // jshint ignore:line
|
||||
flash_tip_type: flashType, // jshint ignore:line
|
||||
token: req.session._csrf,
|
||||
layout: 'sub/layout.html',
|
||||
referrer: referrer,
|
||||
httproot: root.replace('https', 'http'),
|
||||
root: root,
|
||||
static: static,
|
||||
static: statik,
|
||||
user: req.session.user,
|
||||
request: req,
|
||||
addons: app.get('is_production') ? false : addons,
|
||||
@ -202,7 +287,7 @@ module.exports = function (app) {
|
||||
};
|
||||
}());
|
||||
|
||||
app.get('/account/:page', features.route('accountPages'), renderAccountSettings);
|
||||
app.get('/account/:page', shouldNotBeSecure, features.route('accountPages'), renderAccountSettings);
|
||||
app.get('/account', function(req, res) {
|
||||
res.redirect('/account/editor');
|
||||
});
|
||||
@ -303,14 +388,14 @@ module.exports = function (app) {
|
||||
app.get('/logout', function (req, res) {
|
||||
if (req.session.user) {
|
||||
var root = sandbox.helpers.url('', true, req.secure);
|
||||
var static = sandbox.helpers.urlForStatic('', req.secure);
|
||||
var statik = sandbox.helpers.urlForStatic('', req.secure);
|
||||
|
||||
res.render('account/logout', {
|
||||
token: req.session._csrf,
|
||||
learn: 'http://learn.jsbin.com/',
|
||||
layout: 'sub/layout.html',
|
||||
root: root,
|
||||
static: static,
|
||||
static: statik,
|
||||
user: req.session.user
|
||||
});
|
||||
} else {
|
||||
@ -364,7 +449,7 @@ module.exports = function (app) {
|
||||
// FIXME the assetUrl url lookup from username should go via memcache,
|
||||
// because doing a mysql query for every image that appears in these bins
|
||||
// will start to get silly expensive
|
||||
app.get('/:username/assets/*', features.route('assets'), function (req, res, next) {
|
||||
app.get('/:username/assets/*', features.route('assets'), function (req, res) {
|
||||
if (req.user.settings && req.user.settings.assetUrl) {
|
||||
res.redirect(req.user.settings.assetUrl + req.params[0]);
|
||||
} else {
|
||||
@ -397,8 +482,7 @@ module.exports = function (app) {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.get('/:username/last(-:n)?/:quiet(quiet)?', tag('keepLatest'), binHandler.getLatestForUser, spike.getStream, binHandler.getBinPreview);
|
||||
// username shortcut routes
|
||||
app.get('/:username/last(-:n)?/edit', binHandler.getLatestForUser, binHandler.getBin);
|
||||
app.get('/:username/last(-:n)?/watch', binHandler.getLatestForUser, binHandler.live, binHandler.getBin);
|
||||
|
||||
@ -438,31 +522,62 @@ module.exports = function (app) {
|
||||
app.get('/download', binParamFromReferer, binHandler.loadBin, binHandler.downloadBin);
|
||||
app.get('/:bin/:rev?/download', binHandler.downloadBin);
|
||||
|
||||
|
||||
/**
|
||||
* Full output routes
|
||||
*/
|
||||
var secureOutput = function (req, res, next) {
|
||||
// 1. check request is supposed to be on a vanity url
|
||||
// 2. if not, then check if the req.headers.host matches security.preview
|
||||
// 3. if not, redirect
|
||||
var metadata = undefsafe(req, 'bin.metadata');
|
||||
var settings = {};
|
||||
var ssl = false;
|
||||
var url;
|
||||
|
||||
// apply x-frame-options, but pass a dummy `next`
|
||||
noframes(req, res, function () {});
|
||||
|
||||
if (settings) {
|
||||
try {
|
||||
settings = JSON.parse(metadata.settings);
|
||||
ssl = features('sslForAll', { session: { user: { name: metadata.name, pro: metadata.pro, settings: { ssl: settings.ssl }}}});
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (features('sslForAll', req)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
url = sandbox.helpers.url(req.url, true, ssl);
|
||||
|
||||
if ( (!req.secure && ssl) || // a) request *should* be secure
|
||||
(req.secure && !ssl) ) { // b) request is secure and *should not* be
|
||||
return res.redirect(url);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
// Source
|
||||
app.get('/:bin/:rev?/source', time('request.source'), binHandler.getBinSource);
|
||||
app.get('/:bin/:rev?.:format(js|json|css|html|md|markdown|stylus|less|coffee|jade|svg)', time('request.source'), binHandler.getBinSourceFile);
|
||||
app.get('/:bin/:rev?/:format(js)', function (req, res) {
|
||||
app.get('/:bin/:rev?/source', secureOutput, time('request.source'), binHandler.getBinSource);
|
||||
app.get('/:bin/:rev?.:format(js|json|css|html|md|markdown|stylus|less|coffee|jade|svg)', secureOutput, time('request.source'), binHandler.getBinSourceFile);
|
||||
app.get('/:bin/:rev?/:format(js)', secureOutput, function (req, res) {
|
||||
// Redirect legacy /js suffix to the new .js extension.
|
||||
res.redirect(301, req.path.replace(/\/js$/, '.js'));
|
||||
});
|
||||
|
||||
// Log - not actually implemented
|
||||
app.get('/:bin/:rev/log', spike.getLog);
|
||||
app.post('/:bin/:rev/log', spike.postLog);
|
||||
|
||||
// Preview
|
||||
app.get('/:bin/:quiet(quiet)?', spike.getStream, binHandler.getBinPreview);
|
||||
app.get('/:bin/:rev?/:quiet(quiet)?', spike.getStream, binHandler.getBinPreview);
|
||||
app.get('/:bin/:rev?/stats', tag('stats'), spike.getStream);
|
||||
app.get('/:username/last(-:n)?/:quiet(quiet)?', secureOutput, tag('keepLatest'), binHandler.getLatestForUser, spike.getStream, binHandler.getBinPreview);
|
||||
app.get('/:bin/:quiet(quiet)?', secureOutput, binHandler.testPreviewAllowed, spike.getStream, binHandler.getBinPreview);
|
||||
app.get('/:bin/:rev?/:quiet(quiet)?', secureOutput, binHandler.testPreviewAllowed, spike.getStream, binHandler.getBinPreview);
|
||||
app.get('/:bin/:rev?/stats', tag('stats'), secureOutput, spike.getStream);
|
||||
|
||||
// used for simple testing
|
||||
app.get('/test/error/:num', function (req, res, next) {
|
||||
next(req.params.num * 1);
|
||||
});
|
||||
|
||||
app.get('/test/bin/:bin', function (req, res, next) {
|
||||
res.send(req.bin);
|
||||
});
|
||||
|
||||
// Handle failed auth requests.
|
||||
app.use(sessionHandler.githubCallbackWithError);
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ replify('spike', {
|
||||
setInterval(function () {
|
||||
Object.keys(sessions).forEach(function (id) {
|
||||
sessions[id].res.forEach(function (res) {
|
||||
if (!res.connection.writable) {
|
||||
if (!res.connection || !res.connection.writable) {
|
||||
utils.removeConnection(id, res);
|
||||
}
|
||||
});
|
||||
|
||||
@ -65,6 +65,10 @@ module.exports = function (config) {
|
||||
plan: req.params.plan,
|
||||
card: req.body.stripeToken
|
||||
};
|
||||
var couponCode = req.url.indexOf('?') !== -1 ? req.url.split('?').pop() : null;
|
||||
if (couponCode) {
|
||||
customerOptions.coupon = couponCode;
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
res.flash('error', 'Please login before attempting to signup');
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
'use strict';
|
||||
var features = require('../features.js');
|
||||
|
||||
module.exports = function routes(app, config) {
|
||||
|
||||
// pass handlers the app object so they have access to the stripe API key
|
||||
var stripeHandler = require('./handlers')(config);
|
||||
|
||||
// following POSTs come directly from stripe
|
||||
app.post('/hooks/stripe', stripeHandler.authEvent, stripeHandler.handleTransaction);
|
||||
|
||||
app.post('/subscribe/:plan', stripeHandler.handleSubscription);
|
||||
|
||||
app.get('/signup', features.route('stripe'), stripeHandler.renderSignUp);
|
||||
|
||||
};
|
||||
|
||||
@ -156,7 +156,7 @@ module.exports = {
|
||||
gravatar: function (email, size) {
|
||||
email = (email || '').trim().toLowerCase();
|
||||
var hash = crypto.createHash('md5').update(email).digest('hex');
|
||||
return 'http://www.gravatar.com/avatar/' + hash + '?s=' + (size || 29); // 26
|
||||
return '//www.gravatar.com/avatar/' + hash + '?s=' + (size || 29); // 26
|
||||
},
|
||||
re: {
|
||||
flatten: /\s+/g,
|
||||
|
||||
48
lib/welcome-panel.js
Normal file
48
lib/welcome-panel.js
Normal file
@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
var info = [];
|
||||
var reg = /^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i;
|
||||
var root = require('./config').url.host || '';
|
||||
var m;
|
||||
var extUrls = function(obj) {
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (key == 'link') {
|
||||
m = obj[key].match(reg);
|
||||
if (m && m[1] !== root) {
|
||||
obj.ext = true;
|
||||
}
|
||||
} else if (typeof obj[key] === 'object') {
|
||||
extUrls(obj[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function load() {
|
||||
fs.readFile(__dirname + '/data/welcome-panel.json', 'utf8', function (error, data) {
|
||||
if (error) {
|
||||
console.error('failed to load welcome-panel.json');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
info = JSON.parse(data);
|
||||
extUrls(info);
|
||||
} catch (e) {
|
||||
console.error('failed to parse welcome-panel.json');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
load();
|
||||
|
||||
process.on('SIGHUP', load);
|
||||
|
||||
module.exports = {
|
||||
getData: function () {
|
||||
return info;
|
||||
}
|
||||
};
|
||||
11
package.json
11
package.json
@ -9,7 +9,7 @@
|
||||
"name": "jsbin",
|
||||
"description": "Collaborative JavaScript Debugging App",
|
||||
"main": "./lib/app",
|
||||
"version": "3.13.24",
|
||||
"version": "3.15.7",
|
||||
"preferGlobal": "true",
|
||||
"homepage": "http://jsbin.com",
|
||||
"bin": "./bin/jsbin",
|
||||
@ -29,13 +29,12 @@
|
||||
"sqlite3": "~2.2.0",
|
||||
"nodemailer": "0.3.20",
|
||||
"commander": "1.0.0",
|
||||
"less": "~1.4.2",
|
||||
"less": "~1.7.3",
|
||||
"jade": "0.26.3",
|
||||
"stylus": "0.28.2",
|
||||
"flatten.js": "0.1.0",
|
||||
"soak": "0.3.0",
|
||||
"async": "~0.1.22",
|
||||
"lynx": "0.0.11",
|
||||
"lynx": "~0.1.1",
|
||||
"file-db": "0.0.2",
|
||||
"underscore": "~1.4.4",
|
||||
"passport": "~0.1.17",
|
||||
@ -54,7 +53,9 @@
|
||||
"express-cookie-blacklist": "~2.0.0",
|
||||
"passport-dropbox-oauth2": "~0.1.5",
|
||||
"handlebars": "~2.0.0-alpha.2",
|
||||
"dropbox": "~0.10.2"
|
||||
"dropbox": "~0.10.2",
|
||||
"lz-string": "~1.3.3",
|
||||
"stylus": "^0.47.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.1",
|
||||
|
||||
@ -75,10 +75,16 @@ form input[type="submit"] {
|
||||
form input[type="submit"]:hover {
|
||||
/*background: linear-gradient(0deg, #e8e8e8, #f8f8f8);*/
|
||||
}
|
||||
form input {
|
||||
form input,
|
||||
form textarea {
|
||||
font-size: 16px;
|
||||
}
|
||||
form input {
|
||||
position: relative;
|
||||
}
|
||||
form textarea {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
width: 50px;
|
||||
@ -143,6 +149,30 @@ form label.mini-desc a {
|
||||
font-weight: normal;
|
||||
margin-right: 0;
|
||||
}
|
||||
span.mini-desc {
|
||||
color: #979A99;
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
}
|
||||
span.mini-desc a {
|
||||
color: #979A99;
|
||||
}
|
||||
.mini-desc-error {
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
background: #FF4136;
|
||||
color: #fff;
|
||||
padding: 5px 10px;
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 100ms ease-out;
|
||||
-ms-transition: opacity 100ms ease-out;
|
||||
transition: opacity 100ms ease-out;
|
||||
}
|
||||
.mini-desc-error.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.loginFeedback {
|
||||
display: none;
|
||||
@ -154,4 +184,39 @@ form label.mini-desc a {
|
||||
white-space: normal;
|
||||
line-height: 18px;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.hintOptions {
|
||||
font-family: SourceCodeProRegular, Menlo, Monaco, consolas, monospace !important;
|
||||
font-size: 16px;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
|
||||
.hintOptWrapper {
|
||||
display: inline;
|
||||
width: 400px;
|
||||
margin-left: 1em;
|
||||
}
|
||||
.hintOptWrapper summary {
|
||||
width: 100px;
|
||||
display: inline;
|
||||
outline: none;
|
||||
color: #979A99;
|
||||
cursor: pointer;
|
||||
}
|
||||
.hintOptWrapper summary + div {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
#jshint, #csshint, #htmlhint, #coffeescripthint {
|
||||
vertical-align: top;
|
||||
margin-top: 6px;
|
||||
}
|
||||
form label.hintOptions {
|
||||
width: 400px;
|
||||
display: block;
|
||||
float: none;
|
||||
text-align: left;
|
||||
margin-top: -8px;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
@ -200,13 +200,14 @@ header nav a {
|
||||
|
||||
header nav a.selected:before {
|
||||
position: absolute;
|
||||
top: 42px;
|
||||
top: 45px; /* 42px */
|
||||
left: 35%;
|
||||
/* border-left: solid 1px #ccc; */
|
||||
/* border-top: solid 1px #ccc; */
|
||||
width: 1.1em;
|
||||
height: 1.1em;
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
background: #fff;
|
||||
content: '';
|
||||
}
|
||||
@ -253,6 +254,8 @@ article p {
|
||||
|
||||
nav {
|
||||
float: right;
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.button.action {
|
||||
@ -735,4 +738,100 @@ span.status {
|
||||
|
||||
span.status.show {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Tip flash messages */
|
||||
/* Slightly changed from the one in style.css */
|
||||
#tip {
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: -moz-none; /* if this is "none" you can't copy text */
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
#tip {
|
||||
color: #000;
|
||||
z-index: 100;
|
||||
display: none;
|
||||
border-top: 1px solid #ccc;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
background: #fdfece;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 2px 10px 2px 10px;
|
||||
-webkit-animation: tip-flash 100ms linear 4 alternate;
|
||||
-moz-animation: tip-flash 100ms linear 4 alternate;
|
||||
-ms-animation: tip-flash 100ms linear 4 alternate;
|
||||
-o-animation: tip-flash 100ms linear 4 alternate;
|
||||
animation: tip-flash 100ms linear 4 alternate;
|
||||
|
||||
-webkit-transition: bottom 100ms linear;
|
||||
transition: bottom 100ms linear;
|
||||
}
|
||||
|
||||
@-webkit-keyframes tip-flash {
|
||||
to {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
@-moz-keyframes tip-flash {
|
||||
to {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
@-ms-keyframes tip-flash {
|
||||
to {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
@-o-keyframes tip-flash {
|
||||
to {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
@keyframes tip-flash {
|
||||
to {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
#tip.error {
|
||||
background: #FF4136;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.error a {
|
||||
color: #fff;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
#tip.notification,
|
||||
#tip.error {
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
line-height: 28px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
#tip p {
|
||||
margin: 5px 25px 5px 0;
|
||||
padding: 0 65px 0 0;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
#tip a.dismiss {
|
||||
line-height: 28px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 2px;
|
||||
text-decoration: none;
|
||||
text-shadow: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.showtip #tip {
|
||||
display: block;
|
||||
}
|
||||
@ -19,3 +19,25 @@ div {
|
||||
.fake-dropdown:after {
|
||||
content: url(/images/down-arrow.png);
|
||||
}
|
||||
|
||||
/* toppanel */
|
||||
a.toppanel-logo {
|
||||
position: relative;
|
||||
left: -10px;
|
||||
top: 237px;
|
||||
width: 20px;
|
||||
}
|
||||
.toppanel a.toppanel-logo {
|
||||
position: relative;
|
||||
left: 65px;
|
||||
top: 0;
|
||||
width: 100px;
|
||||
}
|
||||
.toppanel-button-dropdown:after {
|
||||
text-align: center;
|
||||
}
|
||||
.toppanel-button-disabled {
|
||||
opacity: 0.5;
|
||||
filter: alpha(opacity=50);
|
||||
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
|
||||
}
|
||||
|
||||
@ -1452,6 +1452,13 @@ THE SOFTWARE.
|
||||
/* =============================================================================
|
||||
JS Bin
|
||||
========================================================================== */
|
||||
|
||||
/* Common */
|
||||
.cm-s-solarized .CodeMirror-linenumber {
|
||||
color: rgba(88, 110, 117, 0.3);
|
||||
}
|
||||
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
@ -1590,7 +1597,7 @@ textarea:focus {
|
||||
}
|
||||
|
||||
|
||||
#control, .label, #tip {
|
||||
#control, .label, #tip, #toppanel {
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: -moz-none; /* if this is "none" you can't copy text */
|
||||
@ -2898,6 +2905,20 @@ input {
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
/*@media (max-height: 770px) {*/
|
||||
.hideheader.ready #control {
|
||||
border-bottom: 6px solid #AAA;
|
||||
-webkit-transition: top ease-out 100ms, border-bottom ease-out 100ms;
|
||||
-moz-transition: top ease-out 100ms, border-bottom ease-out 100ms;
|
||||
-o-transition: top ease-out 100ms, border-bottom ease-out 100ms;
|
||||
transition: top ease-out 100ms, border-bottom ease-out 100ms;
|
||||
-webkit-transition-delay: 1s;
|
||||
-moz-transition-delay: 1s;
|
||||
-o-transition-delay: 1s;
|
||||
transition-delay: 1s;
|
||||
top: -35px;
|
||||
}
|
||||
|
||||
/*#loginFeedback:empty {
|
||||
display: none;
|
||||
}
|
||||
@ -4197,8 +4218,11 @@ pre .highlight:last-of-type {
|
||||
.embed #live .options {
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
opacity: 0;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
opacity: 0.3;
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
-webkit-transition: opacity ease-in 50ms;
|
||||
-moz-transition: opacity ease-in 50ms;
|
||||
-o-transition: opacity ease-in 50ms;
|
||||
@ -4209,6 +4233,18 @@ pre .highlight:last-of-type {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.embed #live .label {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.embed .blog {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.embed .options a {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.embed #live iframe {
|
||||
padding-top: 0;
|
||||
}
|
||||
@ -4356,7 +4392,8 @@ html * {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.CodeMirror-activeline-background:before {
|
||||
.CodeMirror-activeline-background:before,
|
||||
.line-highlight:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
background: inherit;
|
||||
@ -4419,6 +4456,10 @@ html * {
|
||||
#panels {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.embed #panels {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* hover card styles */
|
||||
@ -4969,4 +5010,488 @@ tbody:hover .snapshot1 .url a span:before {
|
||||
|
||||
#share .direct-links a {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.CodeMirror-highlight-line,
|
||||
.CodeMirror-highlight-line .CodeMirror-linenumber,
|
||||
.CodeMirror-highlight-line-background {
|
||||
background: rgba(255, 255, 204, 0.6);
|
||||
}
|
||||
|
||||
/* toppanel status */
|
||||
#toppanel {
|
||||
height: 200px;
|
||||
background: #ecf2fa;
|
||||
position: relative;
|
||||
z-index: 100000;
|
||||
border-bottom: rgb(191, 191, 191) solid 1px;
|
||||
margin-top: -200px;
|
||||
}
|
||||
.toppanel #toppanel {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.toppanel #bin,
|
||||
.toppanel #history,
|
||||
.toppanel #history .preview {
|
||||
top: 236px;
|
||||
}
|
||||
|
||||
#toppanel ~ #control .brand {
|
||||
margin-left: 34px;
|
||||
}
|
||||
#toppanel ~ #control .brand img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.toppanel {
|
||||
background-position: center calc((100% - 250px) / 2 + 256px);
|
||||
}
|
||||
body.ready.toppanel {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.toppanel #tip.notification,
|
||||
.toppanel #tip.error {
|
||||
top: 234px;
|
||||
}
|
||||
|
||||
/* toppanel */
|
||||
.toppanel-wrapper {
|
||||
display: block !important;
|
||||
color: #232323;
|
||||
font-size: 0;
|
||||
padding: 10px;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.toppanel-column {
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
padding: 0 4px;
|
||||
vertical-align: top;
|
||||
max-width: 220px;
|
||||
width: 16.66%;
|
||||
height: 179px;
|
||||
overflow: hidden;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@media (max-width: 1223px) { /* 5 */
|
||||
.toppanel-column {
|
||||
width: calc((100% - 217px) / 4);
|
||||
max-width: 100%;
|
||||
}
|
||||
.toppanel-column:nth-child(n+6) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 1023px) { /* 4 */
|
||||
.toppanel-column {
|
||||
width: calc((100% - 217px) / 3);
|
||||
}
|
||||
.toppanel-column:nth-child(n+5) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 816px) { /* 3 */
|
||||
.toppanel-column {
|
||||
width: calc((100% - 217px) / 2);
|
||||
}
|
||||
.toppanel-column:nth-child(n+4) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 616px) { /* 2 */
|
||||
.toppanel-column {
|
||||
width: calc(100% - 217px);
|
||||
}
|
||||
.toppanel-column:nth-child(n+3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 416px) { /* 1 */
|
||||
.toppanel-column:nth-child(n+2) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.toppanel-column-first {
|
||||
padding-right: 10px;
|
||||
overflow: visible;
|
||||
width: 197px;
|
||||
}
|
||||
.toppanel-section + .toppanel-section {
|
||||
margin-top: 13px;
|
||||
}
|
||||
.toppanel-actions {
|
||||
position: relative;
|
||||
top: -12px;
|
||||
z-index: 0;
|
||||
}
|
||||
.toppanel-actions-alone {
|
||||
top: 0;
|
||||
}
|
||||
.toppanel-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
.toppanel-title a:after {
|
||||
content: " »";
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.toppanel-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.toppanel-wrapper a {
|
||||
color: #6293D3;
|
||||
display: block;
|
||||
padding: 6px 10px 6px 1px;
|
||||
text-decoration: none;
|
||||
text-shadow: none;
|
||||
}
|
||||
.toppanel-wrapper a:hover {
|
||||
background: #CCC;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
a.toppanel-twitter-link {
|
||||
display: inline;
|
||||
}
|
||||
.toppanel-bin-name {
|
||||
width: 5em;
|
||||
display: inline-block;
|
||||
}
|
||||
.toppanel-bin-date {
|
||||
color: #888;
|
||||
}
|
||||
.toppanel-quote {
|
||||
line-height: 1.4;
|
||||
}
|
||||
.toppanel-quote-author {
|
||||
color: #888;
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
}
|
||||
div:first-child > .toppanel-button {
|
||||
position: static;
|
||||
}
|
||||
.toppanel-button {
|
||||
border: 1px solid #6293D3;
|
||||
line-height: 25px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
width: 150px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
.toppanel-button-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
.toppanel-button-dropdown:after {
|
||||
border-left: 1px solid #6293D3;
|
||||
content: "▾";
|
||||
font-size: 18px;
|
||||
line-height: 40px;
|
||||
position: absolute;
|
||||
width: 28px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.toppanel-button-disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
a.toppanel-hide {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
padding: 0 5px 7px;
|
||||
font-size: 35px;
|
||||
line-height: 0.7;
|
||||
color: #6293D3;
|
||||
color: rgba(98, 147, 211, 0.5);
|
||||
font-weight: 200;
|
||||
}
|
||||
.toppanel-actions .btn-github {
|
||||
font-size: 70%;
|
||||
width: 163px;
|
||||
margin: 0 auto;
|
||||
color: #232323;
|
||||
}
|
||||
.toppanel-actions .btn-github:hover {
|
||||
background: linear-gradient(0deg, #e8e8e8, #f8f8f8);
|
||||
}
|
||||
|
||||
a.toppanel-logo {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
padding: 4px 6px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
-webkit-transform: translate(-94px, 112px) scale(0.25);
|
||||
-moz-transform: translate(-94px, 112px) scale(0.25);
|
||||
-ms-transform: translate(-94px, 112px) scale(0.25);
|
||||
-o-transform: translate(-94px, 112px) scale(0.25);
|
||||
transform: translate(-94px, 112px) scale(0.25);
|
||||
-webkit-transform-origin: 100% 100%;
|
||||
-moz-transform-origin: 100% 100%;
|
||||
-ms-transform-origin: 100% 100%;
|
||||
-o-transform-origin: 100% 100%;
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
a.toppanel-logo:hover {
|
||||
background: transparent;
|
||||
border: 0 none;
|
||||
}
|
||||
.toppanel-logo img {
|
||||
border: 0 none;
|
||||
width: 100%;
|
||||
}
|
||||
.toppanel .toppanel-logo {
|
||||
cursor: default;
|
||||
-webkit-transform: translate(35px, -8px) scale(1);
|
||||
-moz-transform: translate(35px, -8px) scale(1);
|
||||
-ms-transform: translate(35px, -8px) scale(1);
|
||||
-o-transform: translate(35px, -8px) scale(1);
|
||||
transform: translate(35px, -8px) scale(1);
|
||||
}
|
||||
|
||||
/* toppanel animation */
|
||||
#bin {
|
||||
-webkit-transition: top ease-in-out 100ms;
|
||||
-moz-transition: top ease-in-out 100ms;
|
||||
-o-transition: top ease-in-out 100ms;
|
||||
transition: top ease-in-out 100ms;
|
||||
}
|
||||
#toppanel {
|
||||
-webkit-transition: margin-top ease-in-out 100ms;
|
||||
-moz-transition: margin-top ease-in-out 100ms;
|
||||
-o-transition: margin-top ease-in-out 100ms;
|
||||
transition: margin-top ease-in-out 100ms;
|
||||
}
|
||||
a.toppanel-logo {
|
||||
-webkit-transition: -webkit-transform linear 120ms 120ms;
|
||||
-moz-transition: -moz-transform linear 120ms 120ms;
|
||||
-o-transition: -o-transform linear 120ms 120ms;
|
||||
transition: transform linear 120ms 120ms;
|
||||
}
|
||||
/* slow to remove */
|
||||
.toppanel-slow #bin,
|
||||
.toppanel-slow #toppanel,
|
||||
.toppanel-slow a.toppanel-logo {
|
||||
-webkit-transition-duration: 5s;
|
||||
-moz-transition-duration: 5s;
|
||||
-o-transition-duration: 5s;
|
||||
transition-duration: 5s;
|
||||
}
|
||||
|
||||
.toppanel-close #bin,
|
||||
.toppanel-close #toppanel {
|
||||
-webkit-transition-delay: 120ms;
|
||||
-moz-transition-delay: 120ms;
|
||||
-o-transition-delay: 120ms;
|
||||
transition-delay: 120ms;
|
||||
-webkit-transition-duration: 100ms;
|
||||
-moz-transition-duration: 100ms;
|
||||
-o-transition-duration: 100ms;
|
||||
transition-duration: 100ms;
|
||||
}
|
||||
.toppanel-close a.toppanel-logo {
|
||||
-webkit-transition-delay: 0ms;
|
||||
-moz-transition-delay: 0ms;
|
||||
-o-transition-delay: 0ms;
|
||||
transition-delay: 0ms;
|
||||
-webkit-transition-duration: 120ms;
|
||||
-moz-transition-duration: 120ms;
|
||||
-o-transition-duration: 120ms;
|
||||
transition-duration: 120ms;
|
||||
}
|
||||
|
||||
|
||||
/* CodeMirror lint */
|
||||
/* The lint marker gutter */
|
||||
.CodeMirror-lint-markers {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-tooltip {
|
||||
background-color: infobackground;
|
||||
border: 1px solid black;
|
||||
border-radius: 4px 4px 4px 4px;
|
||||
color: infotext;
|
||||
font-family: monospace;
|
||||
font-size: 10pt;
|
||||
overflow: hidden;
|
||||
padding: 2px 5px;
|
||||
position: fixed;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
z-index: 100;
|
||||
max-width: 600px;
|
||||
opacity: 0;
|
||||
transition: opacity .4s;
|
||||
-moz-transition: opacity .4s;
|
||||
-webkit-transition: opacity .4s;
|
||||
-o-transition: opacity .4s;
|
||||
-ms-transition: opacity .4s;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
|
||||
background-position: left bottom;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-mark-error {
|
||||
background-image:
|
||||
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
|
||||
;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-mark-warning {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
|
||||
}
|
||||
|
||||
.lint-icon-warning:before,
|
||||
.lint-icon-error:before,
|
||||
.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
|
||||
padding-left: 18px;
|
||||
background-position: top left;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.lint-icon-error:before,
|
||||
.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
|
||||
}
|
||||
|
||||
.lint-icon-warning:before,
|
||||
.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
|
||||
}
|
||||
|
||||
.CodeMirror-lint-marker-multiple {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right bottom;
|
||||
width: 100%; height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.lint-icon-warning:before,
|
||||
.lint-icon-error:before {
|
||||
content: '';
|
||||
}
|
||||
.lint-icon-warning.dis:before,
|
||||
.lint-icon-error.dis:before {
|
||||
filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale"); /* Firefox 10+, Firefox on Android */
|
||||
filter: gray; /* IE6-9 */
|
||||
-webkit-filter: grayscale(100%);
|
||||
}
|
||||
.lint-icon-error.dis:before {
|
||||
-webkit-filter: grayscale(100%) invert(85%);
|
||||
}
|
||||
.lint-error {
|
||||
background: #FFA;
|
||||
font-size: 80%;
|
||||
color: #A00;
|
||||
padding: 2px 5px 3px;
|
||||
}
|
||||
.console-wrapper {
|
||||
background: #FFF;
|
||||
color: #333;
|
||||
}
|
||||
.console-log-head {
|
||||
background: #ecf2fa;
|
||||
border: 1px solid #CCC;
|
||||
border-top: 0 none;
|
||||
color: #6293d3;
|
||||
cursor: pointer;
|
||||
float: none;
|
||||
font-size: 13px;
|
||||
height: auto;
|
||||
line-height: 19px;
|
||||
margin: 0;
|
||||
padding: 0.2em 0.2em 0.2em 1em;
|
||||
width: auto;
|
||||
}
|
||||
.console-log {
|
||||
border: 1px solid #CCC;
|
||||
border-top: 0 none;
|
||||
font-size: 13px;
|
||||
height: 10.5em;
|
||||
overflow: auto;
|
||||
}
|
||||
.console-log-line {
|
||||
border-top: 1px solid #CCC;
|
||||
cursor: pointer;
|
||||
padding: 0.2em;
|
||||
}
|
||||
.console-log-line:first-child {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.console-log-line:before {
|
||||
display: inline-block;
|
||||
min-width: 1em;
|
||||
padding-right: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
#console-log-line-selected {
|
||||
background: #AFF;
|
||||
}
|
||||
|
||||
/* embed tweaks */
|
||||
.embed #control {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.embed #runwithalerts {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.embed #bin {
|
||||
top: 28px;
|
||||
}
|
||||
|
||||
.embed .dropdownmenu a,
|
||||
.embed .button {
|
||||
height: 18px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.embed #panels {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.embed #panels .button {
|
||||
border-radius: 0;
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
height: 18px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.embed .menu .brand {
|
||||
margin-left: 0;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.embed .menu .brand img {
|
||||
margin-top: 2px;
|
||||
}
|
||||
104
public/css/upgrade.css
Normal file
104
public/css/upgrade.css
Normal file
@ -0,0 +1,104 @@
|
||||
body {
|
||||
font-family: 'helvetica neue', sans-serif;
|
||||
font-weight:200!important;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight:300;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight:400;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1;
|
||||
width: 80%;
|
||||
border-collapse: collapse;
|
||||
margin-left:10%;
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
border-top: 1px solid rgb(240, 240, 240);
|
||||
}
|
||||
|
||||
tr.headings th {
|
||||
vertical-align: bottom;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align:center;
|
||||
line-height:30px;
|
||||
font-size:16px;
|
||||
font-weight:300;
|
||||
width:160px;
|
||||
}
|
||||
|
||||
td.feature {
|
||||
text-align:left;
|
||||
width: auto;
|
||||
min-width:230px;
|
||||
}
|
||||
|
||||
.coupon-btn {
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
#coupon_code {
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
.backers {
|
||||
width:720px;
|
||||
margin:0 auto;
|
||||
}
|
||||
.backers a {
|
||||
|
||||
}
|
||||
#body .backers .backer-img {
|
||||
width:200px;
|
||||
margin:20px auto;
|
||||
border:none;
|
||||
}
|
||||
/*
|
||||
button.stripe-button-el {
|
||||
font-size: 1.4em;
|
||||
font-weight: 200;
|
||||
background: #f0f0f0;
|
||||
background: linear-gradient(0deg, #f0f0f0, #fefefe);
|
||||
box-shadow: 0px 1px 1px #eee;
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 2px;
|
||||
padding: 10px 12px;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
border-bottom: 2px solid #aaa;
|
||||
}
|
||||
button.stripe-button-el:hover {
|
||||
background: linear-gradient(0deg, #e8e8e8, #f8f8f8);
|
||||
}
|
||||
*/
|
||||
|
||||
th small {
|
||||
color: rgb(165, 164, 164);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/*tr.anon td.anon:after, tr.anon td.free:after, tr.anon td.pro:after {
|
||||
content: "\2713";
|
||||
}
|
||||
tr.free td.free:after, tr.free td.pro:after {
|
||||
content: "\2713";
|
||||
}
|
||||
tr.pro td.pro:after {
|
||||
content: "\2713";
|
||||
}*/
|
||||
@ -6,7 +6,7 @@
|
||||
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/normalize/2.1.0/normalize.css">
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
|
||||
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.3.0.js"></script>
|
||||
<script src="http://builds.emberjs.com/tags/v1.5.1/ember.js"></script>
|
||||
<script src="http://builds.emberjs.com/tags/v1.6.0/ember.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
43
public/images/dave.svg
Normal file
43
public/images/dave.svg
Normal file
@ -0,0 +1,43 @@
|
||||
<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<g id="svg_1" transform="translate(72, 20)">
|
||||
<path id="svg_2" d="m8.787,159.65099l0,147.51601c0,28.61899 53.37401,51.82199 119.21401,51.82199c65.841,0 119.214,-23.203 119.214,-51.82199l0,-147.51601c0,0 -4.95901,50.373 -119.214,50.373c-114.25701,0.00101 -119.21401,-50.373 -119.21401,-50.373z" fill="#9EAAB2"/>
|
||||
<ellipse id="svg_3" ry="51.818" rx="119.214" cy="159.651" cx="128.001" fill="#8E979A"/>
|
||||
<path id="svg_4" d="m236.838,156.95399c0,4.564 -1.96799,8.67799 -5.599,12.77899c-7.185,8.11801 -54.739,26.93901 -102.5,26.97902c-48.25101,0.04199 -96.71501,-18.77901 -103.97701,-26.97902c-3.63799,-4.10399 -5.598,-8.209 -5.598,-12.77899c0,-22.71901 48.729,-41.13599 108.83701,-41.13599c60.10802,0.00099 108.83701,18.41698 108.83701,41.13599z" fill="#1A2B2F"/>
|
||||
<g id="svg_5" class="hide">
|
||||
<path id="svg_6" d="m24.761,107.832c17.174,11.981 60.859,23.16399 103.23899,23.16399c42.38001,0 88.46203,-11.58199 103.23803,-23.16399l0,-7.18901c0,0 -32.38202,20.72 -103.23803,20.72c-70.85799,0 -103.23899,-20.72 -103.23899,-20.72l0,7.18901z" fill="#1B3134"/>
|
||||
<path id="svg_7" d="m128.00101,119.25401c-68.99001,0 -125.22301,-21.459 -127.89101,-48.32401c-0.066,0.662 -0.109,1.328 -0.109,1.996c0,27.793 57.308,50.321 128.00001,50.321c70.692,0 127.99997,-22.528 127.99997,-50.321c0,-0.668 -0.04298,-1.334 -0.10997,-1.996c-2.66699,26.86501 -58.89999,48.32401 -127.89,48.32401z" fill="#8E979A"/>
|
||||
<path id="svg_8" d="m231.239,165.562c0,0 -20.98901,29.55299 -103.23801,29.55299c-82.251,0 -103.23901,-29.55299 -103.23901,-29.55299l0,-64.16699c0,0 29.719,20.23601 100.80901,20.23601c71.08901,0 105.66901,-20.23601 105.66901,-20.23601l0,64.16699l-0.00101,0z" fill="#213F48"/>
|
||||
<path id="svg_9" d="m128.00101,0c-70.69201,0 -128.00001,22.53 -128.00001,50.322c0,7.642 0,14.82301 0,21.96601c0,27.78999 57.308,50.31999 128.00001,50.31999c70.692,0 127.99997,-22.53 127.99997,-50.31999c0,-7.87601 0,-14.37901 0,-21.96601c0,-27.792 -57.30797,-50.322 -127.99997,-50.322z" fill="#9EAAB2"/>
|
||||
<path id="svg_10" d="m127.738,16.975c18.91702,0 36.344,2.599 49.566,6.879l18.944,-6.879l4.58701,19.51801l-57.217,0l-0.729,-0.148l20.342,-7.383c-9.35699,-3.127 -21.84,-5.039 -35.44001,-5.039c-10.89999,0 -21.11299,1.23801 -29.55699,3.355l-18.79401,-3.816c13.183,-4.106 30.194,-6.487 48.298,-6.487zm-73.982,17.971c0.635,-2.843 7.603,-5.11 15.575,-5.11c7.97,0 14.132,2.268 13.758,5.11c-0.38,2.894 -7.37,5.278 -15.604,5.278c-8.233,0.001 -14.376,-2.384 -13.729,-5.278zm74.3,36.05701c-22.30102,0 -42.05502,-3.54601 -55.67001,-9.10801l-25.09,9.10801l5.203,-23.77501l59.869,0l0.272,0.05901l-23.11501,8.389c9.561,3.757 23.19501,6.13499 38.459,6.13499c12.54401,0 23.90801,-1.591 32.80102,-4.22l21.90099,4.69c-13.29999,5.27001 -32.50098,8.72201 -54.62999,8.72201zm62.16101,-16.366c-8.599,0 -15.94701,-2.602 -16.405,-5.753c-0.448,-3.096 5.933,-5.562 14.241,-5.562c8.312,0 15.63701,2.466 16.37001,5.562c0.74698,3.151 -5.60802,5.753 -14.20601,5.753z" fill="#DBE1E3" clip-rule="evenodd" fill-rule="evenodd"/>
|
||||
<path id="svg_11" d="m196.15601,155.806c0,9.787 -7.16,17.72301 -16.979,17.72301c-9.81799,0 -16.978,-7.936 -16.978,-17.72301c0,-9.789 7.16,-17.722 16.978,-17.722c9.819,0 16.979,7.933 16.979,17.722z" fill="#1B3134" clip-rule="evenodd" fill-rule="evenodd"/>
|
||||
<circle id="svg_12" r="15.597" cy="152.28" cx="179.178" fill="#51C3F1" clip-rule="evenodd" fill-rule="evenodd"/>
|
||||
<g id="svg_13">
|
||||
<path id="svg_14" d="m93.398,155.806c0,9.787 -7.16,17.72301 -16.979,17.72301c-9.817,0 -16.979,-7.936 -16.979,-17.72301c0,-9.789 7.161,-17.722 16.979,-17.722c9.819,0 16.979,7.933 16.979,17.722z" fill="#1B3134" clip-rule="evenodd" fill-rule="evenodd"/>
|
||||
<circle id="svg_15" r="15.596" cy="152.28" cx="76.575" fill="#51C3F1" clip-rule="evenodd" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<path id="svg_16" d="m216.86101,108.50201l0,-24.414c0,0 34.646,-10.649 39.13997,-33.766c0,7.642 0,14.82301 0,21.96601c0,14.22098 -15.01097,27.06299 -39.13997,36.21399z" fill="#8E979A"/>
|
||||
<path id="svg_17" d="m39.139,108.50201l0,-24.681c-27.257,-8.519 -37.908,-23.96301 -39.139,-33.499c0,7.642 0,14.82301 0,21.96601c0.001,14.22098 15.011,27.06299 39.139,36.21399z" fill="#8E979A"/>
|
||||
<path id="svg_18" d="m128.00101,96.65001c-68.99001,0 -125.22301,-21.459 -127.89101,-48.32401c-0.066,0.663 -0.109,1.327 -0.109,1.997c0,27.79102 57.308,50.32001 128.00001,50.32001c70.692,0 127.99997,-22.52899 127.99997,-50.32001c0,-0.67 -0.04298,-1.334 -0.10997,-1.997c-2.66699,26.86501 -58.89999,48.32401 -127.89,48.32401z" fill="#DBE1E3"/>
|
||||
<circle id="svg_19" r="5.088" cy="149.872" cx="179.177" fill="#FFFFFF" clip-rule="evenodd" fill-rule="evenodd"/>
|
||||
<circle id="svg_20" r="5.088" cy="149.872" cx="76.42" fill="#FFFFFF" clip-rule="evenodd" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<path id="svg_21" d="m8.787,159.65099l0,147.51601c0,28.61899 53.37401,51.82199 119.21401,51.82199c65.841,0 119.214,-23.203 119.214,-51.82199l0,-147.51601c0,0 -4.95901,50.373 -119.214,50.373c-114.25701,0.00101 -119.21401,-50.373 -119.21401,-50.373z" fill="#9EAAB2"/>
|
||||
<path id="svg_22" d="m127.96101,191.95599c-51.834,-0.008 -94.681,-15.004 -103.199,-32.30501l0,10.082c0,0 20.977,28.338 103.158,28.35802l0,0c0.01601,0 0.028,0 0.041,0c0.01401,0 0.02501,0 0.04002,0l0,0c82.18399,-0.02002 103.23801,-28.35802 103.23801,-28.35802l0,-10.082c-8.51804,17.30101 -51.44402,32.29701 -103.27802,32.30501z" fill="#1B3134"/>
|
||||
<path id="svg_23" d="m231.27101,184.228c3.36398,-2.575 6.64499,-5.15201 9.12399,-8.63901c1.423,-2.006 2.595,-4.14099 3.69601,-6.31599c1.21899,-2.119 2.26498,-4.311 3.12299,-6.57001l0,144.46501c0,13.26999 -11.48001,25.375 -30.353,34.54199l0,-148.78c5.09,-2.39801 9.94699,-5.28799 14.41,-8.702z" fill="#8E979A"/>
|
||||
<path id="svg_24" d="m24.731,184.228c-3.366,-2.575 -6.645,-5.15201 -9.124,-8.63901c-1.426,-2.006 -2.597,-4.14099 -3.69701,-6.31599c-1.219,-2.119 -2.264,-4.311 -3.123,-6.57001l0,144.46501c0,13.26999 11.48,25.375 30.352,34.54199l0,-148.78c-5.088,-2.39801 -9.946,-5.28799 -14.408,-8.702z" fill="#8E979A"/>
|
||||
<path id="svg_25" d="m128.00101,355.116c-64.21601,0 -116.55601,-21.45898 -119.03801,-48.32401c-0.062,0.66199 -0.103,1.328 -0.103,1.99399c0,27.793 53.341,50.32202 119.14101,50.32202c65.8,0 119.14101,-22.52902 119.14101,-50.32202c0,-0.66599 -0.03799,-1.332 -0.103,-1.99399c-2.483,26.86499 -54.82201,48.32401 -119.03801,48.32401z" fill="#8E979A"/>
|
||||
<path id="svg_26" d="m128.00101,207.974c-64.21601,0 -116.55601,-21.457 -119.03801,-48.32199c-0.062,0.66199 -0.103,1.32599 -0.103,1.99599c0,27.79201 53.341,50.32002 119.14101,50.32002c65.8,0 119.14101,-22.52802 119.14101,-50.32002c0,-0.67 -0.03799,-1.334 -0.103,-1.99599c-2.483,26.86499 -54.82201,48.32199 -119.03801,48.32199z" fill="#DBE1E3"/>
|
||||
<path id="svg_27" d="m128.00101,338.60699c-64.21601,0 -116.55601,-21.457 -119.03801,-48.32199c-0.062,0.664 -0.103,1.32599 -0.103,1.996c0,27.79199 53.341,50.31998 119.14101,50.31998c65.8,0 119.14101,-22.52798 119.14101,-50.31998c0,-0.67001 -0.03799,-1.332 -0.103,-1.996c-2.483,26.86499 -54.82201,48.32199 -119.03801,48.32199z" fill="#DBE1E3"/>
|
||||
<g id="svg_28">
|
||||
<path id="svg_29" d="m128.74001,327.302c-5.31901,0 -10.66801,-0.27899 -19.08201,-0.992c-3.94299,-0.33798 -7.153,-3.86301 -7.153,-7.867l0,-79.97299c0,-3.728 2.82401,-6.54099 6.57001,-6.54099c0.207,0 0.42099,0.00998 0.633,0.02798c8.382,0.71201 13.71899,0.991 19.03201,0.991c5.312,0 10.64799,-0.27899 19.034,-0.992c0.21001,-0.01801 0.423,-0.02698 0.63,-0.02698c3.744,0 6.57001,2.81198 6.57001,6.54099l0,79.97299c0,4.004 -3.21101,7.52902 -7.15401,7.867c-8.416,0.71298 -13.76401,0.992 -19.08,0.992zm-19.665,-91.379c-1.54199,0 -2.57599,1.022 -2.57599,2.547l0,79.97299c0,1.91202 1.63399,3.728 3.498,3.88702c8.29099,0.70297 13.54599,0.97897 18.743,0.97897c5.19398,0 10.44899,-0.27499 18.74199,-0.97897c1.86301,-0.15802 3.498,-1.97501 3.498,-3.88702l0,-79.97299c0,-1.617 -1.20299,-2.68399 -2.864,-2.53299c-8.511,0.72198 -13.942,1.004 -19.37599,1.004c-5.43501,0 -10.86701,-0.28201 -19.37501,-1.004c-0.09801,-0.01001 -0.19701,-0.01401 -0.29001,-0.01401z" fill="#DBE1E3"/>
|
||||
</g>
|
||||
<g id="svg_30">
|
||||
<path id="svg_31" d="m226.244,232.49298c-9.48099,6.14499 -27.129,17.582 -62.338,25.51599c5.05701,26.83801 5.05701,26.83801 11.70599,62.01401c12.18102,-3.625 37.52901,-11.17401 58.06499,-28.104c-2.34299,-19.66599 -4.22198,-34.961 -7.43298,-59.42599" fill="#F5DD3E"/>
|
||||
</g>
|
||||
<path id="svg_32" d="m203.304,303.173c-6.93901,3.828 -11.68201,-1.73401 -11.68201,-1.73401l4.097,-3.89301c0,0 2.285,2.94702 6.06,0.43402c1.76799,-1.17599 0.61501,-7.06601 0.61501,-7.06601l-3.394,-16.19501l5.34099,-2.25198c0,0 3.23801,18.70099 3.57602,20.57199c0.63199,3.50299 1.15298,6.95099 -4.61301,10.134z" fill="#3B3735"/>
|
||||
<path id="svg_33" d="m217.68901,294.991c-3.90799,0.59 -6.117,-2.32401 -6.117,-2.32401l3.56299,-4.36099c0,0 1.34302,1.48999 3.20801,1.211c1.582,-0.242 3.439,-1.29501 4.08701,-3.28302c1.104,-3.40399 0.08099,-4.56998 -3.38599,-4.18399c-3.46301,0.38699 -7.819,0.89502 -8.59003,-5.936c-0.68399,-6.05899 4.61301,-10.694 8.19901,-11.32401c3.55801,-0.62299 5.543,1.88901 5.543,1.88901l-3.548,4.30701c0,0 -2.35397,-1.07001 -3.76898,0.19699c-1.28702,1.155 -1.78201,2.66202 -1.29199,3.80701c0.42899,1.00299 1.72498,1.44501 3.806,1.29102c2.082,-0.15002 5.50998,-1.36102 7.62,2.88498c2.37299,4.76901 -0.099,14.435 -9.32401,15.82501z" fill="#3B3735"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.2 KiB |
@ -26,6 +26,14 @@
|
||||
html: null
|
||||
};
|
||||
|
||||
// default CodeMirror settings
|
||||
var cmDefaultSettings = {
|
||||
lineWrapping: true,
|
||||
theme: 'jsbin',
|
||||
indentUnit: 2,
|
||||
tabSize: 2
|
||||
};
|
||||
|
||||
// needed for the keymaps
|
||||
$.browser = {};
|
||||
// work out the browser platform
|
||||
@ -74,12 +82,19 @@
|
||||
'vim',
|
||||
'emacs',
|
||||
'sublime',
|
||||
'tern'
|
||||
'tern',
|
||||
'matchbrackets'
|
||||
];
|
||||
var $addons = {};
|
||||
|
||||
var $saveStatus = $('span.status');
|
||||
var saveTimer = null;
|
||||
|
||||
|
||||
// setup variables;
|
||||
var $saveStatus = $('span.status');
|
||||
var saveTimer = null;
|
||||
|
||||
var $textarea = $('textarea');
|
||||
var currentSettings = getCurrentSettings();
|
||||
if (currentSettings.editor === undefined) {
|
||||
@ -88,6 +103,7 @@
|
||||
if (currentSettings.addons === undefined) {
|
||||
currentSettings.addons = {};
|
||||
}
|
||||
currentSettings.editor = $.extend({}, cmDefaultSettings, currentSettings.editor);
|
||||
jsbin.settings = $.extend({}, currentSettings);
|
||||
|
||||
var editor = window.editor = CodeMirror.fromTextArea($textarea[0], $.extend({
|
||||
@ -137,7 +153,8 @@
|
||||
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('tabSize', parseInt($tabSize.val(), 10));
|
||||
editor.setOption('indentUnit', parseInt($tabSize.val(), 10));
|
||||
editor.setOption('theme', $theme.val());
|
||||
$CodeMirror.css('font-size', $fontsize.val()+'px');
|
||||
editor.refresh();
|
||||
@ -146,7 +163,7 @@
|
||||
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');
|
||||
@ -179,6 +196,9 @@
|
||||
}
|
||||
reloadAddons(tempAddonsKeys);
|
||||
|
||||
clearTimeout(saveTimer);
|
||||
$saveStatus.addClass('show');
|
||||
|
||||
// Save on server
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
@ -188,6 +208,13 @@
|
||||
_csrf: $csrf.val()
|
||||
},
|
||||
success: function() {
|
||||
clearTimeout(saveTimer);
|
||||
saveTimer = setTimeout(function () {
|
||||
$saveStatus.addClass('show');
|
||||
saveTimer = setTimeout(function () {
|
||||
$saveStatus.removeClass('show');
|
||||
}, 3000);
|
||||
}, 1000);
|
||||
if (console && console.log) {
|
||||
console.log('Success on saving settings');
|
||||
}
|
||||
@ -196,6 +223,11 @@
|
||||
if (console && console.log) {
|
||||
console.log('Error: ' + status);
|
||||
}
|
||||
},
|
||||
complete: function () {
|
||||
saveTimer = setTimeout(function () {
|
||||
$saveStatus.removeClass('show');
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -24,12 +24,27 @@
|
||||
|
||||
// Setup variables
|
||||
var $csrf = $('#_csrf');
|
||||
var hintShow = {
|
||||
console: true,
|
||||
line: false,
|
||||
// under: false,
|
||||
// tooltip: true,
|
||||
gutter: false
|
||||
};
|
||||
var currentSettings = {
|
||||
panels: [],
|
||||
includejs: true,
|
||||
focusedPanel: 'html',
|
||||
jshint: true,
|
||||
assetUrl: '',
|
||||
hintShow: hintShow,
|
||||
jshint: true,
|
||||
jshintOptions: '',
|
||||
csshint: false,
|
||||
csshintOptions: '',
|
||||
htmlhint: false,
|
||||
htmlhintOptions: '',
|
||||
coffeescripthint: false,
|
||||
coffeescripthintOptions: '',
|
||||
};
|
||||
var $saveStatus = $('span.status');
|
||||
var saveTimer = null;
|
||||
@ -39,20 +54,26 @@
|
||||
var $includejs = $('#includejs').prop('checked', currentSettings.includejs);
|
||||
var $focusedPanel = $('#focused-panel').val(currentSettings.focusedPanel);
|
||||
var $assetUrl = $('#asset-url').val(currentSettings.assetUrl);
|
||||
var hints = ['js'];
|
||||
var hints = ['js', 'css', 'html', 'coffeescript'];
|
||||
var $ssl = $('#ssl'); // .prop('checked', currentSettings.ssl); // checking happens server side
|
||||
var $hints = {};
|
||||
var $hintsOptions = {};
|
||||
var $hintsOptWrapper = {};
|
||||
var hintsOptionsVal = {};
|
||||
var $hintsOptError = {};
|
||||
var $hintShow = {};
|
||||
|
||||
var jshints = {
|
||||
'forin': 'About unsafe <code>for..in</code>',
|
||||
'eqnull': 'About <code>== null</code>',
|
||||
'noempty': 'About empty blocks',
|
||||
'eqeqeq': 'About unsafe comparisons',
|
||||
'boss': 'About assignments inside <code>if/for/...</code>',
|
||||
'undef': 'When variable is undefined',
|
||||
'unused': 'When variable is defined but not used',
|
||||
'curly': 'When blocks omit <code>{}</code>'
|
||||
};
|
||||
var source = '';
|
||||
// var jshints = {
|
||||
// 'forin': 'About unsafe <code>for..in</code>',
|
||||
// 'eqnull': 'About <code>== null</code>',
|
||||
// 'noempty': 'About empty blocks',
|
||||
// 'eqeqeq': 'About unsafe comparisons',
|
||||
// 'boss': 'About assignments inside <code>if/for/...</code>',
|
||||
// 'undef': 'When variable is undefined',
|
||||
// 'unused': 'When variable is defined but not used',
|
||||
// 'curly': 'When blocks omit <code>{}</code>'
|
||||
// };
|
||||
// var source = '';
|
||||
|
||||
for (var i = 0; i < panels.length; i++) {
|
||||
$panels[panels[i]] = $('#panel-' + panels[i])
|
||||
@ -60,22 +81,43 @@
|
||||
}
|
||||
|
||||
for (var m = 0; m < hints.length; m++) {
|
||||
$hintsOptWrapper[hints[m]] = $('.' + hints[m] + 'hintOptWrapper')
|
||||
.toggle(currentSettings[ hints[m] + 'hint' ]);
|
||||
$hints[hints[m]] = $('#' + hints[m] + 'hint')
|
||||
.prop('checked', currentSettings[ hints[m] + 'hint' ]);
|
||||
}
|
||||
.prop('checked', currentSettings[ hints[m] + 'hint' ])
|
||||
.on('click', { el: $hintsOptWrapper[hints[m]] }, function(event) {
|
||||
event.data.el.toggle(this.checked);
|
||||
});
|
||||
|
||||
for (var prop in jshints) {
|
||||
if (jshints.hasOwnProperty(prop)) {
|
||||
source += '<div><label for="' + prop + '">' + jshints[prop] + '</label>' +
|
||||
'<input id="' + prop + '" type="checkbox">' +
|
||||
'</div>';
|
||||
hintsOptionsVal[hints[m]] = JSON.stringify(currentSettings[ hints[m] + 'hintOptions'], undefined, 2);
|
||||
|
||||
if (hintsOptionsVal[hints[m]] === '{}' || !currentSettings[ hints[m] + 'hintOptions']) {
|
||||
hintsOptionsVal[hints[m]] = '';
|
||||
}
|
||||
$hintsOptions[hints[m]] = $('#' + hints[m] + 'hintOptions')
|
||||
.val(hintsOptionsVal[hints[m]]);
|
||||
$hintsOptError[hints[m]] = $('#' + hints[m] + 'hintOptError');
|
||||
}
|
||||
$hintShow = {};
|
||||
for (var key in hintShow) {
|
||||
if (hintShow.hasOwnProperty(key)) {
|
||||
$hintShow[key] = $('#hintShow-' + key).prop('checked', currentSettings.hintShow[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// for (var prop in jshints) {
|
||||
// if (jshints.hasOwnProperty(prop)) {
|
||||
// source += '<div><label for="' + prop + '">' + jshints[prop] + '</label>' +
|
||||
// '<input id="' + prop + '" type="checkbox">' +
|
||||
// '</div>';
|
||||
// }
|
||||
// }
|
||||
|
||||
// Listeners
|
||||
$(':checkbox').on('change', saveSettings);
|
||||
$('select').on('change', saveSettings);
|
||||
$('input').on('blur', saveSettings);
|
||||
$('input:not([type=checkbox])').on('blur', saveSettings);
|
||||
$('textarea').on('blur', saveSettings);
|
||||
|
||||
function saveSettings() {
|
||||
// Merge all our settings together
|
||||
@ -90,20 +132,36 @@
|
||||
|
||||
for (var m = 0; m < hints.length; m++) {
|
||||
localStorageSettings[ hints[m] + 'hint' ] = $hints[hints[m]].prop('checked');
|
||||
$hintsOptError[ hints[m] ].html('').removeClass('show');
|
||||
try {
|
||||
localStorageSettings[ hints[m] + 'hintOptions' ] = JSON.parse($hintsOptions[ hints[m] ].val() || '{}');
|
||||
} catch (e) {
|
||||
$hintsOptError[ hints[m] ].html(e).addClass('show');
|
||||
}
|
||||
}
|
||||
localStorageSettings.hintShow = {};
|
||||
for (var key in hintShow) {
|
||||
if (hintShow.hasOwnProperty(key)) {
|
||||
localStorageSettings.hintShow[key] = $hintShow[key].prop('checked');
|
||||
}
|
||||
}
|
||||
|
||||
localStorageSettings.includejs = $includejs.prop('checked');
|
||||
localStorageSettings.focusedPanel = $focusedPanel.val();
|
||||
localStorageSettings.assetUrl = $assetUrl.val();
|
||||
localStorageSettings.ssl = $ssl.prop('checked');
|
||||
|
||||
localStorage.settings = JSON.stringify(localStorageSettings);
|
||||
console.log(localStorageSettings);
|
||||
|
||||
clearTimeout(saveTimer);
|
||||
$saveStatus.addClass('show');
|
||||
|
||||
// Save on server
|
||||
$.ajax({
|
||||
beforeSend: function () {
|
||||
clearTimeout(saveTimer);
|
||||
$saveStatus.addClass('show');
|
||||
},
|
||||
url: 'editor',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
@ -124,7 +182,7 @@
|
||||
complete: function () {
|
||||
saveTimer = setTimeout(function () {
|
||||
$saveStatus.removeClass('show');
|
||||
}, 1000)
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
24
public/js/account/upgrade-page.js
Normal file
24
public/js/account/upgrade-page.js
Normal file
@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
/* globals $, backers */
|
||||
|
||||
var $backers = $('.backers');
|
||||
|
||||
backers.forEach(function (backer) {
|
||||
var $a = $('<a>');
|
||||
$a.attr('href', backer.url);
|
||||
var $img = $('<img>');
|
||||
$img.attr('src', backer.image).addClass('backer-img');
|
||||
$a.html($img);
|
||||
$backers.append($a);
|
||||
});
|
||||
|
||||
var $backers = $('.backers');
|
||||
|
||||
backers.forEach(function (backer) {
|
||||
var $a = $('<a>');
|
||||
$a.attr('href', backer.url);
|
||||
var $img = $('<img>');
|
||||
$img.attr('src', backer.image).addClass('backer-img');
|
||||
$a.html($img);
|
||||
$backers.append($a);
|
||||
});
|
||||
13
public/js/account/upgrade.js
Normal file
13
public/js/account/upgrade.js
Normal file
@ -0,0 +1,13 @@
|
||||
/* globals $ */
|
||||
(function () {
|
||||
|
||||
var $input = $('#coupon_code');
|
||||
var $form = $('#stripe_pro_month');
|
||||
var originalAction = $form.attr('action');
|
||||
|
||||
$input.on('change', function () {
|
||||
'use strict';
|
||||
$form.attr('action', originalAction + '?' + $input.val());
|
||||
});
|
||||
|
||||
}());
|
||||
@ -119,6 +119,16 @@ var analytics = {
|
||||
},
|
||||
runconsole: function (from) {
|
||||
analytics.track(from || 'button', 'run console');
|
||||
},
|
||||
welcomePanelState: function (state) {
|
||||
var s = 'close';
|
||||
if (state) {
|
||||
s = 'open';
|
||||
}
|
||||
analytics.track('state', 'welcome-panel', s);
|
||||
},
|
||||
welcomePanelLink: function (url) {
|
||||
analytics.track('welcome-panel-link', url);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
// if a gist has been requested, lazy load the gist library and plug it in
|
||||
if (/gist(\/.*)?\/\d+/.test(window.location.pathname) && (!sessionStorage.getItem('javascript') && !sessionStorage.getItem('html'))) {
|
||||
if (/gist\/.*/.test(window.location.pathname)) {
|
||||
window.editors = editors; // needs to be global when the callback triggers to set the content
|
||||
loadGist = function () {
|
||||
$.getScript(jsbin.static + '/js/chrome/gist.js', function () {
|
||||
window.gist = new Gist(window.location.pathname.replace(/.*?(\d+).*/, "$1"));
|
||||
});
|
||||
window.gist = new Gist(window.location.pathname.replace(/.*\/([^/]+)$/, "$1"));
|
||||
};
|
||||
|
||||
if (editors.ready) {
|
||||
@ -43,4 +41,4 @@ document.getElementsByTagName('head')[0].appendChild(link);
|
||||
|
||||
if (jsbin.embed) {
|
||||
analytics.embed();
|
||||
}
|
||||
}
|
||||
52
public/js/chrome/archive.js
Normal file
52
public/js/chrome/archive.js
Normal file
@ -0,0 +1,52 @@
|
||||
function archive(unarchive) {
|
||||
/*global jsbin, $, $document, analytics*/
|
||||
'use strict';
|
||||
var type = unarchive === false ? 'unarchive' : 'archive';
|
||||
var text = unarchive === false ? 'restore from archive' : 'archiving';
|
||||
analytics[type](jsbin.getURL());
|
||||
if (!jsbin.user.name) {
|
||||
$document.trigger('tip', {
|
||||
type: 'notication',
|
||||
content: 'You must be logged in and the owner of the bin to archive.'
|
||||
});
|
||||
} else if (jsbin.owner()) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: jsbin.getURL() + '/' + type,
|
||||
error: function () {
|
||||
$document.trigger('tip', {
|
||||
type: 'error',
|
||||
content: 'The ' + text + ' failed. If this continues, please can you file an issue?'
|
||||
});
|
||||
},
|
||||
success: function () {
|
||||
jsbin.state.metadata.archive = unarchive !== false;
|
||||
updateArchiveMenu();
|
||||
$document.trigger('tip', {
|
||||
type: 'notication',
|
||||
autohide: 5000,
|
||||
content: 'This bin is now ' + (unarchive === false ? 'restored from the archive.' : 'archived.')
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$document.trigger('tip', {
|
||||
type: 'notication',
|
||||
content: 'The ' + text + ' failed. You can only archive bins that you own.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateArchiveMenu() {
|
||||
if (jsbin.state.metadata && jsbin.state.metadata.archive) {
|
||||
$('a.archivebin').hide();
|
||||
$('a.unarchivebin').show();
|
||||
} else {
|
||||
$('a.archivebin').show();
|
||||
$('a.unarchivebin').hide();
|
||||
}
|
||||
}
|
||||
|
||||
updateArchiveMenu();
|
||||
|
||||
var unarchive = archive.bind(null, false);
|
||||
@ -2,7 +2,14 @@
|
||||
|
||||
var jshint = function () {
|
||||
var source = editors.javascript.editor.getCode();
|
||||
var ok = JSHINT(source);
|
||||
|
||||
// default jshint options
|
||||
var options = {
|
||||
'eqnull': true
|
||||
};
|
||||
|
||||
$.extend(options, jsbin.settings.jshintOptions || {});
|
||||
var ok = JSHINT(source, options);
|
||||
|
||||
return ok ? true : JSHINT.data();
|
||||
};
|
||||
|
||||
@ -21,7 +21,6 @@ var Gist = (function () { // jshint ignore:line
|
||||
$.get('https://api.github.com/gists/' + id + token, function (data) {
|
||||
if (!data) {return;}
|
||||
$.each(data.files, function (fileName, fileData) {
|
||||
console.log.apply(console, [].slice.call(arguments));
|
||||
var ext = fileName.split('.').slice(-1).join('');
|
||||
gist.code[ext] = fileData.content;
|
||||
});
|
||||
@ -38,7 +37,9 @@ var Gist = (function () { // jshint ignore:line
|
||||
panel = jsbin.panels.panels[target];
|
||||
if (!panel) {return;}
|
||||
processors.set(target, processorInit.id);
|
||||
jsbin.saveDisabled = true;
|
||||
panel.setCode(data);
|
||||
jsbin.saveDisabled = false;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -76,6 +76,7 @@
|
||||
if (jsbin.state.streaming) {
|
||||
if (window.EventSource && owner) {
|
||||
listenStats();
|
||||
handleVisibility();
|
||||
var url = jsbin.getURL();
|
||||
$document.on('saved', function () {
|
||||
var newurl = window.location.toString();
|
||||
@ -91,6 +92,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
function handleVisibility() {
|
||||
var hiddenProperty = 'hidden' in document ? 'hidden' :
|
||||
'webkitHidden' in document ? 'webkitHidden' :
|
||||
'mozHidden' in document ? 'mozHidden' :
|
||||
null;
|
||||
var visibilityStateProperty = 'visibilityState' in document ? 'visibilityState' :
|
||||
'webkitVisibilityState' in document ? 'webkitVisibilityState' :
|
||||
'mozVisibilityState' in document ? 'mozVisibilityState' :
|
||||
null;
|
||||
|
||||
if (visibilityStateProperty) {
|
||||
var visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange');
|
||||
document.addEventListener(visibilityChangeEvent, function visibilityChangeEvent() {
|
||||
if (document[hiddenProperty]) { // hidden
|
||||
es.close();
|
||||
} else {
|
||||
listenStats();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateStats(event, _data) {
|
||||
var data = _data ? JSON.parse(_data) : JSON.parse(event.data);
|
||||
|
||||
@ -117,8 +140,11 @@
|
||||
}
|
||||
|
||||
function listenStats() {
|
||||
es = new EventSource(jsbin.getURL() + '/stats?checksum=' + jsbin.state.checksum);
|
||||
es.addEventListener('stats', throttle(updateStats, 1000));
|
||||
if (window.EventSource && owner) {
|
||||
// TODO use pagevisibility api to close connection
|
||||
es = new EventSource(jsbin.getURL() + '/stats?checksum=' + jsbin.state.checksum);
|
||||
es.addEventListener('stats', throttle(updateStats, 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
49
public/js/chrome/last-bin.js
Normal file
49
public/js/chrome/last-bin.js
Normal file
@ -0,0 +1,49 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function getExpires() {
|
||||
var d = new Date();
|
||||
|
||||
// expires in 1 hour from now
|
||||
d.setTime(+d + 1000 * 60 * 60);
|
||||
return d.toUTCString();
|
||||
}
|
||||
|
||||
function save() {
|
||||
var url = jsbin.getURL(true) + '/edit';
|
||||
if (url) {
|
||||
document.cookie = 'last=' + encodeURIComponent(url) + '; expires=' + getExpires() + '; path=/';
|
||||
} else {
|
||||
// expire cookie
|
||||
document.cookie = 'last=""; expires=-1; path=/';
|
||||
}
|
||||
}
|
||||
|
||||
function readCookie(name) {
|
||||
var nameEQ = name + '=';
|
||||
var ca = document.cookie.split(';');
|
||||
for(var i=0;i < ca.length;i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0)==' ') c = c.substring(1,c.length);
|
||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function updateBackButton() {
|
||||
var el = document.getElementById('back');
|
||||
var back = readCookie('last');
|
||||
|
||||
if (el && back !== null && back !== '%2Fedit') {
|
||||
el.href = decodeURIComponent(back);
|
||||
}
|
||||
}
|
||||
|
||||
// save the bin url when the bin is saved, changed and when we load first time
|
||||
if (jsbin && jsbin.getURL) {
|
||||
$document.on('saved', save);
|
||||
save();
|
||||
} else {
|
||||
updateBackButton();
|
||||
}
|
||||
})();
|
||||
@ -230,6 +230,11 @@ $('#runconsole').click(function () {
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#clearconsole').click(function () {
|
||||
jsconsole.clear();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#showhelp').click(function () {
|
||||
$body.toggleClass('keyboardHelp');
|
||||
keyboardHelpVisible = $body.is('.keyboardHelp');
|
||||
@ -512,6 +517,14 @@ $('a.deletebin').on('click', function (e) {
|
||||
}
|
||||
});
|
||||
|
||||
$('a.archivebin').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
archive();
|
||||
});
|
||||
|
||||
$('a.unarchivebin').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
archive(false);
|
||||
});
|
||||
|
||||
}());
|
||||
|
||||
@ -56,6 +56,7 @@ var split = $('#sharemenu').find('.share-split').length;
|
||||
|
||||
// TODO candidate for removal
|
||||
function updateSavedState() {
|
||||
'use strict';
|
||||
if (split) {
|
||||
return;
|
||||
}
|
||||
@ -76,12 +77,21 @@ function updateSavedState() {
|
||||
$shareLinks.each(function () {
|
||||
var url = jsbin.getURL({ revision: withRevision }) + this.getAttribute('data-path') + (query && this.id !== 'livepreview' ? '?' + query : ''),
|
||||
nodeName = this.nodeName;
|
||||
var hash = panels.getHighlightLines();
|
||||
|
||||
if (hash) {
|
||||
hash = '#' + hash;
|
||||
}
|
||||
|
||||
if (nodeName === 'A') {
|
||||
this.href = url;
|
||||
} else if (nodeName === 'INPUT') {
|
||||
this.value = url;
|
||||
if (path === '/edit') {
|
||||
this.value += hash;
|
||||
}
|
||||
} else if (nodeName === 'TEXTAREA') {
|
||||
this.value = ('<a class="jsbin-embed" href="' + url + '">' + documentTitle + '</a><' + 'script src="' + jsbin.static + '/js/embed.js"><' + '/script>').replace(/<>"&/g, function (m) {
|
||||
this.value = ('<a class="jsbin-embed" href="' + url + hash + '">' + documentTitle + '</a><' + 'script src="' + jsbin.static + '/js/embed.js"><' + '/script>').replace(/<>"&/g, function (m) {
|
||||
return {
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
@ -239,6 +249,13 @@ if (!jsbin.saveDisabled) {
|
||||
});
|
||||
}
|
||||
|
||||
function compressKeys(keys, obj) {
|
||||
obj.compressed = keys;
|
||||
keys.split(',').forEach(function (key) {
|
||||
obj[key] = LZString.compressToUTF16(obj[key]);
|
||||
});
|
||||
}
|
||||
|
||||
function updateCode(panelId, callback) {
|
||||
var panelSettings = {};
|
||||
|
||||
@ -246,17 +263,23 @@ function updateCode(panelId, callback) {
|
||||
panelSettings.processors = jsbin.state.processors;
|
||||
}
|
||||
|
||||
var data = {
|
||||
code: jsbin.state.code,
|
||||
revision: jsbin.state.revision,
|
||||
method: 'update',
|
||||
panel: panelId,
|
||||
content: editors[panelId].getCode(),
|
||||
checksum: saveChecksum,
|
||||
settings: JSON.stringify(panelSettings),
|
||||
};
|
||||
|
||||
if (jsbin.settings.useCompression) {
|
||||
compressKeys('content', data);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: jsbin.getURL() + '/save',
|
||||
data: {
|
||||
code: jsbin.state.code,
|
||||
revision: jsbin.state.revision,
|
||||
method: 'update',
|
||||
panel: panelId,
|
||||
content: editors[panelId].getCode(),
|
||||
checksum: saveChecksum,
|
||||
settings: JSON.stringify(panelSettings)
|
||||
},
|
||||
data: data,
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
headers: {'Accept': 'application/json'},
|
||||
@ -345,15 +368,23 @@ function saveCode(method, ajax, ajaxCallback) {
|
||||
jsbin.panels.save();
|
||||
jsbin.panels.saveOnExit = true;
|
||||
|
||||
var data = $form.serializeArray().reduce(function(obj, data) {
|
||||
obj[data.name] = data.value;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
if (jsbin.settings.useCompression) {
|
||||
compressKeys('html,css,javascript', data);
|
||||
}
|
||||
|
||||
if (ajax) {
|
||||
$.ajax({
|
||||
url: $form.attr('action'),
|
||||
data: $form.serialize(),
|
||||
data: data,
|
||||
dataType: 'json',
|
||||
type: 'post',
|
||||
headers: {'Accept': 'application/json'},
|
||||
success: function (data) {
|
||||
$form.attr('action', data.url + '/save');
|
||||
if (ajaxCallback) {
|
||||
ajaxCallback(data);
|
||||
}
|
||||
@ -365,10 +396,14 @@ function saveCode(method, ajax, ajaxCallback) {
|
||||
jsbin.state.code = data.code;
|
||||
jsbin.state.revision = data.revision;
|
||||
jsbin.state.latest = true; // this is never not true...end of conversation!
|
||||
jsbin.state.metadata = { name: jsbin.user.name };
|
||||
$form.attr('action', jsbin.getURL() + '/save');
|
||||
|
||||
if (window.history && window.history.pushState) {
|
||||
// updateURL(edit);
|
||||
window.history.pushState(null, '', jsbin.getURL() + '/edit');
|
||||
var hash = panels.getHighlightLines();
|
||||
if (hash) {hash = '#' + hash;}
|
||||
window.history.pushState(null, '', jsbin.getURL() + '/edit' + hash);
|
||||
sessionStorage.setItem('url', jsbin.getURL());
|
||||
} else {
|
||||
window.location.hash = data.edit;
|
||||
|
||||
27
public/js/chrome/settings.js
Normal file
27
public/js/chrome/settings.js
Normal file
@ -0,0 +1,27 @@
|
||||
/*global jsbin, $*/
|
||||
|
||||
var settings = {
|
||||
save: function () {
|
||||
localStorage.setItem('settings', JSON.stringify(jsbin.settings));
|
||||
|
||||
$.ajax({
|
||||
url: '/account/editor',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
settings: localStorage.settings,
|
||||
_csrf: jsbin.state.token
|
||||
},
|
||||
success: function() {
|
||||
if (console && console.log) {
|
||||
console.log('Success on saving settings');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status) {
|
||||
if (console && console.log) {
|
||||
console.log('Error: ' + status);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
70
public/js/chrome/welcome-panel.js
Normal file
70
public/js/chrome/welcome-panel.js
Normal file
@ -0,0 +1,70 @@
|
||||
(function () {
|
||||
/*global jsbin, $, $body, $document, analytics, settings*/
|
||||
'use strict';
|
||||
|
||||
if (!$('#toppanel').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (jsbin.settings.gui === undefined) {
|
||||
jsbin.settings.gui = {};
|
||||
}
|
||||
if (jsbin.settings.gui.toppanel === undefined) {
|
||||
jsbin.settings.gui.toppanel = true;
|
||||
localStorage.setItem('settings', JSON.stringify(jsbin.settings));
|
||||
}
|
||||
|
||||
if ($body.hasClass('toppanel') && jsbin.settings.gui.toppanel === false) {
|
||||
$body.addClass('toppanel-close');
|
||||
$body.removeClass('toppanel');
|
||||
}
|
||||
|
||||
// analytics for panel state
|
||||
analytics.welcomePanelState(jsbin.settings.gui.toppanel);
|
||||
|
||||
var removeToppanel = function() {
|
||||
jsbin.settings.gui.toppanel = false;
|
||||
settings.save();
|
||||
$body.addClass('toppanel-close');
|
||||
$body.removeClass('toppanel');
|
||||
};
|
||||
|
||||
var showToppanel = function() {
|
||||
jsbin.settings.gui.toppanel = true;
|
||||
settings.save();
|
||||
$body.removeClass('toppanel-close');
|
||||
$body.addClass('toppanel');
|
||||
};
|
||||
|
||||
// to remove
|
||||
var goSlow = function(e) {
|
||||
$body.removeClass('toppanel-slow');
|
||||
if (e.shiftKey) {
|
||||
$body.addClass('toppanel-slow');
|
||||
}
|
||||
};
|
||||
|
||||
$('.toppanel-hide').click(function(event) {
|
||||
event.preventDefault();
|
||||
goSlow(event);
|
||||
removeToppanel();
|
||||
});
|
||||
$('.toppanel-logo').click(function(event) {
|
||||
event.preventDefault();
|
||||
goSlow(event);
|
||||
showToppanel();
|
||||
});
|
||||
$document.keydown(function (event) {
|
||||
if (event.which === 27) {
|
||||
if ($body.hasClass('toppanel')) {
|
||||
removeToppanel();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// analytics for links
|
||||
$('#toppanel').find('.toppanel-link').mousedown(function() {
|
||||
analytics.welcomePanelLink(this.href);
|
||||
});
|
||||
|
||||
}());
|
||||
@ -11,13 +11,39 @@
|
||||
fold: false,
|
||||
sublime: false,
|
||||
tern: false,
|
||||
activeline: true
|
||||
activeline: true,
|
||||
matchbrackets: false
|
||||
};
|
||||
|
||||
if (!jsbin.settings.addons) {
|
||||
jsbin.settings.addons = defaults;
|
||||
}
|
||||
|
||||
var detailsSupport = 'open' in document.createElement('details');
|
||||
|
||||
var settingsHints = {};
|
||||
var settingsHintShow = {};
|
||||
var hintShow = {
|
||||
console: true,
|
||||
line: false,
|
||||
under: false,
|
||||
gutter: false
|
||||
};
|
||||
// css must go last for the moment due to CSSLint creating the
|
||||
// global variable 'exports'
|
||||
['js', 'html', 'coffeescript', 'css'].forEach(function (val) {
|
||||
var h = val + 'hint';
|
||||
var d = false;
|
||||
if (val === 'js') {
|
||||
d = true;
|
||||
}
|
||||
settingsHints[h] = (jsbin.settings[h] !== undefined) ? jsbin.settings[h] : d;
|
||||
});
|
||||
|
||||
settingsHintShow = $.extend({}, hintShow, jsbin.settings.hintShow);
|
||||
settingsHintShow.tooltip = settingsHintShow.gutter;
|
||||
var settingsAddons = $.extend({}, jsbin.settings.addons, settingsHints);
|
||||
|
||||
var addons = {
|
||||
closebrackets: {
|
||||
url: '/js/vendor/codemirror4/addon/edit/closebrackets.js',
|
||||
@ -102,7 +128,10 @@
|
||||
cm.foldCode(cm.getCursor());
|
||||
}});
|
||||
setOption(cm, 'foldGutter', true);
|
||||
setOption(cm, 'gutters', ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']);
|
||||
var gutters = cm.getOption('gutters');
|
||||
gutters.push('CodeMirror-linenumbers');
|
||||
gutters.push('CodeMirror-foldgutter');
|
||||
setOption(cm, 'gutters', gutters);
|
||||
}
|
||||
},
|
||||
sublime: {
|
||||
@ -143,7 +172,7 @@
|
||||
],
|
||||
test: function () {
|
||||
return jsbin.panels.panels.javascript.editor.openDialog &&
|
||||
(typeof window.ternBasicDefs !== undefined) &&
|
||||
(typeof window.ternBasicDefs !== 'undefined') &&
|
||||
CodeMirror.showHint &&
|
||||
CodeMirror.TernServer &&
|
||||
CodeMirror.startTern;
|
||||
@ -157,11 +186,83 @@
|
||||
'/js/vendor/codemirror4/addon/selection/active-line.js'
|
||||
],
|
||||
test: function() {
|
||||
return CodeMirror.defaults.styleActiveLine !== undefined;
|
||||
return (typeof CodeMirror.defaults.styleActiveLine !== 'undefined');
|
||||
},
|
||||
done: function(cm) {
|
||||
setOption(cm, 'styleActiveLine', true);
|
||||
}
|
||||
},
|
||||
matchbrackets: {
|
||||
url: [],
|
||||
test: function() {
|
||||
return (typeof CodeMirror.defaults.matchBrackets !== 'undefined');
|
||||
},
|
||||
done: function(cm) {
|
||||
setOption(cm, 'matchBrackets', true);
|
||||
}
|
||||
},
|
||||
csshint: {
|
||||
url: [
|
||||
'/js/vendor/csslint/csslint.min.js',
|
||||
'/js/vendor/cm_addons/lint/css-lint.js'
|
||||
],
|
||||
test: function() {
|
||||
return hintingTest('css') &&
|
||||
(typeof CSSLint !== 'undefined');
|
||||
},
|
||||
done: function(cm) {
|
||||
if (cm.getOption('mode') !== 'css') {
|
||||
return;
|
||||
}
|
||||
hintingDone(cm);
|
||||
}
|
||||
},
|
||||
jshint: {
|
||||
url: [],
|
||||
test: function() {
|
||||
return hintingTest('javascript') &&
|
||||
(typeof JSHINT !== 'undefined');
|
||||
},
|
||||
done: function(cm) {
|
||||
if (cm.getOption('mode') !== 'javascript') {
|
||||
return;
|
||||
}
|
||||
hintingDone(cm, {
|
||||
'eqnull': true
|
||||
});
|
||||
}
|
||||
},
|
||||
htmlhint: {
|
||||
url: [
|
||||
'/js/vendor/htmlhint/htmlhint.js',
|
||||
'/js/vendor/cm_addons/lint/html-lint.js'
|
||||
],
|
||||
test: function() {
|
||||
return hintingTest('htmlmixed') &&
|
||||
(typeof HTMLHint !== 'undefined');
|
||||
},
|
||||
done: function(cm) {
|
||||
if (cm.getOption('mode') !== 'htmlmixed') {
|
||||
return;
|
||||
}
|
||||
hintingDone(cm);
|
||||
}
|
||||
},
|
||||
coffeescripthint: {
|
||||
url: [
|
||||
'/js/vendor/coffeelint/coffeelint.min.js',
|
||||
'/js/vendor/cm_addons/lint/coffeescript-lint.js'
|
||||
],
|
||||
test: function() {
|
||||
return hintingTest('coffeescript') &&
|
||||
(typeof coffeelint !== 'undefined');
|
||||
},
|
||||
done: function(cm) {
|
||||
if (cm.getOption('mode') !== 'coffeescript') {
|
||||
return;
|
||||
}
|
||||
hintingDone(cm);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -215,15 +316,61 @@
|
||||
|
||||
function defaultTest(prop) {
|
||||
return function () {
|
||||
return CodeMirror.optionHandlers[prop] !== undefined;
|
||||
return (typeof CodeMirror.optionHandlers[prop] !== 'undefined');
|
||||
};
|
||||
}
|
||||
|
||||
var options = Object.keys(jsbin.settings.addons);
|
||||
function hintingTest(mode) {
|
||||
return (typeof CodeMirror.defaults.lint !== 'undefined') &&
|
||||
CodeMirror.helpers.lint &&
|
||||
CodeMirror.helpers.lint[mode] &&
|
||||
CodeMirror.optionHandlers.lint;
|
||||
}
|
||||
|
||||
window.hintingDone = function(cm, defhintOptions) {
|
||||
var mode = cm.getOption('mode');
|
||||
if (mode === 'javascript') {
|
||||
mode = 'js';
|
||||
}
|
||||
if (mode === 'htmlmixed') {
|
||||
mode = 'html';
|
||||
}
|
||||
var opt = $.extend({}, settingsHintShow);
|
||||
opt.consoleParent = cm.getWrapperElement().parentNode.parentNode;
|
||||
setOption(cm, 'lintOpt', opt);
|
||||
setOption(cm, 'lintRules', $.extend({}, defhintOptions, jsbin.settings[mode + 'hintOptions']));
|
||||
if (opt.gutter) {
|
||||
var gutters = cm.getOption('gutters');
|
||||
if (gutters.indexOf('CodeMirror-lint-markers') === -1) {
|
||||
gutters.push('CodeMirror-lint-markers');
|
||||
setOption(cm, 'gutters', gutters);
|
||||
}
|
||||
setOption(cm, 'lint', true);
|
||||
var ln = cm.getOption('lineNumbers');
|
||||
setOption(cm, 'lineNumbers', !ln);
|
||||
setOption(cm, 'lineNumbers', ln);
|
||||
} else {
|
||||
setOption(cm, 'lint', true);
|
||||
}
|
||||
if (opt.console) {
|
||||
$document.trigger('sizeeditors');
|
||||
$(cm.consolelint.head).on('click', function() {
|
||||
if (!detailsSupport) {
|
||||
$(this).nextAll().toggle();
|
||||
}
|
||||
// trigger a resize after the click has completed and the details is close
|
||||
setTimeout(function () {
|
||||
$document.trigger('sizeeditors');
|
||||
}, 10);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var options = Object.keys(settingsAddons);
|
||||
|
||||
function loadAddon(key) {
|
||||
var addon = addons[key];
|
||||
if (addon && jsbin.settings.addons[key]) {
|
||||
if (addon && settingsAddons[key]) {
|
||||
if (typeof addon.url === 'string') {
|
||||
addon.url = [addon.url];
|
||||
}
|
||||
@ -254,4 +401,12 @@
|
||||
}
|
||||
};
|
||||
|
||||
// External method to realod the selected addon
|
||||
// may be useful in the future
|
||||
// window.reloadSelectedAddon = function(addon) {
|
||||
// if (options.indexOf(addon) !== -1) {
|
||||
// loadAddon(addon);
|
||||
// }
|
||||
// };
|
||||
|
||||
})();
|
||||
@ -110,12 +110,12 @@ panels.restore = function () {
|
||||
width = $window.width(),
|
||||
deferredCodeInsert = '',
|
||||
focused = !!sessionStorage.getItem('panel'),
|
||||
validPanels = 'live javascript html css console'.split(' ');
|
||||
validPanels = 'live javascript html css console'.split(' '),
|
||||
cachedHash = '';
|
||||
|
||||
// TODO document why this happens...
|
||||
// if (history.replaceState && (location.pathname.indexOf('/edit') !== -1) || ((location.origin + location.pathname) === jsbin.getURL() + '/')) {
|
||||
// history.replaceState(null, '', jsbin.getURL() + (jsbin.getURL() === jsbin.root ? '' : '/edit'));
|
||||
// }
|
||||
if (history.replaceState && (location.pathname.indexOf('/edit') !== -1) || ((location.origin + location.pathname) === jsbin.getURL() + '/')) {
|
||||
history.replaceState(null, '', jsbin.getURL() + (jsbin.getURL() === jsbin.root ? '' : '/edit') + (hash ? '#' + hash : ''));
|
||||
}
|
||||
|
||||
if (search || hash) {
|
||||
var query = (search || hash);
|
||||
@ -306,6 +306,23 @@ panels.savecontent = function () {
|
||||
}
|
||||
};
|
||||
|
||||
panels.getHighlightLines = function () {
|
||||
'use strict';
|
||||
var hash = [];
|
||||
var lines = '';
|
||||
var panel;
|
||||
for (name in panels.panels) {
|
||||
panel = panels.panels[name];
|
||||
if (panel.editor) {
|
||||
lines = panel.editor.highlightLines().string;
|
||||
if (lines) {
|
||||
hash.push(name.substr(0, 1).toUpperCase() + ':L' + lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hash.join(',');
|
||||
};
|
||||
|
||||
panels.focus = function (panel) {
|
||||
this.focused = panel;
|
||||
if (panel) {
|
||||
@ -579,16 +596,31 @@ var editorsReady = setInterval(function () {
|
||||
var ready = true,
|
||||
resizeTimer = null,
|
||||
panel,
|
||||
panelId;
|
||||
panelId,
|
||||
hash = window.location.hash.substring(1);
|
||||
|
||||
|
||||
for (panelId in panels.panels) {
|
||||
panel = panels.panels[panelId];
|
||||
if (panel.visible && !panel.ready) ready = false;
|
||||
if (panel.visible && !panel.ready) {
|
||||
ready = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
panels.ready = ready;
|
||||
|
||||
if (ready) {
|
||||
panels.allEditors(function (panel) {
|
||||
var key = panel.id.substr(0, 1).toUpperCase() + ':L';
|
||||
if (hash.indexOf(key) !== -1) {
|
||||
var lines = hash.match(new RegExp(key + '(\\d+(?:-\\d+)?)'));
|
||||
if (lines !== null) {
|
||||
panel.editor.highlightLines(lines[1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
clearInterval(editorsReady);
|
||||
// panels.ready = true;
|
||||
// if (typeof editors.onReady == 'function') editors.onReady();
|
||||
|
||||
@ -47,20 +47,18 @@ if (!customKeys.disabled) {
|
||||
|
||||
if (event.ctrlKey) { event.metaKey = true; }
|
||||
|
||||
if (event.metaKey && event.which === 89) {
|
||||
archive(!event.shiftKey);
|
||||
return event.preventDefault();
|
||||
}
|
||||
|
||||
if (event.metaKey && event.which === 79) { // open
|
||||
$('a.homebtn').trigger('click', 'keyboard');
|
||||
event.preventDefault();
|
||||
} else if (event.metaKey && event.shiftKey && event.which === 8) { // cmd+shift+backspace
|
||||
$('a.deletebin:first').trigger('click', 'keyboard');
|
||||
event.preventDefault();
|
||||
// } else if (event.altKey && event.which === 83) { // open share menu
|
||||
// var $sharemenu = $('#sharemenu');
|
||||
// if ($sharemenu.hasClass('open')) {
|
||||
|
||||
// }
|
||||
// $('#sharemenu a').trigger('mousedown');
|
||||
// event.preventDefault();
|
||||
} else if (event.metaKey && event.which === 83) { // save
|
||||
} else if (!jsbin.embed && event.metaKey && event.which === 83) { // save
|
||||
if (event.shiftKey === false) {
|
||||
if (saveChecksum) {
|
||||
saveChecksum = false;
|
||||
|
||||
@ -33,6 +33,15 @@ var libraries = [
|
||||
'label': 'jQuery UI WIP (via git)',
|
||||
'group': 'jQuery UI'
|
||||
},
|
||||
{
|
||||
'url': [
|
||||
'http://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.min.css',
|
||||
'http://code.jquery.com/jquery-1.11.0.min.js',
|
||||
'http://code.jquery.com/ui/1.11.0/jquery-ui.min.js'
|
||||
],
|
||||
'label': 'jQuery UI 1.11.0',
|
||||
'group': 'jQuery UI'
|
||||
},
|
||||
{
|
||||
'url': [
|
||||
'http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.min.css',
|
||||
@ -99,8 +108,8 @@ var libraries = [
|
||||
{
|
||||
'url': [
|
||||
'http://code.jquery.com/jquery.min.js',
|
||||
'http://getbootstrap.com/dist/css/bootstrap.css',
|
||||
'http://getbootstrap.com/dist/js/bootstrap.js'
|
||||
'http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css',
|
||||
'http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js'
|
||||
],
|
||||
'label': 'Bootstrap Latest',
|
||||
'group': 'Bootstrap'
|
||||
@ -108,9 +117,8 @@ var libraries = [
|
||||
{
|
||||
'url': [
|
||||
'http://code.jquery.com/jquery.min.js',
|
||||
'http://getbootstrap.com/2.3.2/assets/css/bootstrap.css',
|
||||
'http://getbootstrap.com/2.3.2/assets/css/bootstrap-responsive.css',
|
||||
'http://getbootstrap.com/2.3.2/assets/js/bootstrap.js'
|
||||
'http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css',
|
||||
'http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js'
|
||||
],
|
||||
'label': 'Bootstrap 2.3.2',
|
||||
'group': 'Bootstrap'
|
||||
@ -157,8 +165,13 @@ var libraries = [
|
||||
'group': 'YUI'
|
||||
},
|
||||
{
|
||||
'url': 'http://ajax.googleapis.com/ajax/libs/mootools/1/mootools-yui-compressed.js',
|
||||
'label': 'MooTools latest',
|
||||
'url': 'http://ajax.googleapis.com/ajax/libs/mootools/1.5.0/mootools-yui-compressed.js',
|
||||
'label': 'MooTools 1.5.0',
|
||||
'group': 'MooTools'
|
||||
},
|
||||
{
|
||||
'url': 'http://ajax.googleapis.com/ajax/libs/mootools/1.5.0/mootools-nocompat-yui-compressed.js',
|
||||
'label': 'MooTools 1.5.0 (without 1.2+ compatibility layer)',
|
||||
'group': 'MooTools'
|
||||
},
|
||||
{
|
||||
@ -205,6 +218,30 @@ var libraries = [
|
||||
'label': 'Dijit 1.7.4 (Claro)',
|
||||
'group': 'Dojo'
|
||||
},
|
||||
{
|
||||
'url': [
|
||||
'http://cdn.kendostatic.com/2014.1.528/styles/kendo.common.min.css',
|
||||
'http://cdn.kendostatic.com/2014.1.528/styles/kendo.default.min.css',
|
||||
'http://code.jquery.com/jquery-1.9.1.min.js',
|
||||
'http://cdn.kendostatic.com/2014.1.528/js/kendo.ui.core.min.js'
|
||||
],
|
||||
'label': 'Kendo UI Core Q1 SP2',
|
||||
'group': 'Kendo UI'
|
||||
},
|
||||
{
|
||||
'url': [
|
||||
'http://cdn.kendostatic.com/2014.1.318/styles/kendo.common.min.css',
|
||||
'http://cdn.kendostatic.com/2014.1.318/styles/kendo.rtl.min.css',
|
||||
'http://cdn.kendostatic.com/2014.1.318/styles/kendo.default.min.css',
|
||||
'http://cdn.kendostatic.com/2014.1.318/styles/kendo.dataviz.min.css',
|
||||
'http://cdn.kendostatic.com/2014.1.318/styles/kendo.dataviz.default.min.css',
|
||||
'http://cdn.kendostatic.com/2014.1.318/styles/kendo.mobile.all.min.css',
|
||||
'http://code.jquery.com/jquery-1.9.1.min.js',
|
||||
'http://cdn.kendostatic.com/2014.1.318/js/kendo.all.min.js'
|
||||
],
|
||||
'label': 'Kendo UI Q1 2014',
|
||||
'group': 'Kendo UI'
|
||||
},
|
||||
{
|
||||
'url': [
|
||||
'http://cdn.kendostatic.com/2013.3.1119/styles/kendo.common.min.css',
|
||||
@ -219,20 +256,6 @@ var libraries = [
|
||||
'label': 'Kendo UI Q3 2013',
|
||||
'group': 'Kendo UI'
|
||||
},
|
||||
{
|
||||
'url': [
|
||||
'http://cdn.kendostatic.com/2013.2.716/styles/kendo.common.min.css',
|
||||
'http://cdn.kendostatic.com/2013.2.716/styles/kendo.rtl.min.css',
|
||||
'http://cdn.kendostatic.com/2013.2.716/styles/kendo.default.min.css',
|
||||
'http://cdn.kendostatic.com/2013.2.716/styles/kendo.dataviz.min.css',
|
||||
'http://cdn.kendostatic.com/2013.2.716/styles/kendo.dataviz.default.min.css',
|
||||
'http://cdn.kendostatic.com/2013.2.716/styles/kendo.mobile.all.min.css',
|
||||
'http://code.jquery.com/jquery-1.9.1.min.js',
|
||||
'http://cdn.kendostatic.com/2013.2.716/js/kendo.all.min.js'
|
||||
],
|
||||
'label': 'Kendo UI Q2 2013',
|
||||
'group': 'Kendo UI'
|
||||
},
|
||||
{
|
||||
'url' : [
|
||||
'http://code.jquery.com/qunit/qunit-git.css',
|
||||
@ -261,6 +284,11 @@ var libraries = [
|
||||
'label': 'Angular 1.2.14 Stable',
|
||||
'group': 'Angular'
|
||||
},
|
||||
{
|
||||
'url': 'https://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js',
|
||||
'label': 'Angular 1.2.14 Uncompressed',
|
||||
'group': 'Angular'
|
||||
},
|
||||
{
|
||||
'url':'https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js',
|
||||
'label': 'Angular 1.0.7 Stable',
|
||||
@ -300,6 +328,26 @@ var libraries = [
|
||||
'label': 'Enyo 2.2.0',
|
||||
'group': 'Enyo'
|
||||
},
|
||||
{
|
||||
'url': '//cdnjs.cloudflare.com/ajax/libs/bluebird/1.2.2/bluebird.js',
|
||||
'label': 'Bluebird 1.2.2',
|
||||
'group': 'Promises'
|
||||
},
|
||||
{
|
||||
'url': 'https://www.promisejs.org/polyfills/promise-4.0.0.js',
|
||||
'label': 'Promise 4.0.0',
|
||||
'group': 'Promises'
|
||||
},
|
||||
{
|
||||
'url': '//cdnjs.cloudflare.com/ajax/libs/q.js/1.0.1/q.js',
|
||||
'label': 'Q 1.0.1',
|
||||
'group': 'Promises'
|
||||
},
|
||||
{
|
||||
'url': '//cdn.jsdelivr.net/rsvp/3.0.6/rsvp.js',
|
||||
'label': 'RSVP 3.0.6',
|
||||
'group': 'Promises'
|
||||
},
|
||||
{
|
||||
'url': [
|
||||
'https://rawgithub.com/ai/autoprefixer-rails/master/vendor/autoprefixer.js'
|
||||
@ -375,8 +423,15 @@ var libraries = [
|
||||
'label': 'Lo-Dash 2.4.1'
|
||||
},
|
||||
{
|
||||
'url': 'http://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js',
|
||||
'label': 'Modernizr 2.6.2'
|
||||
'url': 'http://modernizr.com/downloads/modernizr-latest.js',
|
||||
'label': 'Modernizr Development latest'
|
||||
},
|
||||
{
|
||||
'url': [
|
||||
'http://cdnjs.cloudflare.com/ajax/libs/modernizr/2.6.2/modernizr.min.js',
|
||||
'http://cdnjs.cloudflare.com/ajax/libs/detectizr/1.5.0/detectizr.min.js'
|
||||
],
|
||||
'label': 'Detectizr 1.5.0'
|
||||
},
|
||||
{
|
||||
'url': 'http://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js',
|
||||
@ -390,6 +445,10 @@ var libraries = [
|
||||
'url': 'http://d3js.org/d3.v3.min.js',
|
||||
'label': 'D3 3.x'
|
||||
},
|
||||
{
|
||||
'url': '//code.highcharts.com/highcharts.js',
|
||||
'label': 'Highcharts latest'
|
||||
},
|
||||
{
|
||||
'url': 'http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js',
|
||||
'label': 'Raphaël 2.1.0'
|
||||
@ -450,10 +509,10 @@ var libraries = [
|
||||
},
|
||||
{
|
||||
'url': [
|
||||
'//cdnjs.cloudflare.com/ajax/libs/polymer/0.2.3/platform.js',
|
||||
'//cdnjs.cloudflare.com/ajax/libs/polymer/0.2.3/polymer.js'
|
||||
'//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.3/platform.js',
|
||||
'//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.3/polymer.js'
|
||||
],
|
||||
'label': 'Polymer 0.2.3'
|
||||
'label': 'Polymer 0.3.3'
|
||||
},
|
||||
{
|
||||
'url': '//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css',
|
||||
@ -475,6 +534,10 @@ var libraries = [
|
||||
{
|
||||
'url': 'http://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.7/TweenMax.min.js',
|
||||
'label': 'GSAP 1.11.7'
|
||||
},
|
||||
{
|
||||
'url': '//cdnjs.cloudflare.com/ajax/libs/phaser/2.0.5/phaser.min.js',
|
||||
'label': 'Phaser 2.0.5'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@ -42,6 +42,7 @@ CodeMirror.commands.snippets = function (cm) {
|
||||
};
|
||||
|
||||
var Panel = function (name, settings) {
|
||||
'use strict';
|
||||
var panel = this,
|
||||
showPanelButton = true,
|
||||
$panel = null,
|
||||
@ -74,17 +75,18 @@ var Panel = function (name, settings) {
|
||||
}
|
||||
|
||||
// this is nasty and wrong, but I'm going to put here anyway .i..
|
||||
if (this.id === 'javascript') {
|
||||
this.on('processor', function (e, preprocessor) {
|
||||
if (preprocessor === 'none') {
|
||||
jshintEnabled = true;
|
||||
checkForErrors();
|
||||
} else {
|
||||
jshintEnabled = false;
|
||||
$error.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
// removed as we have a different way to check for errors
|
||||
// if (this.id === 'javascript') {
|
||||
// this.on('processor', function (e, preprocessor) {
|
||||
// if (preprocessor === 'none') {
|
||||
// jshintEnabled = true;
|
||||
// checkForErrors();
|
||||
// } else {
|
||||
// jshintEnabled = false;
|
||||
// $error.hide();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
if (settings.editor) {
|
||||
cmSettings = {
|
||||
@ -93,7 +95,9 @@ var Panel = function (name, settings) {
|
||||
dragDrop: false, // we handle it ourselves
|
||||
mode: editorModes[panelLanguage],
|
||||
lineWrapping: true,
|
||||
theme: jsbin.settings.theme || 'jsbin'
|
||||
// gutters: ['line-highlight'],
|
||||
theme: jsbin.settings.theme || 'jsbin',
|
||||
highlighLine: true
|
||||
};
|
||||
|
||||
$.extend(cmSettings, jsbin.settings.editor || {});
|
||||
@ -114,11 +118,27 @@ var Panel = function (name, settings) {
|
||||
profile: name /* define Zen Coding output profile */
|
||||
});
|
||||
|
||||
// make sure tabSize and indentUnit are numbers
|
||||
if (typeof cmSettings.tabSize === 'string') {
|
||||
cmSettings.tabSize = parseInt(cmSettings.tabSize, 10) || 2;
|
||||
}
|
||||
if (typeof cmSettings.indentUnit === 'string') {
|
||||
cmSettings.indentUnit = parseInt(cmSettings.indentUnit, 10) || 2;
|
||||
}
|
||||
|
||||
panel.editor = CodeMirror.fromTextArea(panel.el, cmSettings);
|
||||
|
||||
panel.editor.on('highlightLines', function () {
|
||||
window.location.hash = panels.getHighlightLines();
|
||||
});
|
||||
|
||||
// Bind events using CM3 syntax
|
||||
panel.editor.on('change', function codeChange(cm, changeObj) {
|
||||
$document.trigger('codeChange', [{ panelId: panel.id, revert: true, origin: changeObj.origin }]);
|
||||
if (jsbin.saveDisabled) {
|
||||
$document.trigger('codeChange.live', [{ panelId: panel.id, revert: true, origin: changeObj.origin }]);
|
||||
} else {
|
||||
$document.trigger('codeChange', [{ panelId: panel.id, revert: true, origin: changeObj.origin }]);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
@ -433,14 +453,8 @@ Panel.prototype = {
|
||||
var height = panel.editor.scroller.closest('.panel').outerHeight(),
|
||||
offset = 0;
|
||||
// offset = panel.$el.find('> .label').outerHeight();
|
||||
|
||||
// special case for the javascript panel
|
||||
if (panel.name === 'javascript') {
|
||||
if ($error === null) { // it wasn't there right away, so we populate
|
||||
$error = panel.$el.find('details');
|
||||
}
|
||||
offset += ($error.filter(':visible').height() || 0);
|
||||
}
|
||||
$error = panel.$el.find('details');
|
||||
offset += ($error.filter(':visible').height() || 0);
|
||||
|
||||
if (!jsbin.lameEditor) {
|
||||
editor.scroller.height(height - offset);
|
||||
@ -509,7 +523,7 @@ function populateEditor(editor, panel) {
|
||||
if (!editor.codeSet) {
|
||||
// populate - should eventually use: session, saved data, local storage
|
||||
var cached = sessionStorage.getItem('jsbin.content.' + panel), // session code
|
||||
saved = localStorage.getItem('saved-' + panel), // user template
|
||||
saved = jsbin.embed ? null : localStorage.getItem('saved-' + panel), // user template
|
||||
sessionURL = sessionStorage.getItem('url'),
|
||||
changed = false;
|
||||
|
||||
@ -528,7 +542,7 @@ function populateEditor(editor, panel) {
|
||||
// tell the document that it's currently being edited, but check that it doesn't match the saved template
|
||||
// because sessionStorage gets set on a reload
|
||||
changed = cached != saved && cached != template[panel];
|
||||
} else if (saved !== null && !/edit/.test(window.location) && !window.location.search) { // then their saved preference
|
||||
} else if (!template.post && saved !== null && !/(edit|embed)$/.test(window.location) && !window.location.search) { // then their saved preference
|
||||
editor.setCode(saved);
|
||||
} else { // otherwise fall back on the JS Bin default
|
||||
editor.setCode(template[panel]);
|
||||
|
||||
@ -2,12 +2,16 @@
|
||||
'use strict';
|
||||
|
||||
/*globals $, jsbin, CodeMirror, template, ternDefinitions, ternBasicDefs */
|
||||
if (!jsbin.settings.addons.tern) {
|
||||
return;
|
||||
}
|
||||
|
||||
var ternServer;
|
||||
var ternLoaded = {};
|
||||
|
||||
var initTern = function(editor, defs){
|
||||
var keyMap = {
|
||||
'Ctrl-Q': function(cm) { ternServer.selectName(cm); },
|
||||
'Ctrl-I': function(cm) { ternServer.showType(cm); },
|
||||
'Ctrl-Space': function(cm) { ternServer.complete(cm); }
|
||||
};
|
||||
@ -90,7 +94,7 @@
|
||||
if (cm.options.indentWithTabs) {
|
||||
indent = '\t';
|
||||
} else {
|
||||
indent = new Array(cm.options.indentUnit + 1).join(' ');
|
||||
indent = new Array(cm.options.indentUnit * 1 + 1).join(' ');
|
||||
}
|
||||
|
||||
if (tok.string === ';') {
|
||||
|
||||
1
public/js/intro-start.js
Normal file
1
public/js/intro-start.js
Normal file
@ -0,0 +1 @@
|
||||
function start(template, jsbin, window, document, undefined) {
|
||||
@ -1,11 +1,13 @@
|
||||
try {
|
||||
console.log('init');
|
||||
console.log('Dave is ready.');
|
||||
} catch (e) {
|
||||
var console = {
|
||||
window.console = {
|
||||
log: function () {
|
||||
// alert([].slice.call(arguments).join('\n'));
|
||||
},
|
||||
warn: function () {}
|
||||
warn: function () {},
|
||||
trace: function () {},
|
||||
error: function () {}
|
||||
};
|
||||
}
|
||||
|
||||
@ -57,20 +59,47 @@ function dedupe(array) {
|
||||
return results;
|
||||
}
|
||||
|
||||
function exposeSettings() {
|
||||
'use strict';
|
||||
if (window.jsbin instanceof Node || !window.jsbin) { // because...STUPIDITY!!!
|
||||
window.jsbin = {
|
||||
'static': jsbin['static']
|
||||
}; // create the holding object
|
||||
|
||||
if (jsbin.state.metadata && jsbin.user && jsbin.state.metadata.name === jsbin.user.name && jsbin.user.name) {
|
||||
window.jsbin.settings = jsbin.settings;
|
||||
return;
|
||||
}
|
||||
|
||||
var key = 'o' + (Math.random() * 1).toString(32).slice(2);
|
||||
Object.defineProperty(window, key, {
|
||||
get:function () {
|
||||
window.jsbin.settings = jsbin.settings;
|
||||
console.log('jsbin.settings can how be modified on the console');
|
||||
}
|
||||
});
|
||||
if (!jsbin.embed) {
|
||||
console.log('To edit settings, type this string into the console: ' + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window['jsbin'] || (window.jsbin = {});
|
||||
// dodgy?
|
||||
var storedSettings = localStorage.getItem('settings');
|
||||
if (storedSettings === "undefined") {
|
||||
// yes, equals the *string* "undefined", then something went wrong
|
||||
storedSettings = null;
|
||||
}
|
||||
|
||||
window.jsbin.settings = $.extend(JSON.parse(storedSettings || '{}'), jsbin.settings);
|
||||
// In all cases localStorage takes precedence over user settings so users can
|
||||
// configure it from the console and overwrite the server delivered settings
|
||||
jsbin.settings = $.extend({}, jsbin.settings, JSON.parse(storedSettings || '{}'));
|
||||
|
||||
if (jsbin.user) {
|
||||
$.extend(window.jsbin.settings, jsbin.user.settings);
|
||||
jsbin.settings = $.extend({}, jsbin.user.settings, jsbin.settings);
|
||||
}
|
||||
|
||||
exposeSettings();
|
||||
|
||||
// if the above code isn't dodgy, this for hellz bells is:
|
||||
jsbin.mobile = /WebKit.*Mobile.*|Android/.test(navigator.userAgent);
|
||||
jsbin.tablet = /iPad/i.test(navigator.userAgent); // sue me.
|
||||
@ -91,10 +120,6 @@ jsbin.ie = (function(){
|
||||
if (!storedSettings && (location.origin + location.pathname) === jsbin.root + '/') {
|
||||
// first timer - let's welcome them shall we, Dave?
|
||||
localStorage.setItem('settings', '{}');
|
||||
if (!jsbin.custom) {
|
||||
window.location = jsbin.root + '/welcome/1/edit?html,live'
|
||||
+ (location.search.indexOf('api=') !== -1 ? ',&' + location.search.substring(1) : '');
|
||||
}
|
||||
}
|
||||
|
||||
if (!jsbin.settings.editor) {
|
||||
@ -120,6 +145,10 @@ jQuery.ajaxPrefilter(function (options, original, xhr) {
|
||||
}
|
||||
});
|
||||
|
||||
jsbin.owner = function () {
|
||||
return jsbin.user && jsbin.user.name && jsbin.state.metadata && jsbin.state.metadata.name === jsbin.user.name;
|
||||
};
|
||||
|
||||
jsbin.getURL = function (options) {
|
||||
if (!options) { options = {}; }
|
||||
|
||||
|
||||
1
public/js/outro-start.js
Normal file
1
public/js/outro-start.js
Normal file
@ -0,0 +1 @@
|
||||
}
|
||||
@ -1 +1 @@
|
||||
})(this, document);
|
||||
})(window, document);
|
||||
@ -279,7 +279,7 @@ var processors = jsbin.processors = (function () {
|
||||
id: 'less',
|
||||
target: 'css',
|
||||
extensions: ['less'],
|
||||
url: jsbin.static + '/js/vendor/less-1.4.2.min.js',
|
||||
url: jsbin.static + '/js/vendor/less-1.7.3.min.js',
|
||||
init: function (ready) {
|
||||
// In CodeMirror 4, less is now included in the css mode, so no files to load
|
||||
ready();
|
||||
@ -449,6 +449,26 @@ var processors = jsbin.processors = (function () {
|
||||
delete jsbin.state.processors[panelId];
|
||||
delete panel.type;
|
||||
}
|
||||
|
||||
// linting
|
||||
mmMode = cmMode;
|
||||
if (cmMode === 'javascript') {
|
||||
mmMode = 'js';
|
||||
}
|
||||
if (cmMode === 'htmlmixed') {
|
||||
mmMode = 'html';
|
||||
}
|
||||
var isHint = panel.editor.getOption('lint');
|
||||
if (isHint) {
|
||||
panel.editor.lintStop();
|
||||
}
|
||||
if (jsbin.settings[mmMode + 'hint']) {
|
||||
panel.editor.setOption('mode', cmMode);
|
||||
if (typeof hintingDone !== 'undefined') {
|
||||
panel.editor.setOption('mode', cmMode);
|
||||
hintingDone(panel.editor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
processors.reset = function (panelId) {
|
||||
|
||||
5
public/js/prod/addon-tern-3.15.3.min.js
vendored
Normal file
5
public/js/prod/addon-tern-3.15.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
public/js/prod/addon-tern-3.15.4.min.js
vendored
Normal file
5
public/js/prod/addon-tern-3.15.4.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
94476
public/js/prod/jsbin-3.15.3.js
Normal file
94476
public/js/prod/jsbin-3.15.3.js
Normal file
File diff suppressed because it is too large
Load Diff
20
public/js/prod/jsbin-3.15.3.min.js
vendored
Normal file
20
public/js/prod/jsbin-3.15.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
94476
public/js/prod/jsbin-3.15.4.js
Normal file
94476
public/js/prod/jsbin-3.15.4.js
Normal file
File diff suppressed because it is too large
Load Diff
20
public/js/prod/jsbin-3.15.4.min.js
vendored
Normal file
20
public/js/prod/jsbin-3.15.4.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1482
public/js/prod/runner-3.15.3.js
Normal file
1482
public/js/prod/runner-3.15.3.js
Normal file
File diff suppressed because it is too large
Load Diff
1
public/js/prod/runner-3.15.3.min.js
vendored
Normal file
1
public/js/prod/runner-3.15.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1482
public/js/prod/runner-3.15.4.js
Normal file
1482
public/js/prod/runner-3.15.4.js
Normal file
File diff suppressed because it is too large
Load Diff
1
public/js/prod/runner-3.15.4.min.js
vendored
Normal file
1
public/js/prod/runner-3.15.4.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -583,8 +583,10 @@ var msgType = '';
|
||||
jsconsole.init(document.getElementById('output'));
|
||||
|
||||
function upgradeConsolePanel(console) {
|
||||
console.$el.click(function () {
|
||||
jsconsole.focus();
|
||||
console.$el.click(function (event) {
|
||||
if (!$(event.target).closest('#output').length) {
|
||||
jsconsole.focus();
|
||||
}
|
||||
});
|
||||
console.reset = function () {
|
||||
jsconsole.reset();
|
||||
|
||||
@ -192,7 +192,7 @@ var renderer = (function () {
|
||||
size.show().html(data.width + 'px');
|
||||
hide();
|
||||
}
|
||||
if (jsbin.embed && embedResizeDone === false) {
|
||||
if (jsbin.embed && self !== top && embedResizeDone === false) {
|
||||
embedResizeDone = true;
|
||||
// Inform the outer page of a size change
|
||||
var height = ($body.outerHeight(true) - $(renderer.runner.iframe).height()) + data.offsetHeight;
|
||||
|
||||
@ -144,9 +144,9 @@
|
||||
$('a[pubdate]', $history).attr('pubdate', function (i, val) {
|
||||
return val.replace('Z', '+0000');
|
||||
}).prettyDate();
|
||||
setInterval(function(){
|
||||
$created.prettyDate();
|
||||
}, 30 * 1000);
|
||||
// setInterval(function(){
|
||||
// $created.prettyDate();
|
||||
// }, 30 * 1000);
|
||||
|
||||
// Update the layout straign away
|
||||
setTimeout(function () {
|
||||
|
||||
@ -4,16 +4,16 @@
|
||||
* ========================================================================== */
|
||||
|
||||
var proxyConsole = (function () {
|
||||
|
||||
'use strict';
|
||||
var supportsConsole = true;
|
||||
try { window.console.log('runner'); } catch (e) { supportsConsole = false; }
|
||||
try { window.console.log('d[ o_0 ]b'); } catch (e) { supportsConsole = false; }
|
||||
|
||||
var proxyConsole = {};
|
||||
var proxyConsole = function() {};
|
||||
|
||||
/**
|
||||
* Stringify all of the console objects from an array for proxying
|
||||
*/
|
||||
proxyConsole.stringifyArgs = function (args) {
|
||||
var stringifyArgs = function (args) {
|
||||
var newArgs = [];
|
||||
// TODO this was forEach but when the array is [undefined] it wouldn't
|
||||
// iterate over them
|
||||
@ -31,7 +31,7 @@ var proxyConsole = (function () {
|
||||
|
||||
// Create each of these methods on the proxy, and postMessage up to JS Bin
|
||||
// when one is called.
|
||||
var methods = [
|
||||
var methods = proxyConsole.prototype.methods = [
|
||||
'debug', 'clear', 'error', 'info', 'log', 'warn', 'dir', 'props', '_raw',
|
||||
'group', 'groupEnd', 'dirxml', 'table', 'trace', 'assert', 'count',
|
||||
'markTimeline', 'profile', 'profileEnd', 'time', 'timeEnd', 'timeStamp',
|
||||
@ -39,10 +39,10 @@ var proxyConsole = (function () {
|
||||
];
|
||||
methods.forEach(function (method) {
|
||||
// Create console method
|
||||
proxyConsole[method] = function () {
|
||||
proxyConsole.prototype[method] = function () {
|
||||
// Replace args that can't be sent through postMessage
|
||||
var originalArgs = [].slice.call(arguments),
|
||||
args = proxyConsole.stringifyArgs(originalArgs);
|
||||
args = stringifyArgs(originalArgs);
|
||||
|
||||
// Post up with method and the arguments
|
||||
runner.postMessage('console', {
|
||||
@ -59,6 +59,6 @@ var proxyConsole = (function () {
|
||||
};
|
||||
});
|
||||
|
||||
return proxyConsole;
|
||||
return new proxyConsole();
|
||||
|
||||
}());
|
||||
@ -4,7 +4,7 @@
|
||||
* ========================================================================== */
|
||||
|
||||
var runner = (function () {
|
||||
|
||||
'use strict';
|
||||
var runner = {};
|
||||
|
||||
/**
|
||||
@ -18,7 +18,7 @@ var runner = (function () {
|
||||
*/
|
||||
runner.error = function () {
|
||||
var args = ['Runner:'].concat([].slice.call(arguments));
|
||||
if (!('console' in window)) return alert(args.join(' '));
|
||||
if (!('console' in window)) {return alert(args.join(' '));}
|
||||
window.console.error.apply(console, args);
|
||||
};
|
||||
|
||||
@ -26,7 +26,7 @@ var runner = (function () {
|
||||
* Handle all incoming postMessages to the runner
|
||||
*/
|
||||
runner.handleMessage = function (event) {
|
||||
if (!event.origin) return;
|
||||
if (!event.origin) {return;}
|
||||
var data = event.data;
|
||||
try {
|
||||
data = JSON.parse(event.data);
|
||||
@ -67,6 +67,12 @@ var runner = (function () {
|
||||
childWindow = getIframeWindow(iframe);
|
||||
if (!childDoc) childDoc = childWindow.document;
|
||||
|
||||
// Reset the console to the prototype state
|
||||
proxyConsole.methods.forEach(function (method) {
|
||||
delete proxyConsole[method];
|
||||
});
|
||||
|
||||
|
||||
// Process the source according to the options passed in
|
||||
var source = processor.render(data.source, data.options);
|
||||
|
||||
|
||||
@ -226,8 +226,7 @@ var id = location.pathname.replace(/\/(preview|edit|watch).*$/, ''),
|
||||
useSS = false,
|
||||
es = null;
|
||||
|
||||
// Wait for a bit, then set up the EventSource stream
|
||||
setTimeout(function () {
|
||||
function startStream() {
|
||||
es = new EventSource(id + '?' + Math.random());
|
||||
if (codecasting) {
|
||||
codecastStream();
|
||||
@ -240,6 +239,37 @@ setTimeout(function () {
|
||||
$document.trigger('stats', [event.data]);
|
||||
});
|
||||
}
|
||||
|
||||
return es;
|
||||
}
|
||||
|
||||
function handleVisibility(es, listen) {
|
||||
var hiddenProperty = 'hidden' in document ? 'hidden' :
|
||||
'webkitHidden' in document ? 'webkitHidden' :
|
||||
'mozHidden' in document ? 'mozHidden' :
|
||||
null;
|
||||
var visibilityStateProperty = 'visibilityState' in document ? 'visibilityState' :
|
||||
'webkitVisibilityState' in document ? 'webkitVisibilityState' :
|
||||
'mozVisibilityState' in document ? 'mozVisibilityState' :
|
||||
null;
|
||||
|
||||
if (visibilityStateProperty) {
|
||||
var visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange');
|
||||
document.addEventListener(visibilityChangeEvent, function visibilityChangeEvent() {
|
||||
if (document[hiddenProperty]) { // hidden
|
||||
es.close();
|
||||
} else {
|
||||
es = listen();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Wait for a bit, then set up the EventSource stream
|
||||
setTimeout(function() {
|
||||
es = startStream();
|
||||
handleVisibility(es, startStream);
|
||||
}, 500);
|
||||
|
||||
// If this is the render stream, restore data from before the last reload if
|
||||
|
||||
172
public/js/vendor/cm_addons/cm-highlight-line.js
vendored
Normal file
172
public/js/vendor/cm_addons/cm-highlight-line.js
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
// Remy Sharp / MIT
|
||||
//
|
||||
// Because sometimes you want line numbers to be highlighted
|
||||
//
|
||||
// Adds an option 'highlightedLine' which, when enabled, gives the
|
||||
// active line's wrapping <div> the CSS class 'CodeMirror-highlight-line',
|
||||
// and gives its background <div> the class 'CodeMirror-highlight-line-background'.
|
||||
// *and* emits an event called "highlight-lines" against the CM instance with
|
||||
// the first argument being the line numbers now highlighted.
|
||||
|
||||
(function(mod) {
|
||||
/* globals define, CodeMirror */
|
||||
'use strict';
|
||||
if (typeof exports === 'object' && typeof module === 'object') { // CommonJS
|
||||
mod(require('../../lib/codemirror'));
|
||||
} else if (typeof define === 'function' && define.amd) { // AMD
|
||||
define(['../../lib/codemirror'], mod);
|
||||
} else { // Plain browser env
|
||||
mod(CodeMirror);
|
||||
}
|
||||
})(function(CodeMirror) {
|
||||
'use strict';
|
||||
var WRAP_CLASS = 'CodeMirror-highlight-line';
|
||||
var BACK_CLASS = 'CodeMirror-highlight-line-background';
|
||||
|
||||
CodeMirror.defineOption('highlighLine', false, function(cm, val, old) {
|
||||
var prev = old && old !== CodeMirror.Init;
|
||||
if (val && !prev) {
|
||||
cm.state.highlightedLines = [];
|
||||
if (typeof val !== 'boolean') {
|
||||
updateHighlightedLines(cm, parseLinesToArray(val));
|
||||
}
|
||||
cm.on('gutterClick', gutterClick);
|
||||
} else if (!val && prev) {
|
||||
cm.off('gutterClick', gutterClick);
|
||||
clearHighlightedLines(cm);
|
||||
delete cm.state.highlightedLines;
|
||||
}
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension('highlightLines', function (lines) {
|
||||
if (lines) {
|
||||
clearHighlightedLines(this);
|
||||
updateHighlightedLines(this, parseLinesToArray(lines));
|
||||
} else {
|
||||
var active = [].slice.call(this.state.highlightedLines);
|
||||
return {
|
||||
lines: active,
|
||||
string: parseArrayToString(active)
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
function parseLinesToArray(str) {
|
||||
var active = [];
|
||||
|
||||
if (({}).toString.call(str) === '[object Array]') {
|
||||
// wat...oh you gave me an array
|
||||
return str;
|
||||
}
|
||||
|
||||
if (str.indexOf('-') !== -1) {
|
||||
// range
|
||||
var range = str.split('-');
|
||||
var i = parseInt(range[0], 10);
|
||||
var length = parseInt(range[1], 10);
|
||||
for (; i <= length; i++) {
|
||||
active.push(i-1);
|
||||
}
|
||||
} else {
|
||||
active = [parseInt(str, 10) - 1];
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
function parseArrayToString(active) {
|
||||
if (active.length === 1) {
|
||||
return (active[0] + 1) + '';
|
||||
} else if (active.length === 0) {
|
||||
return '';
|
||||
} else {
|
||||
return (active[0] + 1) + '-' + (active.slice(-1)[0] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
function clearHighlightedLines(cm) {
|
||||
for (var i = 0; i < cm.state.highlightedLines.length; i++) {
|
||||
cm.removeLineClass(cm.state.highlightedLines[i], 'wrap', WRAP_CLASS);
|
||||
cm.removeLineClass(cm.state.highlightedLines[i], 'background', BACK_CLASS);
|
||||
}
|
||||
cm.state.highlightedLines = [];
|
||||
}
|
||||
|
||||
function sameArray(a, b) {
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function highlightLines(cm, lineNumber, event) {
|
||||
// copy the array (to avoid creating a reference)
|
||||
var active = [].slice.call(cm.state.highlightedLines, 0);
|
||||
|
||||
// shiftKey down gives multi-line highlight support
|
||||
if (active.length && event.shiftKey) {
|
||||
var i = active[0];
|
||||
active = [];
|
||||
|
||||
// then highlight *to* this line
|
||||
if (lineNumber < i) {
|
||||
// highlight *up* to this new number
|
||||
// reduce highlight to this point
|
||||
for (; i >= lineNumber; i--) {
|
||||
active.push(i);
|
||||
}
|
||||
} else {
|
||||
// reduce highlight to this point
|
||||
for (; i <= lineNumber; i++) {
|
||||
active.push(i);
|
||||
}
|
||||
}
|
||||
} else if (active.indexOf(lineNumber) === -1) {
|
||||
active = [lineNumber]; // only select one line
|
||||
}
|
||||
|
||||
// sort the line numbers so when the user gets them in the event, it's vaguely sane.
|
||||
active = active.sort(function (a, b) {
|
||||
return a - b;
|
||||
});
|
||||
|
||||
if (sameArray(cm.state.highlightedLines, active)) {
|
||||
clearHighlightedLines(cm);
|
||||
if (event) {
|
||||
// only signal if it came from a user action
|
||||
signal(cm, active);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
updateHighlightedLines(cm, active, event);
|
||||
}
|
||||
|
||||
function updateHighlightedLines(cm, active, event) {
|
||||
cm.operation(function() {
|
||||
clearHighlightedLines(cm);
|
||||
for (var i = 0; i < active.length; i++) {
|
||||
cm.addLineClass(active[i], 'wrap', WRAP_CLASS);
|
||||
cm.addLineClass(active[i], 'background', BACK_CLASS);
|
||||
}
|
||||
cm.state.highlightedLines = active;
|
||||
if (event) {
|
||||
// only signal if it came from a user action
|
||||
signal(cm, active);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function signal(cm, active) {
|
||||
CodeMirror.signal(cm, 'highlightLines', cm, active, parseArrayToString(active));
|
||||
}
|
||||
|
||||
function gutterClick(cm, lineNumber, gutter, event) {
|
||||
highlightLines(cm, lineNumber, event);
|
||||
}
|
||||
});
|
||||
38
public/js/vendor/cm_addons/lint/coffeescript-lint.js
vendored
Normal file
38
public/js/vendor/cm_addons/lint/coffeescript-lint.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js
|
||||
|
||||
// declare global: coffeelint
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.registerHelper("lint", "coffeescript", function(text, options) {
|
||||
var found = [];
|
||||
var parseError = function(err) {
|
||||
var loc = err.lineNumber;
|
||||
found.push({from: CodeMirror.Pos(loc-1, 0),
|
||||
to: CodeMirror.Pos(loc, 0),
|
||||
severity: err.level,
|
||||
message: err.message});
|
||||
};
|
||||
try {
|
||||
var res = coffeelint.lint(text, options);
|
||||
for(var i = 0; i < res.length; i++) {
|
||||
parseError(res[i]);
|
||||
}
|
||||
} catch(e) {
|
||||
found.push({from: CodeMirror.Pos(e.location.first_line, 0),
|
||||
to: CodeMirror.Pos(e.location.last_line, e.location.last_column),
|
||||
severity: 'error',
|
||||
message: e.message});
|
||||
}
|
||||
return found;
|
||||
});
|
||||
|
||||
});
|
||||
35
public/js/vendor/cm_addons/lint/css-lint.js
vendored
Normal file
35
public/js/vendor/cm_addons/lint/css-lint.js
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||
|
||||
// Depends on csslint.js from https://github.com/stubbornella/csslint
|
||||
|
||||
// declare global: CSSLint
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.registerHelper("lint", "css", function(text, options) {
|
||||
var found = [];
|
||||
if (!window.CSSLint) return found;
|
||||
var results = CSSLint.verify(text, options), messages = results.messages, message = null;
|
||||
for ( var i = 0; i < messages.length; i++) {
|
||||
message = messages[i];
|
||||
var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
|
||||
found.push({
|
||||
from: CodeMirror.Pos(startLine, startCol),
|
||||
to: CodeMirror.Pos(endLine, endCol),
|
||||
message: message.message,
|
||||
severity : message.type
|
||||
});
|
||||
}
|
||||
return found;
|
||||
});
|
||||
|
||||
});
|
||||
32
public/js/vendor/cm_addons/lint/html-lint.js
vendored
Normal file
32
public/js/vendor/cm_addons/lint/html-lint.js
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
// Depends on htmlhint.js from https://github.com/yaniswang/HTMLHint
|
||||
|
||||
// declare global: HTMLHint
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.registerHelper("lint", "htmlmixed", function(text, options) {
|
||||
var found = [];
|
||||
if (!window.HTMLHint) return found;
|
||||
var results = HTMLHint.verify(text, options), messages = results, message = null;
|
||||
for ( var i = 0; i < messages.length; i++) {
|
||||
message = messages[i];
|
||||
var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
|
||||
found.push({
|
||||
from: CodeMirror.Pos(startLine, startCol),
|
||||
to: CodeMirror.Pos(endLine, endCol),
|
||||
message: message.message,
|
||||
severity : message.type
|
||||
});
|
||||
}
|
||||
return found;
|
||||
});
|
||||
|
||||
});
|
||||
132
public/js/vendor/cm_addons/lint/javascript-lint.js
vendored
Normal file
132
public/js/vendor/cm_addons/lint/javascript-lint.js
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
// declare global: JSHINT
|
||||
|
||||
var bogus = [ "Dangerous comment" ];
|
||||
|
||||
var warnings = [ [ "Expected '{'",
|
||||
"Statement body should be inside '{ }' braces." ] ];
|
||||
|
||||
var errors = [ "Missing semicolon", "Extra comma", "Missing property name",
|
||||
"Unmatched ", " and instead saw", " is not defined",
|
||||
"Unclosed string", "Stopping, unable to continue" ];
|
||||
|
||||
function validator(text, options) {
|
||||
JSHINT(text, options);
|
||||
var errors = JSHINT.data().errors, result = [];
|
||||
if (errors) parseErrors(errors, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CodeMirror.registerHelper("lint", "javascript", validator);
|
||||
|
||||
function cleanup(error) {
|
||||
// All problems are warnings by default
|
||||
fixWith(error, warnings, "warning", true);
|
||||
fixWith(error, errors, "error");
|
||||
|
||||
return isBogus(error) ? null : error;
|
||||
}
|
||||
|
||||
function fixWith(error, fixes, severity, force) {
|
||||
var description, fix, find, replace, found;
|
||||
|
||||
description = error.description;
|
||||
|
||||
for ( var i = 0; i < fixes.length; i++) {
|
||||
fix = fixes[i];
|
||||
find = (typeof fix === "string" ? fix : fix[0]);
|
||||
replace = (typeof fix === "string" ? null : fix[1]);
|
||||
found = description.indexOf(find) !== -1;
|
||||
|
||||
if (force || found) {
|
||||
error.severity = severity;
|
||||
}
|
||||
if (found && replace) {
|
||||
error.description = replace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isBogus(error) {
|
||||
var description = error.description;
|
||||
for ( var i = 0; i < bogus.length; i++) {
|
||||
if (description.indexOf(bogus[i]) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function parseErrors(errors, output) {
|
||||
for ( var i = 0; i < errors.length; i++) {
|
||||
var error = errors[i];
|
||||
if (error) {
|
||||
var linetabpositions, index;
|
||||
|
||||
linetabpositions = [];
|
||||
|
||||
// This next block is to fix a problem in jshint. Jshint
|
||||
// replaces
|
||||
// all tabs with spaces then performs some checks. The error
|
||||
// positions (character/space) are then reported incorrectly,
|
||||
// not taking the replacement step into account. Here we look
|
||||
// at the evidence line and try to adjust the character position
|
||||
// to the correct value.
|
||||
if (error.evidence) {
|
||||
// Tab positions are computed once per line and cached
|
||||
var tabpositions = linetabpositions[error.line];
|
||||
if (!tabpositions) {
|
||||
var evidence = error.evidence;
|
||||
tabpositions = [];
|
||||
// ugggh phantomjs does not like this
|
||||
// forEachChar(evidence, function(item, index) {
|
||||
Array.prototype.forEach.call(evidence, function(item,
|
||||
index) {
|
||||
if (item === '\t') {
|
||||
// First col is 1 (not 0) to match error
|
||||
// positions
|
||||
tabpositions.push(index + 1);
|
||||
}
|
||||
});
|
||||
linetabpositions[error.line] = tabpositions;
|
||||
}
|
||||
if (tabpositions.length > 0) {
|
||||
var pos = error.character;
|
||||
tabpositions.forEach(function(tabposition) {
|
||||
if (pos > tabposition) pos -= 1;
|
||||
});
|
||||
error.character = pos;
|
||||
}
|
||||
}
|
||||
|
||||
var start = error.character - 1, end = start + 1;
|
||||
if (error.evidence) {
|
||||
index = error.evidence.substring(start).search(/.\b/);
|
||||
if (index > -1) {
|
||||
end += index;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to format expected by validation service
|
||||
error.description = error.reason;// + "(jshint)";
|
||||
error.start = error.character;
|
||||
error.end = end;
|
||||
error = cleanup(error);
|
||||
|
||||
if (error)
|
||||
output.push({message: error.description,
|
||||
severity: error.severity,
|
||||
from: CodeMirror.Pos(error.line - 1, start),
|
||||
to: CodeMirror.Pos(error.line - 1, end)});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
140
public/js/vendor/cm_addons/lint/lint.css
vendored
Normal file
140
public/js/vendor/cm_addons/lint/lint.css
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
/* The lint marker gutter */
|
||||
.CodeMirror-lint-markers {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-tooltip {
|
||||
background-color: infobackground;
|
||||
border: 1px solid black;
|
||||
border-radius: 4px 4px 4px 4px;
|
||||
color: infotext;
|
||||
font-family: monospace;
|
||||
font-size: 10pt;
|
||||
overflow: hidden;
|
||||
padding: 2px 5px;
|
||||
position: fixed;
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
z-index: 100;
|
||||
max-width: 600px;
|
||||
opacity: 0;
|
||||
transition: opacity .4s;
|
||||
-moz-transition: opacity .4s;
|
||||
-webkit-transition: opacity .4s;
|
||||
-o-transition: opacity .4s;
|
||||
-ms-transition: opacity .4s;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
|
||||
background-position: left bottom;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-mark-error {
|
||||
background-image:
|
||||
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
|
||||
;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-mark-warning {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
|
||||
}
|
||||
|
||||
.lint-icon-warning:before,
|
||||
.lint-icon-error:before,
|
||||
.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
|
||||
padding-left: 18px;
|
||||
background-position: top left;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.lint-icon-error:before,
|
||||
.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
|
||||
}
|
||||
|
||||
.lint-icon-warning:before,
|
||||
.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
|
||||
}
|
||||
|
||||
.CodeMirror-lint-marker-multiple {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right bottom;
|
||||
width: 100%; height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.lint-icon-warning:before,
|
||||
.lint-icon-error:before {
|
||||
content: '';
|
||||
}
|
||||
.lint-icon-warning.dis:before,
|
||||
.lint-icon-error.dis:before {
|
||||
filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale"); /* Firefox 10+, Firefox on Android */
|
||||
filter: gray; /* IE6-9 */
|
||||
-webkit-filter: grayscale(100%);
|
||||
}
|
||||
.lint-icon-error.dis:before {
|
||||
-webkit-filter: grayscale(100%) invert(85%);
|
||||
}
|
||||
.lint-error {
|
||||
background: #FFA;
|
||||
font-size: 80%;
|
||||
color: #A00;
|
||||
padding: 2px 5px 3px;
|
||||
}
|
||||
.console-wrapper {
|
||||
background: #FFF;
|
||||
color: #333;
|
||||
}
|
||||
.console-log-head {
|
||||
background: #ecf2fa;
|
||||
border: 1px solid #CCC;
|
||||
border-top: 0 none;
|
||||
color: #6293d3;
|
||||
cursor: pointer;
|
||||
float: none;
|
||||
font-size: 13px;
|
||||
height: auto;
|
||||
line-height: 19px;
|
||||
margin: 0;
|
||||
padding: 0.2em 0.2em 0.2em 1em;
|
||||
width: auto;
|
||||
}
|
||||
.console-log {
|
||||
border: 1px solid #CCC;
|
||||
border-top: 0 none;
|
||||
font-size: 13px;
|
||||
height: 10.5em;
|
||||
overflow: auto;
|
||||
}
|
||||
.console-log-line {
|
||||
border-top: 1px solid #CCC;
|
||||
cursor: pointer;
|
||||
padding: 0.2em;
|
||||
}
|
||||
.console-log-line:first-child {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.console-log-line:before {
|
||||
display: inline-block;
|
||||
min-width: 1em;
|
||||
padding-right: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
#console-log-line-selected {
|
||||
background: #AFF;
|
||||
}
|
||||
425
public/js/vendor/cm_addons/lint/lint.js
vendored
Normal file
425
public/js/vendor/cm_addons/lint/lint.js
vendored
Normal file
@ -0,0 +1,425 @@
|
||||
(function(mod) {
|
||||
if (typeof exports == 'object' && typeof module == 'object') // CommonJS
|
||||
mod(require('../../lib/codemirror'));
|
||||
else if (typeof define == 'function' && define.amd) // AMD
|
||||
define(['../../lib/codemirror'], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
'use strict';
|
||||
var GUTTER_ID = 'CodeMirror-lint-markers';
|
||||
var SEVERITIES = /^(?:error|warning)$/;
|
||||
|
||||
function showTooltip(e, content) {
|
||||
var tt = document.createElement('div');
|
||||
tt.className = 'CodeMirror-lint-tooltip';
|
||||
tt.appendChild(content.cloneNode(true));
|
||||
document.body.appendChild(tt);
|
||||
|
||||
function position(e) {
|
||||
if (!tt.parentNode) return CodeMirror.off(document, 'mousemove', position);
|
||||
tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + 'px';
|
||||
tt.style.left = (e.clientX + 5) + 'px';
|
||||
}
|
||||
CodeMirror.on(document, 'mousemove', position);
|
||||
position(e);
|
||||
if (tt.style.opacity != null) tt.style.opacity = 1;
|
||||
return tt;
|
||||
}
|
||||
function rm(elt) {
|
||||
if (elt.parentNode) elt.parentNode.removeChild(elt);
|
||||
}
|
||||
function hideTooltip(tt) {
|
||||
if (!tt.parentNode) return;
|
||||
if (tt.style.opacity == null) rm(tt);
|
||||
tt.style.opacity = 0;
|
||||
setTimeout(function() { rm(tt); }, 600);
|
||||
}
|
||||
|
||||
function showTooltipFor(e, content, node) {
|
||||
var tooltip = showTooltip(e, content);
|
||||
function hide() {
|
||||
CodeMirror.off(node, 'mouseout', hide);
|
||||
if (tooltip) { hideTooltip(tooltip); tooltip = null; }
|
||||
}
|
||||
var poll = setInterval(function() {
|
||||
if (tooltip) for (var n = node;; n = n.parentNode) {
|
||||
if (n == document.body) return;
|
||||
if (!n) { hide(); break; }
|
||||
}
|
||||
if (!tooltip) return clearInterval(poll);
|
||||
}, 400);
|
||||
CodeMirror.on(node, 'mouseout', hide);
|
||||
}
|
||||
|
||||
function LintState(cm, options, hasGutter) {
|
||||
this.lineWidgets = [];
|
||||
this.marked = [];
|
||||
this.options = options;
|
||||
this.timeout = null;
|
||||
this.hasGutter = hasGutter;
|
||||
this.onMouseOver = function(e) { onMouseOver(cm, e); };
|
||||
}
|
||||
|
||||
function parseOptions(cm, options) {
|
||||
if (options instanceof Function) return {getAnnotations: options};
|
||||
if (!options || options === true) options = {};
|
||||
if (!options.getAnnotations) options.getAnnotations = cm.getHelper(CodeMirror.Pos(0, 0), 'lint');
|
||||
if (!options.getAnnotations && cm.getOption('mode') === 'htmlmixed') {
|
||||
options.getAnnotations = CodeMirror.helpers.lint.htmlmixed;
|
||||
}
|
||||
if (!options.getAnnotations && cm.getOption('mode') === 'coffeescript') {
|
||||
options.getAnnotations = CodeMirror.helpers.lint.coffeescript;
|
||||
}
|
||||
if (!options.getAnnotations) throw new Error('Required option "getAnnotations" missing (lint addon)');
|
||||
return options;
|
||||
}
|
||||
|
||||
function underClear(cm) {
|
||||
var state = cm.state.lint;
|
||||
for (var i = 0; i < state.marked.length; ++i)
|
||||
state.marked[i].clear();
|
||||
state.marked.length = 0;
|
||||
}
|
||||
|
||||
function gutterMarkerDraw(labels, severity, multiple, tooltip) {
|
||||
var marker = document.createElement('div'), inner = marker;
|
||||
marker.className = 'CodeMirror-lint-marker-' + severity;
|
||||
if (multiple) {
|
||||
inner = marker.appendChild(document.createElement('div'));
|
||||
inner.className = 'CodeMirror-lint-marker-multiple';
|
||||
}
|
||||
|
||||
if (tooltip) {
|
||||
CodeMirror.on(inner, 'mouseover', function(e) {
|
||||
showTooltipFor(e, labels, inner);
|
||||
});
|
||||
}
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
function getMaxSeverity(a, b) {
|
||||
if (a == 'error') return a;
|
||||
else return b;
|
||||
}
|
||||
|
||||
function groupByLine(annotations) {
|
||||
var lines = [];
|
||||
for (var i = 0; i < annotations.length; ++i) {
|
||||
var ann = annotations[i], line = ann.from.line;
|
||||
(lines[line] || (lines[line] = [])).push(ann);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
function annotationTooltip(ann, severity) {
|
||||
var tip = document.createElement('div');
|
||||
tip.className = 'CodeMirror-lint-message-' + severity;
|
||||
tip.appendChild(document.createTextNode(ann.message));
|
||||
return tip;
|
||||
}
|
||||
|
||||
function gutterDraw(line, tipLabel, maxSeverity, anns, state, cm) {
|
||||
cm.setGutterMarker(line, GUTTER_ID, gutterMarkerDraw(tipLabel, maxSeverity, anns.length > 1, cm.options.lintOpt.tooltip));
|
||||
}
|
||||
|
||||
function gutterReset(cm) {
|
||||
cm.clearGutter(GUTTER_ID);
|
||||
}
|
||||
|
||||
function underDraw(ann, severity, state, cm) {
|
||||
state.marked.push(cm.markText(ann.from, ann.to, {
|
||||
className: 'CodeMirror-lint-mark-' + severity,
|
||||
__annotation: ann
|
||||
}));
|
||||
}
|
||||
|
||||
function consoleInit(cm) {
|
||||
var wrapper = document.createElement('details');
|
||||
var head = document.createElement('summary');
|
||||
var logs = document.createElement('div');
|
||||
wrapper.className = 'console-wrapper';
|
||||
head.className = 'console-log-head';
|
||||
logs.className = 'console-log';
|
||||
wrapper.appendChild(head);
|
||||
wrapper.appendChild(logs);
|
||||
cm.consolelint = {
|
||||
wrapper: wrapper,
|
||||
logs: logs,
|
||||
head: head,
|
||||
error: 0,
|
||||
warning: 0
|
||||
};
|
||||
cm.options.lintOpt.consoleParent.appendChild(wrapper);
|
||||
CodeMirror.on(logs, 'click', function(event) {
|
||||
consoleClick(event, cm);
|
||||
});
|
||||
}
|
||||
|
||||
function consoleLine(ann, severity, cm) {
|
||||
cm.consolelint[severity]++;
|
||||
var line = document.createElement('div');
|
||||
line.className = 'console-log-line lint-icon-' + severity;
|
||||
line.setAttribute('data-ch', ann.from.ch);
|
||||
line.setAttribute('data-line', ann.from.line);
|
||||
line.setAttribute('data-reason', ann.message);
|
||||
line.appendChild(document.createTextNode('Line ' + (ann.from.line + 1) + ': ' + ann.message));
|
||||
return line;
|
||||
}
|
||||
|
||||
function consoleReset(cm) {
|
||||
cm.consolelint.error = 0;
|
||||
cm.consolelint.warning = 0;
|
||||
cm.consolelint.logs.innerHTML = '';
|
||||
}
|
||||
|
||||
function consoleHeadUpdate(cm) {
|
||||
var counterEclass = '';
|
||||
var counterWclass = '';
|
||||
var es = 's';
|
||||
var ws = 's';
|
||||
if (cm.consolelint.error === 0) counterEclass = ' dis';
|
||||
if (cm.consolelint.warning === 0) counterWclass = ' dis';
|
||||
if (cm.consolelint.error === 1) es = '';
|
||||
if (cm.consolelint.warning === 1) ws = '';
|
||||
cm.consolelint.head.innerHTML = '';
|
||||
if (counterEclass && counterWclass) {
|
||||
cm.consolelint.head.style.display = 'none';
|
||||
cm.consolelint.logs.style.display = 'none';
|
||||
$document.trigger('sizeeditors');
|
||||
return;
|
||||
}
|
||||
cm.consolelint.head.style.display = '';
|
||||
cm.consolelint.logs.style.display = '';
|
||||
if (!counterEclass) {
|
||||
cm.consolelint.head.innerHTML += '<i class="lint-icon-error' + counterEclass + '"></i> ' +
|
||||
cm.consolelint.error + ' error' + es + ' ';
|
||||
}
|
||||
if (!counterWclass) {
|
||||
cm.consolelint.head.innerHTML += '<i class="lint-icon-warning' + counterWclass + '"></i> ' +
|
||||
cm.consolelint.warning + ' warning' + ws;
|
||||
}
|
||||
$document.trigger('sizeeditors');
|
||||
}
|
||||
|
||||
function consoleClick(event, cm) {
|
||||
var target = event.target;
|
||||
if (target.className.indexOf('console-log-line') !== -1) {
|
||||
var ch = target.getAttribute('data-ch') * 1;
|
||||
var line = target.getAttribute('data-line') * 1;
|
||||
var reason = target.getAttribute('data-reason');
|
||||
var lineHeight = cm.defaultTextHeight();
|
||||
cm.setCursor({ line: line, ch: ch });
|
||||
cm.scrollIntoView(null, lineHeight * 3);
|
||||
cm.focus();
|
||||
|
||||
var old = document.getElementById('console-log-line-selected');
|
||||
if (old) {
|
||||
old.id = '';
|
||||
}
|
||||
target.id = 'console-log-line-selected';
|
||||
|
||||
if (cm.options.lintOpt.line) {
|
||||
lineUpdate({ reason: reason, line: line }, cm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lineUpdate(ann, cm) {
|
||||
lineReset(cm);
|
||||
var msg = document.createElement('div');
|
||||
msg.appendChild(document.createTextNode(ann.reason));
|
||||
msg.className = 'lint-error';
|
||||
cm.state.lint.lineWidgets.push(cm.addLineWidget(ann.line * 1, msg, { coverGutter: false, noHScroll: true }));
|
||||
}
|
||||
|
||||
function lineReset(cm) {
|
||||
var lineWidgets = cm.state.lint.lineWidgets;
|
||||
for (var i = 0; i < lineWidgets.length; ++i) {
|
||||
cm.removeLineWidget(lineWidgets[i]);
|
||||
}
|
||||
lineWidgets.length = 0;
|
||||
}
|
||||
|
||||
function startLinting(cm) {
|
||||
var state = cm.state.lint, options = state.options;
|
||||
if (options.async)
|
||||
options.getAnnotations(cm, updateLinting, cm.options.lintRules);
|
||||
else
|
||||
updateLinting(cm, options.getAnnotations(cm.getValue(), cm.options.lintRules));
|
||||
}
|
||||
|
||||
function updateLinting(cm, annotationsNotSorted) {
|
||||
var state = cm.state.lint, options = state.options;
|
||||
|
||||
if (cm.options.lintOpt.under) {
|
||||
underClear(cm);
|
||||
}
|
||||
|
||||
if (state.hasGutter) {
|
||||
gutterReset(cm);
|
||||
}
|
||||
|
||||
var annotations = groupByLine(annotationsNotSorted);
|
||||
|
||||
if (cm.options.lintOpt.console) {
|
||||
consoleReset(cm);
|
||||
if (cm.options.lintOpt.line) {
|
||||
lineReset(cm);
|
||||
}
|
||||
}
|
||||
|
||||
for (var line = 0; line < annotations.length; ++line) {
|
||||
var anns = annotations[line];
|
||||
if (!anns) continue;
|
||||
|
||||
var maxSeverity = null;
|
||||
var tipLabel = state.hasGutter && document.createDocumentFragment();
|
||||
|
||||
for (var i = 0; i < anns.length; ++i) {
|
||||
var ann = anns[i];
|
||||
var severity = ann.severity;
|
||||
if (!SEVERITIES.test(severity)) severity = 'error';
|
||||
maxSeverity = getMaxSeverity(maxSeverity, severity);
|
||||
|
||||
if (options.formatAnnotation) ann = options.formatAnnotation(ann);
|
||||
|
||||
if (state.hasGutter) {
|
||||
tipLabel.appendChild(annotationTooltip(ann, severity));
|
||||
}
|
||||
|
||||
if (cm.options.lintOpt.console) {
|
||||
cm.consolelint.logs.appendChild(consoleLine(ann, severity, cm));
|
||||
}
|
||||
|
||||
if (cm.options.lintOpt.under) {
|
||||
if (ann.to) {
|
||||
underDraw(ann, severity, state, cm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.hasGutter) {
|
||||
gutterDraw(line, tipLabel, maxSeverity, anns, state, cm);
|
||||
}
|
||||
}
|
||||
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
|
||||
|
||||
if (cm.options.lintOpt.console) {
|
||||
consoleHeadUpdate(cm);
|
||||
}
|
||||
}
|
||||
|
||||
function onChange(cm) {
|
||||
var state = cm.state.lint;
|
||||
clearTimeout(state.timeout);
|
||||
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
|
||||
}
|
||||
|
||||
function popupSpanTooltip(ann, e) {
|
||||
var target = e.target || e.srcElement;
|
||||
showTooltipFor(e, annotationTooltip(ann), target);
|
||||
}
|
||||
|
||||
// When the mouseover fires, the cursor might not actually be over
|
||||
// the character itself yet. These pairs of x,y offsets are used to
|
||||
// probe a few nearby points when no suitable marked range is found.
|
||||
var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0];
|
||||
|
||||
function onMouseOver(cm, e) {
|
||||
if (!/\bCodeMirror-lint-mark-/.test((e.target || e.srcElement).className)) return;
|
||||
for (var i = 0; i < nearby.length; i += 2) {
|
||||
var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i],
|
||||
top: e.clientY + nearby[i + 1]}, 'client'));
|
||||
for (var j = 0; j < spans.length; ++j) {
|
||||
var span = spans[j], ann = span.__annotation;
|
||||
if (ann) return popupSpanTooltip(ann, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lintStop(cm) {
|
||||
lineReset(cm);
|
||||
cm.setOption('lint', false);
|
||||
var opt = cm.getOption('lintOpt');
|
||||
if (opt.gutter) {
|
||||
var gutters = cm.getOption('gutters');
|
||||
var pos = gutters.indexOf('CodeMirror-lint-markers');
|
||||
if (pos !== -1) {
|
||||
gutters.splice(pos, 1);
|
||||
cm.setOption('gutters', gutters);
|
||||
var ln = cm.getOption('lineNumbers');
|
||||
cm.setOption('lineNumbers', !ln);
|
||||
cm.setOption('lineNumbers', ln);
|
||||
}
|
||||
}
|
||||
if (opt.console) {
|
||||
opt.consoleParent.removeChild(cm.consolelint.wrapper);
|
||||
$document.trigger('sizeeditors');
|
||||
}
|
||||
cm.refresh();
|
||||
}
|
||||
|
||||
CodeMirror.defineOption('lint', false, function(cm, val, old) {
|
||||
var defaults = {
|
||||
console: true,
|
||||
consoleParent: cm.getWrapperElement().parentNode,
|
||||
line: true,
|
||||
under: true,
|
||||
tooltip: true
|
||||
};
|
||||
if (!cm.options.lintOpt) {
|
||||
cm.options.lintOpt = {};
|
||||
}
|
||||
for (var key in defaults) {
|
||||
if (defaults.hasOwnProperty(key)) {
|
||||
cm.options.lintOpt[key] = (cm.options.lintOpt[key] !== undefined) ? cm.options.lintOpt[key] : defaults[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!cm.options.lintRules) {
|
||||
cm.options.lintRules = {};
|
||||
}
|
||||
|
||||
if (old && old != CodeMirror.Init) {
|
||||
if (cm.options.lintOpt.under) {
|
||||
underClear(cm);
|
||||
}
|
||||
|
||||
if (cm.state.lint.hasGutter) {
|
||||
gutterReset(cm);
|
||||
}
|
||||
|
||||
cm.off('change', onChange);
|
||||
CodeMirror.off(cm.getWrapperElement(), 'mouseover', cm.state.lint.onMouseOver);
|
||||
delete cm.state.lint;
|
||||
}
|
||||
|
||||
if (val) {
|
||||
var gutters = cm.getOption('gutters'), hasLintGutter = false;
|
||||
for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
|
||||
var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
|
||||
cm.on('change', onChange);
|
||||
|
||||
if (cm.options.lintOpt.console) {
|
||||
consoleInit(cm);
|
||||
}
|
||||
|
||||
if (cm.options.lintOpt.tooltip)
|
||||
CodeMirror.on(cm.getWrapperElement(), 'mouseover', state.onMouseOver);
|
||||
|
||||
startLinting(cm);
|
||||
|
||||
cm.lintStop = function() {
|
||||
return lintStop(this);
|
||||
};
|
||||
}
|
||||
|
||||
// probably to improve according to real case scenarios
|
||||
// cm.updateLinting = function(annotationsNotSorted) {
|
||||
// updateLinting(cm, annotationsNotSorted);
|
||||
// console.log('ciao');
|
||||
// };
|
||||
});
|
||||
});
|
||||
2324
public/js/vendor/coffeelint/coffeelint.js
vendored
Normal file
2324
public/js/vendor/coffeelint/coffeelint.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
public/js/vendor/coffeelint/coffeelint.min.js
vendored
Normal file
1
public/js/vendor/coffeelint/coffeelint.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9259
public/js/vendor/csslint/csslint.js
vendored
Normal file
9259
public/js/vendor/csslint/csslint.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
public/js/vendor/csslint/csslint.min.js
vendored
Normal file
1
public/js/vendor/csslint/csslint.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
8
public/js/vendor/htmlhint/htmlhint.js
vendored
Normal file
8
public/js/vendor/htmlhint/htmlhint.js
vendored
Normal file
File diff suppressed because one or more lines are too long
64723
public/js/vendor/jshint/jshint.js
vendored
64723
public/js/vendor/jshint/jshint.js
vendored
File diff suppressed because it is too large
Load Diff
1
public/js/vendor/jshint/jshint.min.js
vendored
Normal file
1
public/js/vendor/jshint/jshint.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4748
public/js/vendor/jshint/jshint.old.js
vendored
Normal file
4748
public/js/vendor/jshint/jshint.old.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4
public/js/vendor/jshint/jshint.old.min.js
vendored
Normal file
4
public/js/vendor/jshint/jshint.old.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
public/js/vendor/less-1.4.2.min.js
vendored
11
public/js/vendor/less-1.4.2.min.js
vendored
File diff suppressed because one or more lines are too long
16
public/js/vendor/less-1.7.3.min.js
vendored
Normal file
16
public/js/vendor/less-1.7.3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
665
public/js/vendor/lz-string-1.3.3.js
vendored
Normal file
665
public/js/vendor/lz-string-1.3.3.js
vendored
Normal file
@ -0,0 +1,665 @@
|
||||
// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
|
||||
// This work is free. You can redistribute it and/or modify it
|
||||
// under the terms of the WTFPL, Version 2
|
||||
// For more information see LICENSE.txt or http://www.wtfpl.net/
|
||||
//
|
||||
// For more information, the home page:
|
||||
// http://pieroxy.net/blog/pages/lz-string/testing.html
|
||||
//
|
||||
// LZ-based compression algorithm, version 1.3.3
|
||||
var LZString = {
|
||||
|
||||
|
||||
// private property
|
||||
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
||||
_f : String.fromCharCode,
|
||||
|
||||
compressToBase64 : function (input) {
|
||||
if (input == null) return "";
|
||||
var output = "";
|
||||
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
||||
var i = 0;
|
||||
|
||||
input = LZString.compress(input);
|
||||
|
||||
while (i < input.length*2) {
|
||||
|
||||
if (i%2==0) {
|
||||
chr1 = input.charCodeAt(i/2) >> 8;
|
||||
chr2 = input.charCodeAt(i/2) & 255;
|
||||
if (i/2+1 < input.length)
|
||||
chr3 = input.charCodeAt(i/2+1) >> 8;
|
||||
else
|
||||
chr3 = NaN;
|
||||
} else {
|
||||
chr1 = input.charCodeAt((i-1)/2) & 255;
|
||||
if ((i+1)/2 < input.length) {
|
||||
chr2 = input.charCodeAt((i+1)/2) >> 8;
|
||||
chr3 = input.charCodeAt((i+1)/2) & 255;
|
||||
} else
|
||||
chr2=chr3=NaN;
|
||||
}
|
||||
i+=3;
|
||||
|
||||
enc1 = chr1 >> 2;
|
||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
||||
enc4 = chr3 & 63;
|
||||
|
||||
if (isNaN(chr2)) {
|
||||
enc3 = enc4 = 64;
|
||||
} else if (isNaN(chr3)) {
|
||||
enc4 = 64;
|
||||
}
|
||||
|
||||
output = output +
|
||||
LZString._keyStr.charAt(enc1) + LZString._keyStr.charAt(enc2) +
|
||||
LZString._keyStr.charAt(enc3) + LZString._keyStr.charAt(enc4);
|
||||
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
decompressFromBase64 : function (input) {
|
||||
if (input == null) return "";
|
||||
var output = "",
|
||||
ol = 0,
|
||||
output_,
|
||||
chr1, chr2, chr3,
|
||||
enc1, enc2, enc3, enc4,
|
||||
i = 0, f=LZString._f;
|
||||
|
||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
|
||||
|
||||
while (i < input.length) {
|
||||
|
||||
enc1 = LZString._keyStr.indexOf(input.charAt(i++));
|
||||
enc2 = LZString._keyStr.indexOf(input.charAt(i++));
|
||||
enc3 = LZString._keyStr.indexOf(input.charAt(i++));
|
||||
enc4 = LZString._keyStr.indexOf(input.charAt(i++));
|
||||
|
||||
chr1 = (enc1 << 2) | (enc2 >> 4);
|
||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
||||
chr3 = ((enc3 & 3) << 6) | enc4;
|
||||
|
||||
if (ol%2==0) {
|
||||
output_ = chr1 << 8;
|
||||
|
||||
if (enc3 != 64) {
|
||||
output += f(output_ | chr2);
|
||||
}
|
||||
if (enc4 != 64) {
|
||||
output_ = chr3 << 8;
|
||||
}
|
||||
} else {
|
||||
output = output + f(output_ | chr1);
|
||||
|
||||
if (enc3 != 64) {
|
||||
output_ = chr2 << 8;
|
||||
}
|
||||
if (enc4 != 64) {
|
||||
output += f(output_ | chr3);
|
||||
}
|
||||
}
|
||||
ol+=3;
|
||||
}
|
||||
|
||||
return LZString.decompress(output);
|
||||
|
||||
},
|
||||
|
||||
compressToUTF16 : function (input) {
|
||||
if (input == null) return "";
|
||||
var output = "",
|
||||
i,c,
|
||||
current,
|
||||
status = 0,
|
||||
f = LZString._f;
|
||||
|
||||
input = LZString.compress(input);
|
||||
|
||||
for (i=0 ; i<input.length ; i++) {
|
||||
c = input.charCodeAt(i);
|
||||
switch (status++) {
|
||||
case 0:
|
||||
output += f((c >> 1)+32);
|
||||
current = (c & 1) << 14;
|
||||
break;
|
||||
case 1:
|
||||
output += f((current + (c >> 2))+32);
|
||||
current = (c & 3) << 13;
|
||||
break;
|
||||
case 2:
|
||||
output += f((current + (c >> 3))+32);
|
||||
current = (c & 7) << 12;
|
||||
break;
|
||||
case 3:
|
||||
output += f((current + (c >> 4))+32);
|
||||
current = (c & 15) << 11;
|
||||
break;
|
||||
case 4:
|
||||
output += f((current + (c >> 5))+32);
|
||||
current = (c & 31) << 10;
|
||||
break;
|
||||
case 5:
|
||||
output += f((current + (c >> 6))+32);
|
||||
current = (c & 63) << 9;
|
||||
break;
|
||||
case 6:
|
||||
output += f((current + (c >> 7))+32);
|
||||
current = (c & 127) << 8;
|
||||
break;
|
||||
case 7:
|
||||
output += f((current + (c >> 8))+32);
|
||||
current = (c & 255) << 7;
|
||||
break;
|
||||
case 8:
|
||||
output += f((current + (c >> 9))+32);
|
||||
current = (c & 511) << 6;
|
||||
break;
|
||||
case 9:
|
||||
output += f((current + (c >> 10))+32);
|
||||
current = (c & 1023) << 5;
|
||||
break;
|
||||
case 10:
|
||||
output += f((current + (c >> 11))+32);
|
||||
current = (c & 2047) << 4;
|
||||
break;
|
||||
case 11:
|
||||
output += f((current + (c >> 12))+32);
|
||||
current = (c & 4095) << 3;
|
||||
break;
|
||||
case 12:
|
||||
output += f((current + (c >> 13))+32);
|
||||
current = (c & 8191) << 2;
|
||||
break;
|
||||
case 13:
|
||||
output += f((current + (c >> 14))+32);
|
||||
current = (c & 16383) << 1;
|
||||
break;
|
||||
case 14:
|
||||
output += f((current + (c >> 15))+32, (c & 32767)+32);
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return output + f(current + 32);
|
||||
},
|
||||
|
||||
|
||||
decompressFromUTF16 : function (input) {
|
||||
if (input == null) return "";
|
||||
var output = "",
|
||||
current,c,
|
||||
status=0,
|
||||
i = 0,
|
||||
f = LZString._f;
|
||||
|
||||
while (i < input.length) {
|
||||
c = input.charCodeAt(i) - 32;
|
||||
|
||||
switch (status++) {
|
||||
case 0:
|
||||
current = c << 1;
|
||||
break;
|
||||
case 1:
|
||||
output += f(current | (c >> 14));
|
||||
current = (c&16383) << 2;
|
||||
break;
|
||||
case 2:
|
||||
output += f(current | (c >> 13));
|
||||
current = (c&8191) << 3;
|
||||
break;
|
||||
case 3:
|
||||
output += f(current | (c >> 12));
|
||||
current = (c&4095) << 4;
|
||||
break;
|
||||
case 4:
|
||||
output += f(current | (c >> 11));
|
||||
current = (c&2047) << 5;
|
||||
break;
|
||||
case 5:
|
||||
output += f(current | (c >> 10));
|
||||
current = (c&1023) << 6;
|
||||
break;
|
||||
case 6:
|
||||
output += f(current | (c >> 9));
|
||||
current = (c&511) << 7;
|
||||
break;
|
||||
case 7:
|
||||
output += f(current | (c >> 8));
|
||||
current = (c&255) << 8;
|
||||
break;
|
||||
case 8:
|
||||
output += f(current | (c >> 7));
|
||||
current = (c&127) << 9;
|
||||
break;
|
||||
case 9:
|
||||
output += f(current | (c >> 6));
|
||||
current = (c&63) << 10;
|
||||
break;
|
||||
case 10:
|
||||
output += f(current | (c >> 5));
|
||||
current = (c&31) << 11;
|
||||
break;
|
||||
case 11:
|
||||
output += f(current | (c >> 4));
|
||||
current = (c&15) << 12;
|
||||
break;
|
||||
case 12:
|
||||
output += f(current | (c >> 3));
|
||||
current = (c&7) << 13;
|
||||
break;
|
||||
case 13:
|
||||
output += f(current | (c >> 2));
|
||||
current = (c&3) << 14;
|
||||
break;
|
||||
case 14:
|
||||
output += f(current | (c >> 1));
|
||||
current = (c&1) << 15;
|
||||
break;
|
||||
case 15:
|
||||
output += f(current | c);
|
||||
status=0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return LZString.decompress(output);
|
||||
//return output;
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
compress: function (uncompressed) {
|
||||
if (uncompressed == null) return "";
|
||||
var i, value,
|
||||
context_dictionary= {},
|
||||
context_dictionaryToCreate= {},
|
||||
context_c="",
|
||||
context_wc="",
|
||||
context_w="",
|
||||
context_enlargeIn= 2, // Compensate for the first entry which should not count
|
||||
context_dictSize= 3,
|
||||
context_numBits= 2,
|
||||
context_data_string="",
|
||||
context_data_val=0,
|
||||
context_data_position=0,
|
||||
ii,
|
||||
f=LZString._f;
|
||||
|
||||
for (ii = 0; ii < uncompressed.length; ii += 1) {
|
||||
context_c = uncompressed.charAt(ii);
|
||||
if (!Object.prototype.hasOwnProperty.call(context_dictionary,context_c)) {
|
||||
context_dictionary[context_c] = context_dictSize++;
|
||||
context_dictionaryToCreate[context_c] = true;
|
||||
}
|
||||
|
||||
context_wc = context_w + context_c;
|
||||
if (Object.prototype.hasOwnProperty.call(context_dictionary,context_wc)) {
|
||||
context_w = context_wc;
|
||||
} else {
|
||||
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {
|
||||
if (context_w.charCodeAt(0)<256) {
|
||||
for (i=0 ; i<context_numBits ; i++) {
|
||||
context_data_val = (context_data_val << 1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
}
|
||||
value = context_w.charCodeAt(0);
|
||||
for (i=0 ; i<8 ; i++) {
|
||||
context_data_val = (context_data_val << 1) | (value&1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
value = value >> 1;
|
||||
}
|
||||
} else {
|
||||
value = 1;
|
||||
for (i=0 ; i<context_numBits ; i++) {
|
||||
context_data_val = (context_data_val << 1) | value;
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
value = 0;
|
||||
}
|
||||
value = context_w.charCodeAt(0);
|
||||
for (i=0 ; i<16 ; i++) {
|
||||
context_data_val = (context_data_val << 1) | (value&1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
value = value >> 1;
|
||||
}
|
||||
}
|
||||
context_enlargeIn--;
|
||||
if (context_enlargeIn == 0) {
|
||||
context_enlargeIn = Math.pow(2, context_numBits);
|
||||
context_numBits++;
|
||||
}
|
||||
delete context_dictionaryToCreate[context_w];
|
||||
} else {
|
||||
value = context_dictionary[context_w];
|
||||
for (i=0 ; i<context_numBits ; i++) {
|
||||
context_data_val = (context_data_val << 1) | (value&1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
value = value >> 1;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
context_enlargeIn--;
|
||||
if (context_enlargeIn == 0) {
|
||||
context_enlargeIn = Math.pow(2, context_numBits);
|
||||
context_numBits++;
|
||||
}
|
||||
// Add wc to the dictionary.
|
||||
context_dictionary[context_wc] = context_dictSize++;
|
||||
context_w = String(context_c);
|
||||
}
|
||||
}
|
||||
|
||||
// Output the code for w.
|
||||
if (context_w !== "") {
|
||||
if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) {
|
||||
if (context_w.charCodeAt(0)<256) {
|
||||
for (i=0 ; i<context_numBits ; i++) {
|
||||
context_data_val = (context_data_val << 1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
}
|
||||
value = context_w.charCodeAt(0);
|
||||
for (i=0 ; i<8 ; i++) {
|
||||
context_data_val = (context_data_val << 1) | (value&1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
value = value >> 1;
|
||||
}
|
||||
} else {
|
||||
value = 1;
|
||||
for (i=0 ; i<context_numBits ; i++) {
|
||||
context_data_val = (context_data_val << 1) | value;
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
value = 0;
|
||||
}
|
||||
value = context_w.charCodeAt(0);
|
||||
for (i=0 ; i<16 ; i++) {
|
||||
context_data_val = (context_data_val << 1) | (value&1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
value = value >> 1;
|
||||
}
|
||||
}
|
||||
context_enlargeIn--;
|
||||
if (context_enlargeIn == 0) {
|
||||
context_enlargeIn = Math.pow(2, context_numBits);
|
||||
context_numBits++;
|
||||
}
|
||||
delete context_dictionaryToCreate[context_w];
|
||||
} else {
|
||||
value = context_dictionary[context_w];
|
||||
for (i=0 ; i<context_numBits ; i++) {
|
||||
context_data_val = (context_data_val << 1) | (value&1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
value = value >> 1;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
context_enlargeIn--;
|
||||
if (context_enlargeIn == 0) {
|
||||
context_enlargeIn = Math.pow(2, context_numBits);
|
||||
context_numBits++;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the end of the stream
|
||||
value = 2;
|
||||
for (i=0 ; i<context_numBits ; i++) {
|
||||
context_data_val = (context_data_val << 1) | (value&1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_position = 0;
|
||||
context_data_string += f(context_data_val);
|
||||
context_data_val = 0;
|
||||
} else {
|
||||
context_data_position++;
|
||||
}
|
||||
value = value >> 1;
|
||||
}
|
||||
|
||||
// Flush the last char
|
||||
while (true) {
|
||||
context_data_val = (context_data_val << 1);
|
||||
if (context_data_position == 15) {
|
||||
context_data_string += f(context_data_val);
|
||||
break;
|
||||
}
|
||||
else context_data_position++;
|
||||
}
|
||||
return context_data_string;
|
||||
},
|
||||
|
||||
decompress: function (compressed) {
|
||||
if (compressed == null) return "";
|
||||
if (compressed == "") return null;
|
||||
var dictionary = [],
|
||||
next,
|
||||
enlargeIn = 4,
|
||||
dictSize = 4,
|
||||
numBits = 3,
|
||||
entry = "",
|
||||
result = "",
|
||||
i,
|
||||
w,
|
||||
bits, resb, maxpower, power,
|
||||
c,
|
||||
f = LZString._f,
|
||||
data = {string:compressed, val:compressed.charCodeAt(0), position:32768, index:1};
|
||||
|
||||
for (i = 0; i < 3; i += 1) {
|
||||
dictionary[i] = i;
|
||||
}
|
||||
|
||||
bits = 0;
|
||||
maxpower = Math.pow(2,2);
|
||||
power=1;
|
||||
while (power!=maxpower) {
|
||||
resb = data.val & data.position;
|
||||
data.position >>= 1;
|
||||
if (data.position == 0) {
|
||||
data.position = 32768;
|
||||
data.val = data.string.charCodeAt(data.index++);
|
||||
}
|
||||
bits |= (resb>0 ? 1 : 0) * power;
|
||||
power <<= 1;
|
||||
}
|
||||
|
||||
switch (next = bits) {
|
||||
case 0:
|
||||
bits = 0;
|
||||
maxpower = Math.pow(2,8);
|
||||
power=1;
|
||||
while (power!=maxpower) {
|
||||
resb = data.val & data.position;
|
||||
data.position >>= 1;
|
||||
if (data.position == 0) {
|
||||
data.position = 32768;
|
||||
data.val = data.string.charCodeAt(data.index++);
|
||||
}
|
||||
bits |= (resb>0 ? 1 : 0) * power;
|
||||
power <<= 1;
|
||||
}
|
||||
c = f(bits);
|
||||
break;
|
||||
case 1:
|
||||
bits = 0;
|
||||
maxpower = Math.pow(2,16);
|
||||
power=1;
|
||||
while (power!=maxpower) {
|
||||
resb = data.val & data.position;
|
||||
data.position >>= 1;
|
||||
if (data.position == 0) {
|
||||
data.position = 32768;
|
||||
data.val = data.string.charCodeAt(data.index++);
|
||||
}
|
||||
bits |= (resb>0 ? 1 : 0) * power;
|
||||
power <<= 1;
|
||||
}
|
||||
c = f(bits);
|
||||
break;
|
||||
case 2:
|
||||
return "";
|
||||
}
|
||||
dictionary[3] = c;
|
||||
w = result = c;
|
||||
while (true) {
|
||||
if (data.index > data.string.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
bits = 0;
|
||||
maxpower = Math.pow(2,numBits);
|
||||
power=1;
|
||||
while (power!=maxpower) {
|
||||
resb = data.val & data.position;
|
||||
data.position >>= 1;
|
||||
if (data.position == 0) {
|
||||
data.position = 32768;
|
||||
data.val = data.string.charCodeAt(data.index++);
|
||||
}
|
||||
bits |= (resb>0 ? 1 : 0) * power;
|
||||
power <<= 1;
|
||||
}
|
||||
|
||||
switch (c = bits) {
|
||||
case 0:
|
||||
bits = 0;
|
||||
maxpower = Math.pow(2,8);
|
||||
power=1;
|
||||
while (power!=maxpower) {
|
||||
resb = data.val & data.position;
|
||||
data.position >>= 1;
|
||||
if (data.position == 0) {
|
||||
data.position = 32768;
|
||||
data.val = data.string.charCodeAt(data.index++);
|
||||
}
|
||||
bits |= (resb>0 ? 1 : 0) * power;
|
||||
power <<= 1;
|
||||
}
|
||||
|
||||
dictionary[dictSize++] = f(bits);
|
||||
c = dictSize-1;
|
||||
enlargeIn--;
|
||||
break;
|
||||
case 1:
|
||||
bits = 0;
|
||||
maxpower = Math.pow(2,16);
|
||||
power=1;
|
||||
while (power!=maxpower) {
|
||||
resb = data.val & data.position;
|
||||
data.position >>= 1;
|
||||
if (data.position == 0) {
|
||||
data.position = 32768;
|
||||
data.val = data.string.charCodeAt(data.index++);
|
||||
}
|
||||
bits |= (resb>0 ? 1 : 0) * power;
|
||||
power <<= 1;
|
||||
}
|
||||
dictionary[dictSize++] = f(bits);
|
||||
c = dictSize-1;
|
||||
enlargeIn--;
|
||||
break;
|
||||
case 2:
|
||||
return result;
|
||||
}
|
||||
|
||||
if (enlargeIn == 0) {
|
||||
enlargeIn = Math.pow(2, numBits);
|
||||
numBits++;
|
||||
}
|
||||
|
||||
if (dictionary[c]) {
|
||||
entry = dictionary[c];
|
||||
} else {
|
||||
if (c === dictSize) {
|
||||
entry = w + w.charAt(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
result += entry;
|
||||
|
||||
// Add w+entry[0] to the dictionary.
|
||||
dictionary[dictSize++] = w + entry.charAt(0);
|
||||
enlargeIn--;
|
||||
|
||||
w = entry;
|
||||
|
||||
if (enlargeIn == 0) {
|
||||
enlargeIn = Math.pow(2, numBits);
|
||||
numBits++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if( typeof module !== 'undefined' && module != null ) {
|
||||
module.exports = LZString
|
||||
}
|
||||
9587
public/js/vendor/stylus.js
vendored
9587
public/js/vendor/stylus.js
vendored
File diff suppressed because one or more lines are too long
15
scripts.json
15
scripts.json
@ -16,6 +16,10 @@
|
||||
"/js/vendor/cm_addons/javascript-hint.js",
|
||||
"/js/vendor/codemirror4/addon/edit/matchbrackets.js",
|
||||
"/js/vendor/codemirror4/addon/comment/comment.js",
|
||||
"/js/vendor/cm_addons/lint/javascript-lint.js",
|
||||
"/js/vendor/cm_addons/lint/lint.js",
|
||||
"/js/vendor/lz-string-1.3.3.js",
|
||||
"/js/vendor/cm_addons/cm-highlight-line.js",
|
||||
"/js/editors/snippets.cm.js",
|
||||
"/js/vendor/json2.js",
|
||||
"/js/vendor/prettyprint.js",
|
||||
@ -25,13 +29,13 @@
|
||||
"/js/editors/mobileCodeMirror.js",
|
||||
"/js/chrome/splitter.js",
|
||||
"/js/chrome/analytics.js",
|
||||
"/js/chrome/settings.js",
|
||||
"/js/chrome/font.js",
|
||||
"/js/render/title.js",
|
||||
"/js/render/render.js",
|
||||
"/js/render/live.js",
|
||||
"/js/editors/keycontrol.js",
|
||||
"/js/render/console.js",
|
||||
"/js/chrome/errors.js",
|
||||
"/js/processors/processor.js",
|
||||
"/js/editors/panel.js",
|
||||
"/js/editors/editors.js",
|
||||
@ -39,6 +43,7 @@
|
||||
"/js/editors/library.js",
|
||||
"/js/editors/addons.js",
|
||||
"/js/editors/snapshot.js",
|
||||
"/js/editors/tern.js",
|
||||
"/js/render/saved-history-preview.js",
|
||||
"/js/chrome/share.js",
|
||||
"/js/chrome/esc.js",
|
||||
@ -53,6 +58,9 @@
|
||||
"/js/chrome/gist.js",
|
||||
"/js/chrome/spinner.js",
|
||||
"/js/chrome/infocard.js",
|
||||
"/js/chrome/last-bin.js",
|
||||
"/js/chrome/archive.js",
|
||||
"/js/chrome/welcome-panel.js",
|
||||
"/js/chrome/app.js"
|
||||
],
|
||||
"runner": [
|
||||
@ -79,8 +87,7 @@
|
||||
"/js/vendor/tern/lib/infer.js",
|
||||
"/js/vendor/tern/plugin/doc_comment.js",
|
||||
"/js/editors/defs.js",
|
||||
"/js/editors/definitions.js",
|
||||
"/js/editors/tern.js"
|
||||
"/js/editors/definitions.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,8 +35,8 @@
|
||||
<h1>Customise your JS Bin editor</h1>
|
||||
|
||||
<section id="content">
|
||||
<p>As soon as you make any changes, they will be automatically saved.</p>
|
||||
|
||||
<p>Tweak JS Bin's editor to your liking and manage the addons loaded.</p>
|
||||
|
||||
<form id="editor-settings">
|
||||
<h3>Settings</h3>
|
||||
<div>
|
||||
@ -47,7 +47,7 @@
|
||||
<label for="theme">Theme</label>
|
||||
<select id="theme" name="theme">
|
||||
<option selected>jsbin</option>
|
||||
<option value="default">CodeMirror default</option>
|
||||
<option value="default">codemirror</option>
|
||||
<option>3024-day</option>
|
||||
<option>3024-night</option>
|
||||
<option>ambiance</option>
|
||||
@ -118,6 +118,12 @@
|
||||
<label for="closebrackets" class="mini-desc">Auto-close brackets and quotes</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="matchbrackets">Match brackets</label>
|
||||
<input id="matchbrackets" name="matchbrackets" type="checkbox">
|
||||
<label for="matchbrackets" class="mini-desc">Highlights matching brackets whenever the cursor is next to them</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="highlight">Highlight</label>
|
||||
<input id="highlight" name="highlight" type="checkbox">
|
||||
|
||||
@ -3,12 +3,22 @@
|
||||
<h1>Default Preferences</h1>
|
||||
|
||||
<section id="content">
|
||||
<p>Configure how JS Bin is set up when you create a new bin<!-- , such as your default
|
||||
HTML content, processors selected, and more -->. <br>
|
||||
As soon as you make any changes, they will be automatically saved.</p>
|
||||
<p>Configure how JS Bin is set up when you create a new bin, and how errors are reported.</p>
|
||||
|
||||
<form>
|
||||
<form id="editor-settings">
|
||||
{{#feature request "pro"}}
|
||||
<h3>Security</h3>
|
||||
|
||||
<div>
|
||||
<label for="ssl">Always use SSL</label>
|
||||
<input type="checkbox" name="ssl" id="ssl" {{#feature request "sslForAll"}}checked{{/feature}}>
|
||||
</div>
|
||||
|
||||
<p><small>Note that by enabling SSL for all of JS Bin, including non-https assets in your bins will cause the iframe <em>not</em> to render. This is the correct behaviour for https frames containing non-secure content. <a href="http://jsbin.com/help/ssl">Read more</a>.</small></p>
|
||||
|
||||
{{/feature}}
|
||||
|
||||
{{#feature request "assets"}}
|
||||
<h3>Custom content</h3>
|
||||
<p>If you want to link to images or additional assets, we redirect the <a href="#">/{{user.name}}/assets/</a> relative URL to <em>your asset URL</em>, so you can link to <a href="#">/{{user.name}}/assets/images/logo.png</a> for example.</p>
|
||||
@ -44,10 +54,65 @@
|
||||
<input type="checkbox" checked id="includejs">
|
||||
</div>
|
||||
|
||||
<h3>Linting</h3>
|
||||
|
||||
<div>
|
||||
<span class="label">Show errors in</span>
|
||||
<label><input type="checkbox" name="hintShow-console" id="hintShow-console"> Footer</label>
|
||||
<label><input type="checkbox" name="hintShow-gutter" id="hintShow-gutter"> Gutter</label>
|
||||
<label><input type="checkbox" name="hintShow-line" id="hintShow-line"> Intraline</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="jshint">JSHint</label>
|
||||
<input type="checkbox" checked id="jshint">
|
||||
<details class="jshintOptWrapper hintOptWrapper">
|
||||
<summary>settings</summary>
|
||||
<div>
|
||||
<textarea name="jshintOptions" id="jshintOptions" class="hintOptions" cols="40" rows="5" placeholder='{ "//": "your custom JSHint settings" }'></textarea>
|
||||
<label for="jshintOptions" class="hintOptions"><span class="mini-desc">JSON format, for more info read the <a href="http://jshint.com/docs/options/" target="_blank">documentation</a></span> <span id="jshintOptError" class="mini-desc-error"></span></label>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="coffeescripthint">CoffeeLint</label>
|
||||
<input type="checkbox" checked id="coffeescripthint">
|
||||
<details class="coffeescripthintOptWrapper hintOptWrapper">
|
||||
<summary>settings</summary>
|
||||
<div>
|
||||
<textarea name="coffeescripthintOptions" id="coffeescripthintOptions" class="hintOptions" cols="40" rows="5" placeholder='{ "//": "your custom CoffeeLint settings" }'></textarea>
|
||||
<label for="coffeescripthintOptions" class="hintOptions"><span class="mini-desc">JSON format, for more info read the <a href="http://www.coffeelint.org/#options" target="_blank">documentation</a></span> <span id="coffeescripthintOptError" class="mini-desc-error"></span></label>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="csshint">CSSLint</label>
|
||||
<input type="checkbox" checked id="csshint">
|
||||
<details class="csshintOptWrapper hintOptWrapper">
|
||||
<summary>settings</summary>
|
||||
<div>
|
||||
<textarea name="csshintOptions" id="csshintOptions" class="hintOptions" cols="40" rows="5" placeholder='{ "//": "your custom CSSLint settings" }'></textarea>
|
||||
<label for="csshintOptions" class="hintOptions"><span class="mini-desc">JSON format, for more info read the <a href="https://github.com/CSSLint/csslint/wiki/Rules" target="_blank">documentation</a></span> <span id="csshintOptError" class="mini-desc-error"></span></label>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="htmlhint">HTMLHint</label>
|
||||
<input type="checkbox" checked id="htmlhint">
|
||||
<details class="htmlhintOptWrapper hintOptWrapper">
|
||||
<summary>settings</summary>
|
||||
<div>
|
||||
<textarea name="htmlhintOptions" id="htmlhintOptions" class="hintOptions" cols="40" rows="5" placeholder='{ "//": "your custom HTMLHint settings" }'></textarea>
|
||||
<label for="htmlhintOptions" class="hintOptions"><span class="mini-desc">JSON format, for more info read the <a href="https://github.com/yaniswang/HTMLHint/wiki/Rules" target="_blank">documentation</a></span> <span id="htmlhintOptError" class="mini-desc-error"></span></label>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<input type="hidden" id="_csrf" name="_csrf" value="{{token}}">
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
<!--
|
||||
Created using {{domain}}
|
||||
Source can be edited via {{permalink}}
|
||||
-->
|
||||
Created using JS Bin
|
||||
http://{{domain}}
|
||||
|
||||
Copyright (c) {{year}} {{#user}}by {{.}} {{/user}}({{permalink}})
|
||||
|
||||
Released under the MIT license: http://jsbin.mit-license.org
|
||||
-->
|
||||
107
views/index.html
107
views/index.html
@ -15,7 +15,17 @@
|
||||
<!--[if lt IE 7]> <body class="source ie ie6"> <![endif]-->
|
||||
<!--[if IE 7]> <body class="source ie ie7"> <![endif]-->
|
||||
<!--[if gt IE 7]> <body class="source ie"> <![endif]-->
|
||||
<!--[if !IE]><!--> <body class="source {{#if private}}private{{else}}public{{/if}}"> <!--<![endif]-->
|
||||
<!--[if !IE]><!--> <body class="source {{#if private}}private{{else}}public{{/if}}"> <!--<![endif]-->
|
||||
{{#unless embed}}
|
||||
<script>
|
||||
if(top != self) {
|
||||
window.location = '{{embedURL}}';
|
||||
document.write('<!--');
|
||||
}
|
||||
</script>
|
||||
{{#feature request "welcomePanel"}}
|
||||
{{> welcome_panel}}
|
||||
{{/feature}}{{/unless}}
|
||||
<div id="control">
|
||||
<div class="control">
|
||||
<div id="menuinfo"><p></p></div>
|
||||
@ -23,7 +33,7 @@
|
||||
{{#if embed}}
|
||||
<span class="menu">
|
||||
<a target="_blank" href="{{code_id_path}}/edit" class="brand button group"><img src="{{static}}/images/jsbin_16.png">
|
||||
<h1>JS Bin</h1></a><a href="{{code_id_path}}/save" target="_blank" class="button save">Save</a>
|
||||
<h1>JS Bin</h1></a><a href="/clone" target="_blank" class="button">Save</a>
|
||||
</span>
|
||||
{{else}}
|
||||
<div class="menu">
|
||||
@ -42,6 +52,11 @@
|
||||
<a class="deletebin button group" data-desc="Delete bin" title="Delete this bin" href="{{root}}/delete" data-shortcut="ctrl+shift+del">Delete</a>
|
||||
<a class="deleteallbins button group" data-desc="Delete all bin snapshots" title="Delete all bin snapshots" href="{{root}}/delete-all">Delete all snapshots</a>
|
||||
{{/feature}}
|
||||
<a class="archivebin button group" data-desc="Archive bin" title="Archive this bin" href="{{code_id_path}}/archive" data-shortcut="ctrl+y">Archive</a>
|
||||
<a class="unarchivebin button group" data-desc="Unrchive bin" title="Restore this bin from the archive" href="{{code_id_path}}/unarchive" data-shortcut="ctrl+shift+y">Unarchive</a>
|
||||
{{#if home}}
|
||||
<a href="#" data-desc="Browse your previous created bins" data-shortcut="ctrl+o" class="button group homebtn" data-label="open">My Bins{{#if bincount}} <span class="meta">({{bincount}})</span>{{/if}}</a>
|
||||
{{/if}}
|
||||
<hr data-desc="">
|
||||
<a id="addmeta" data-desc="Insert a description shown in My Bins" title="Add meta data to bin" class="button group" href="#add-description">Add description</a>
|
||||
<a title="Save snapshot" data-desc="Save current work, and begin new revision on next change" data-shortcut="ctrl+s" class="button save group" data-label="save" href="{{root}}/save">Save snapshot</a>
|
||||
@ -53,12 +68,9 @@
|
||||
<a data-desc="Export individual panels to Github's gist{{#unless user.github_token}} as an anonymous user{{/unless}}" class="export-as-gist button group" title="Create a new {{#unless user.github_token}}anonymous {{/unless}}GitHub Gist from this bin" href="#export-to-gist">Export as gist</a>
|
||||
<a data-desc="Download a complete html file for this bin" id="download" title="Save to local drive" class="button download group" href="{{root}}/download" data-label="download">Download</a>
|
||||
<a data-desc="Use content from this bin when creating new bins" class="startingpoint button group" title="Set as starting code" href="{{root}}/save" data-label="save-as-template">Save as template</a>
|
||||
<!-- <a data-desc="Reset to the original JS Bin starting point" id="cleartemplate" title="Reset to the original JS Bin starting template" class="button group" href="#clear-template" data-label="clear-template">Clear template</a> -->
|
||||
<!-- <a data-desc="How to embed a bin" target="_blank" title="How to embed a bin" data-label="how-to-embed" class="button group" href="http://jsbin.com/help/how-can-i-embed-jsbin">How to embed</a> -->
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- spacer (or not) DO NOT TOUCH ERE BE DRAGONS etc wat?
|
||||
--><div class="menu">
|
||||
</div><div class="menu">
|
||||
<span class="button group">Add library
|
||||
<select id="library"></select>
|
||||
</span>
|
||||
@ -139,7 +151,6 @@
|
||||
<li><label><input type="checkbox" data-panel="live">Output</label></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- <div id="removelock"><input type="button" title="Unlocks the revision that you own so that you can continue to write to this specific bin" class="unlocklockrevision" value="Unlock revision #{{revision}}"> <small>to allow changes.</small></div> -->
|
||||
<div data-desc="The url to this bin with the JS Bin editor">
|
||||
<a class="link heading" data-path="/edit" target="_blank" href="{{root}}{{code_id_path}}/edit">Link</a><br><input data-path="/edit" class="link" value="{{root}}{{code_id_path}}/edit" type="text">
|
||||
</div>
|
||||
@ -156,7 +167,7 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div><!-- intentional space -->
|
||||
</div>
|
||||
<div id="start-saving" class="menu">
|
||||
<a href="{{code_id_path}}/save" class="save button group">Start saving your work</a>
|
||||
</div>
|
||||
@ -322,7 +333,7 @@
|
||||
{{/unless}}
|
||||
|
||||
{{#if settings.[ui showblog]}}
|
||||
<div class="menu">
|
||||
<div class="menu blog">
|
||||
<a href="http://jsbin.com/blog" class="button">Blog</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
@ -387,7 +398,6 @@
|
||||
<div class="dropdownmenu processorSelector" data-type="css">
|
||||
<a href="#css">CSS</a>
|
||||
<a href="#less">LESS</a>
|
||||
<!-- <a href="#stylus">Stylus</a> --><!-- Removed because of a crash bug in stylus https://twitter.com/phuunet/status/377735455670030336 -->
|
||||
<a href="#convert">Convert to CSS</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -401,6 +411,7 @@
|
||||
<span class="name"><strong>Console</strong></span>
|
||||
<span class="options">
|
||||
<button id="runconsole" title="ctrl + enter">Run</button>
|
||||
<button id="clearconsole" title="ctrl + l">Clear</button>
|
||||
</span>
|
||||
</div>
|
||||
<div id="console" class="stretch"><ul id="output"></ul><form>
|
||||
@ -420,10 +431,12 @@ Include alerts, prompts & confirm boxes">Run with JS</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form {{#if embed}}target="_blank"{{/if}} id="saveform" method="post" action="{{code_id_path}}/save">
|
||||
{{#unless embed}}
|
||||
<form id="saveform" method="post" action="{{code_id_path}}/save">
|
||||
<input type="hidden" name="method">
|
||||
<input type="hidden" name="_csrf" value="{{token}}">
|
||||
</form>
|
||||
{{/unless}}
|
||||
</div>
|
||||
<div id="tip" class="{{#if flash_tip_type}}{{flash_tip_type}}{{/if}}{{#unless flash_tip_type}} notification{{/unless}}">
|
||||
<p>
|
||||
@ -471,10 +484,6 @@ Include alerts, prompts & confirm boxes">Run with JS</button>
|
||||
<td>ctrl + /</td>
|
||||
<td>Toggle comment on selected lines</td>
|
||||
</tr>
|
||||
<!-- <tr>
|
||||
<td>ctrl + alt + .</td>
|
||||
<td>Close current HTML element</td>
|
||||
</tr> -->
|
||||
<tr>
|
||||
<td>ctrl + [</td>
|
||||
<td>Indents selected lines</td>
|
||||
@ -485,7 +494,7 @@ Include alerts, prompts & confirm boxes">Run with JS</button>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tab</td>
|
||||
<td>Code complete (JavaScript only) & Emmet expand</td>
|
||||
<td>Code complete & <a href="http://docs.emmet.io/" target="_blank">Emmet</a> expand</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ctrl + s</td>
|
||||
@ -495,22 +504,11 @@ Include alerts, prompts & confirm boxes">Run with JS</button>
|
||||
<td>ctrl + shift + s</td>
|
||||
<td>Open the share options</td>
|
||||
</tr>
|
||||
<!-- too confusing, let's throw it away -->
|
||||
<!--
|
||||
<tr>
|
||||
<td>ctrl + \</td>
|
||||
<td>Hide navigation bar</td>
|
||||
<td>ctrl + y</td>
|
||||
<td>Archive Bin</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>esc, ctrl + [num]</td>
|
||||
<td>JS Bin ignores this sequence, and returns control to browser shortcuts</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ctrl + §<br>(or `)</td>
|
||||
<td>Hide focused panel</td>
|
||||
</tr>-->
|
||||
|
||||
<tr><td colspan="2"><small><br>JS Bin also supports <a href="http://docs.emmet.io/" target="_blank">Emmet/Zen Coding</a> shortcuts</small></td></tr>
|
||||
<tr><td colspan="2"><small><br><a href="/help/keyboard-shortcuts" target="_blank">Complete list of JS Bin shortcuts</a></small></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -588,19 +586,10 @@ Include alerts, prompts & confirm boxes">Run with JS</button>
|
||||
</header>
|
||||
<div class="body">
|
||||
<ul>
|
||||
{{#feature request "private"}}<!-- <li>
|
||||
<select>
|
||||
<option>Public</option>
|
||||
<option>Private to me</option>
|
||||
</option>
|
||||
</select>
|
||||
</li> -->
|
||||
{{/feature}}
|
||||
<li><a href="/clone">Clone</a> </li>
|
||||
<li><a class="startingpoint" href="/save-as-template">Save as template</a> </li>
|
||||
<li><a class="export-as-gist" href="#export-gist">Export gist</a> </li>
|
||||
<li class="owner"><a href="/download">Download</a> </li>
|
||||
<!-- <li class="owner"><a href="/archive">Archive</a> </li> -->
|
||||
{{#feature request "delete"}}
|
||||
<li class="owner"><a class="deletebin" href="/delete">Delete</a></li>
|
||||
{{/feature}}
|
||||
@ -609,27 +598,41 @@ Include alerts, prompts & confirm boxes">Run with JS</button>
|
||||
</div>
|
||||
</div>
|
||||
{{/feature}}
|
||||
<script>
|
||||
var template = {{{json_template}}};
|
||||
var jsbin = {{{jsbin}}}; tips = {{tips}};
|
||||
</script>
|
||||
{{#if isProduction}}
|
||||
<script src="{{static}}/js/vendor/jquery-1.11.0.min.js"></script>
|
||||
<script src="{{static}}/js/prod/jsbin-{{version}}.min.js"></script>
|
||||
<!--[if lte IE 8]>
|
||||
<script src="{{static}}/js/vendor/jshint/jshint.old.min.js"></script>
|
||||
<![endif]-->
|
||||
{{#unless embed}}
|
||||
<script>if(top != self) document.write('<!--');</script>
|
||||
{{/unless}}
|
||||
{{#if live}}
|
||||
<script src="{{static}}/js/vendor/eventsource.js"></script>
|
||||
<script src="{{static}}/js/spike.js{{cacheBust}}"></script>
|
||||
{{else}}
|
||||
{{#if concat}}
|
||||
<script src="{{static}}/js/vendor/jquery-1.11.0.js"></script>
|
||||
<script src="{{static}}/js/prod/jsbin-{{version}}.js"></script>
|
||||
<script src="{{static}}/js/vendor/jquery-1.11.0.min.js"></script>
|
||||
{{#if isProduction}}
|
||||
<script src="{{static}}/js/prod/jsbin-{{version}}.min.js"></script>
|
||||
<script>
|
||||
start({{{json_template}}}, {{{jsbin}}}, this, document);
|
||||
</script>
|
||||
{{else}}
|
||||
<script src="{{static}}/js/vendor/jquery-1.11.0.js"></script>
|
||||
{{#scripts}}<script src="{{../static}}{{.}}"></script>{{/scripts}}
|
||||
<script>
|
||||
window.template = {{{json_template}}};
|
||||
window.jsbin = {{{jsbin}}};
|
||||
</script>
|
||||
{{#if concat}}
|
||||
<script src="{{static}}/js/prod/jsbin-{{version}}.js"></script>
|
||||
{{else}}
|
||||
{{#scripts}}<script src="{{../static}}{{.}}"></script>{{/scripts}}
|
||||
{{/if}}
|
||||
{{#addons}}<script src="{{../static}}{{.}}"></script>{{/addons}}
|
||||
{{/if}}
|
||||
{{#addons}}<script src="{{../static}}{{.}}"></script>{{/addons}}
|
||||
{{/if}}
|
||||
{{#if live}}
|
||||
<script src="{{static}}/js/vendor/eventsource.js"></script>
|
||||
<script src="{{static}}/js/spike.js{{cacheBust}}"></script>
|
||||
{{/if}}
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user