Mass check in of JS Bin 2.8 - lots of UI changes - in complete though - IE to test.

This commit is contained in:
remy 2011-06-16 02:07:29 +01:00
parent a6a63e086f
commit 1ddb2a0360
28 changed files with 3593 additions and 558 deletions

View File

@ -1,2 +1,4 @@
all:
git submodule init
git submodule update
php ./build/build.php

View File

@ -1 +1 @@
2.7.4
2.7.5

38
app.php
View File

@ -76,7 +76,10 @@ if (!$action) {
}
$sql = sprintf('insert into sandbox (javascript, html, created, last_viewed, url, revision) values ("%s", "%s", now(), now(), "%s", "%s")', mysql_real_escape_string($javascript), mysql_real_escape_string($html), mysql_real_escape_string($code_id), mysql_real_escape_string($revision));
mysql_query($sql);
$ok = mysql_query($sql);
// error_log('saved: ' . $code_id . ' - ' . $revision . ' -- ' . $ok . ' ' . strlen($sql));
// error_log(mysql_error());
}
if (stripos($method, 'download') !== false) {
@ -190,17 +193,6 @@ function encode($s) {
return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $s) . '"';
}
// returns the app loaded with json html + js content
function edit() {
}
// saves current state - should I store regardless of content, to start their own
// milestones?
function save() {
}
function getCodeIdParams($request) {
$revision = array_pop($request);
$code_id = array_pop($request);
@ -271,8 +263,16 @@ function getCode($code_id, $revision, $testonly = false) {
function defaultCode($not_found = false) {
$library = '';
if (@$_GET['html']) {
$html = $_GET['html'];
$usingRequest = false;
if (isset($_REQUEST['html']) || isset($_REQUEST['js'])) {
$usingRequest = true;
}
if (@$_REQUEST['html']) {
$html = $_REQUEST['html'];
} else if ($usingRequest) {
$html = '';
} else {
$html = <<<HERE_DOC
<!DOCTYPE html>
@ -297,14 +297,16 @@ HERE_DOC;
$javascript = '';
if (!@$_GET['js']) {
if (@$_REQUEST['js']) {
$javascript = $_REQUEST['js'];
} else if ($usingRequest) {
$javascript = '';
} else {
if ($not_found) {
$javascript = 'document.getElementById("hello").innerHTML = "<strong>This URL does not have any code saved to it.</strong>";';
} else {
$javascript = "if (document.getElementById('hello')) {\n document.getElementById('hello').innerHTML = 'Hello World - this was inserted using JavaScript';\n}\n";
}
} else {
$javascript = $_GET['js'];
}
}
return array(get_magic_quotes_gpc() ? stripslashes($html) : $html, get_magic_quotes_gpc() ? stripslashes($javascript) : $javascript);

View File

@ -17,7 +17,7 @@ body {
font-size: 13px;
min-width: 976px;
overflow: hidden;
background: url(/images/jsbin-bg.gif) repeat-x top left;
background: url(/images/jsbin-bg.gif) repeat-x 0 -10px;
}
p {
@ -25,23 +25,32 @@ p {
}
#control {
height: 61px;
overflow: hidden;
height: 51px;
position: absolute;
/* width: 100%;*/
left: 0;
right: 0;
}
#control, .label {
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;
}
.control, .help, .starting {
width: 100px;
padding: 15px;
/* width: 100px;*/
padding: 13px 10px;
float: left;
white-space: nowrap;
}
.control {
padding-right: 0;
width: 50%;
/* width: 50%;*/
}
.starting {
@ -49,7 +58,7 @@ p {
}
.help {
width: 40%;
width: 10%;
text-align: right;
float: right;
}
@ -59,7 +68,7 @@ p {
}
.starting, .help {
line-height: 32px;
line-height: 25px;
}
ul.flat {
@ -106,8 +115,9 @@ a:hover {
}
#bin {
top: 62px;
top: 52px;
width: 100%;
opacity: 0;
}
div.html {
@ -128,8 +138,13 @@ div.html {
border: 0;
}
*/
div.code {
width: 50%;
div.code, #live, .resize {
/* width: 50%;*/
/* -webkit-transition: left ease-out 100ms, right ease-out 100ms;*/
}
.resize {
background: #ccc url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAMCAYAAACnfgdqAAAAMElEQVQIHWP8//8/AxRoMAA5ZkDcAsT/mYCiN4GYBYidGIECUFUMDCAZOEDmUNkAAKKgK80+TE8oAAAAAElFTkSuQmCC) no-repeat left 45%;
}
div.preview {
@ -149,7 +164,7 @@ div.preview {
.code .label p {
/* display: inline;*/
font-weight: bold;
cursor: pointer;
/* cursor: pointer;*/
}
.code .label label {
@ -173,6 +188,10 @@ div.preview {
width: 140px;
}
.javascript {
right: 50%;
}
iframe.javascript {
border-right: 1px solid #ccc !important;
}
@ -183,14 +202,14 @@ iframe.javascript {
margin-right: 10px;
}
a.button {
.button {
border: 1px solid #ccc;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
height: 12px;
line-height: 12px;
padding: 10px;
padding: 6px 10px;
display: block;
float: left;
text-decoration: none;
@ -199,19 +218,30 @@ a.button {
background: rgba(255, 255, 255, 0.3);
}
a.gap {
#control span {
display: block;
float: left;
height: 10px;
padding-top: 4px;
padding-bottom: 10px;
line-height: 20px;
}
.gap {
margin-right: 10px;
}
.button:hover {
a.button:hover {
-moz-box-shadow: #fff 0px 0px 5px;
-webkit-box-shadow: #fff 0px 0px 5px;
box-shadow: #fff 0px 0px 5px;
background: rgba(0, 0, 0, 0.05);
}
.button:active, .button:focus {
a.button:active, .button:focus {
-moz-box-shadow: #C8C8C8 0px 0px 3px;
-webkit-box-shadow: #C8C8C8 0px 0px 3px;
box-shadow: #C8C8C8 0px 0px 3px;
border-color: #fff;
outline: 0;
text-shadow: none;
@ -225,6 +255,7 @@ body.preview a.preview {
border: 1px solid #ccc;
-moz-box-shadow: #fff 0px 0px 5px;
-webkit-box-shadow: #fff 0px 0px 5px;
box-shadow: #fff 0px 0px 5px;
background-image:
-webkit-gradient(
@ -647,7 +678,6 @@ body.streaming {
color: #fff;
font-weight: bold;
line-height: 20px;
padding-left: 15px;
text-shadow: #0A0 0px 1px 0px;
display: block;
background: #0c0;
@ -655,6 +685,10 @@ body.streaming {
cursor: pointer;
}
#streaming .msg {
padding-left: 15px;
}
#streaming a {
text-shadow: #0A0 0px 1px 0px;
color: #fff;
@ -702,10 +736,10 @@ body {
}
#streaming, #control, #bin {
-webkit-transition: top 100ms ease-out;
-o-transition: top 100ms ease-out;
-moz-transition: top 100ms ease-out;
transition: top 100ms ease-out;
-webkit-transition: top 100ms ease-out, opacity 50ms linear;
-o-transition: top 100ms ease-out, opacity 50ms linear;
-moz-transition: top 100ms ease-out, opacity 50ms linear;
transition: top 100ms ease-out, opacity 50ms linear;
}
body.streaming #control,
@ -783,9 +817,16 @@ ie6, li {
.button.download {
padding-left: 24px;
background-image: url(/images/arrow_down_12x12.png);
/* background-image: url(/images/arrow_down_12x12.png);*/
background-image: url(/images/download.png);
background-repeat: no-repeat;
background-position: 8px 55%;
background-position: 8px -33px;
}
.button.download:hover {
background-image: url(/images/download.png);
background-repeat: no-repeat;
background-position: 8px 7px;
}
/* attempt to get a live render preview in */
@ -848,21 +889,281 @@ ie6, li {
50,
color-stop(0, #BFBFBF),
color-stop(1, #949494)
);
);
}
@media screen and (min-width: 1200px) {
#live {
background: white url(/images/jsbin-bg.gif) repeat-x 0 -62px;
top: 0;
left: 67%;
border-top: 0;
border-left: 1px solid #ccc;
}
.live #source {
bottom: 0;
right: 33%;
}
#live {
background: white url(/images/jsbin-bg.gif) repeat-x 0 -62px;
top: 0;
left: 67%;
border-top: 0;
border-left: 1px solid #ccc;
}
.live #source {
bottom: 0;
right: 33%;
}
.autocomplete {
position: absolute;
overflow: hidden;
border: 2px solid #DFE0B4;
}
.autocomplete select {
margin: 0;
padding: 0;
outline: none !important;
background: #FFFFDB;
border: 0;
font-family: MenschRegular, Menlo, Monaco, consolas, monospace;
font-size: 12px;
}
.showtip #bin {
bottom: 26px;
}
#tip {
display: none;
border-top: 1px solid #ccc;
position: absolute;
bottom: 0;
font-size: 12px;
line-height: 20px;
background: #fdfece;
left: 0;
right: 0;
padding: 2px 10px 2px 20px;
}
#tip p {
margin: 0;
}
#tip a.dismiss {
position: absolute;
right: 20px;
top: 0;
text-decoration: none;
}
.showtip #tip {
display: block;
}
details {
position: absolute;
display: block;
bottom: 0;
right: 0;
left: 0;
font-size: 12px;
background: #FEE0E0;
color: #bb0000;
}
summary {
cursor: pointer;
padding: 3px 5px;
display: block;
font-weight: bold;
background: #FC9B9F;
}
details ol {
padding-left: 21px;
max-height: 150px;
overflow: auto;
}
details li {
margin: 5px 0;
cursor: pointer;
}
/* codemirror2 styles */
@font-face {
font-family: 'MenschRegular';
src: url('/font/mensch-webfont.eot');
src: url('/font/mensch-webfont.eot?#iefix') format('eot'),
url('/font/mensch-webfont.woff') format('woff'),
url('/font/mensch-webfont.ttf') format('truetype'),
url('/font/mensch-webfont.svg#webfont0UwCC656') format('svg');
font-weight: normal;
font-style: normal;
}
/*
.editbox {
margin: .4em;
padding: 0;
font-family: MenschRegular, Menlo, Monaco, consolas, monospace;
font-size: 14pt;
color: black;
}*/
.CodeMirror > div {
margin: .6em;
}
.javascript .CodeMirror > div {
margin-top: 25px;
}
.html .CodeMirror > div {
margin-top: 60px;
}
.CodeMirror pre {
/* white-space: pre-wrap !important;*/
}
.editor .CodeMirror {
height: 100%;
top: 0;
bottom: 0;
}
.CodeMirror {
overflow: auto;
height: 300px;
/* top: 0;*/
/* bottom: 0;*/
/* line-height: 1em;*/
font-size: 12px;
font-family: MenschRegular, Menlo, Monaco, consolas, monospace;
_position: relative; /* IE6 hack */
}
.CodeMirror-gutter {
position: absolute; left: 0; top: 0;
background-color: #f7f7f7;
border-right: 1px solid #eee;
min-width: 2em;
height: 100%;
}
.CodeMirror-gutter-text {
color: #aaa;
text-align: right;
padding: .4em .2em .4em .4em;
}
.CodeMirror-lines {
padding: .4em;
}
.CodeMirror pre {
-moz-border-radius: 0;
-webkit-border-radius: 0;
-o-border-radius: 0;
border-radius: 0;
border-width: 0; margin: 0; padding: 0; background: transparent;
font-family: inherit;
}
.CodeMirror-cursor {
z-index: 10;
position: absolute;
visibility: hidden;
border-left: 1px solid black !important;
}
.CodeMirror-focused .CodeMirror-cursor {
visibility: visible;
}
span.CodeMirror-selected {
background: #ccc !important;
color: HighlightText !important;
}
.CodeMirror-focused span.CodeMirror-selected {
background: Highlight !important;
}
.CodeMirror-matchingbracket {color: #0f0 !important;}
.CodeMirror-nonmatchingbracket {color: #f22 !important;}
/* CM2 default */
.cm-s-default span.cm-keyword {color: #708;}
.cm-s-default span.cm-atom {color: #219;}
.cm-s-default span.cm-number {color: #164;}
.cm-s-default span.cm-def {color: #00f;}
.cm-s-default span.cm-variable {color: black;}
.cm-s-default span.cm-variable-2 {color: #05a;}
.cm-s-default span.cm-variable-3 {color: #0a5;}
.cm-s-default span.cm-property {color: black;}
.cm-s-default span.cm-operator {color: black;}
.cm-s-default span.cm-comment {color: #a50;}
.cm-s-default span.cm-string {color: #a11;}
.cm-s-default span.cm-meta {color: #555;}
.cm-s-default span.cm-error {color: #f00;}
.cm-s-default span.cm-qualifier {color: #555;}
.cm-s-default span.cm-builtin {color: #30a;}
.cm-s-default span.cm-bracket {color: #cc7;}
.cm-s-default span.cm-tag {color: #170;}
.cm-s-default span.cm-attribute {color: #00c;}
/* jsbin - based on web inspector */
.cm-s-jsbin span.cm-keyword {color: #AA0D91;}
.cm-s-jsbin span.cm-atom {color: #219;}
.cm-s-jsbin span.cm-number {color: #164;}
.cm-s-jsbin span.cm-def {color: #00f;}
.cm-s-jsbin span.cm-variable {color: black;}
.cm-s-jsbin span.cm-variable-2 {color: #05a;}
.cm-s-jsbin span.cm-variable-3 {color: #0a5;}
.cm-s-jsbin span.cm-property {color: black;}
.cm-s-jsbin span.cm-operator {color: black;}
.cm-s-jsbin span.cm-comment {color: #236E25;}
.cm-s-jsbin span.cm-string {color: #C41A16;}
.cm-s-jsbin span.cm-meta {color: #555;}
.cm-s-jsbin span.cm-error {color: #f00;}
.cm-s-jsbin span.cm-qualifier {color: #555;}
.cm-s-jsbin span.cm-builtin {color: #30a;}
.cm-s-jsbin span.cm-bracket {color: #cc7;}
.cm-s-jsbin span.cm-tag {color: #881280;}
.cm-s-jsbin span.cm-attribute {color: #994500;}
/* neat */
.cm-s-neat span.cm-comment { color: #a86; }
.cm-s-neat span.cm-keyword { font-weight: bold; color: blue; }
.cm-s-neat span.cm-string { color: #a22; }
.cm-s-neat span.cm-builtin { font-weight: bold; color: #077; }
.cm-s-neat span.cm-special { font-weight: bold; color: #0aa; }
.cm-s-neat span.cm-variable { color: black; }
.cm-s-neat span.cm-number, .cm-s-neat span.cm-atom { color: #3a3; }
.cm-s-neat span.cm-meta {color: #555;}
/* elegant */
.cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom {color: #762;}
.cm-s-elegant span.cm-comment {color: #262;font-style: italic;}
.cm-s-elegant span.cm-meta {color: #555;font-style: italic;}
.cm-s-elegant span.cm-variable {color: black;}
.cm-s-elegant span.cm-variable-2 {color: #b11;}
.cm-s-elegant span.cm-qualifier {color: #555;}
.cm-s-elegant span.cm-keyword {color: #730;}
.cm-s-elegant span.cm-builtin {color: #30a;}
.cm-s-elegant span.cm-error {background-color: #fdd;}
/* Loosely based on the Midnight Textmate theme */
.cm-s-night { background: #0a001f; color: #f8f8f8; }
.cm-s-night span.CodeMirror-selected { background: #a8f !important; }
.cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; }
.cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; }
.cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; }
.cm-s-night span.cm-comment { color: #6900a1; }
.cm-s-night span.cm-atom { color: #845dc4; }
.cm-s-night span.cm-number { color: #ffd500; }
.cm-s-night span.cm-keyword { color: #599eff; }
.cm-s-night span.cm-string { color: #37f14a; }
.cm-s-night span.cm-meta { color: #7678e2; }
.cm-s-night span.cm-variable-2 { color: #99b2ff; }
.cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { white; }
.cm-s-night span.cm-error { color: #9d1e15; }
.cm-s-night span.cm-bracket { color: #8da6ce; }
.cm-s-night span.cm-comment { color: #6900a1; }
.cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; }
#bin.ready {
opacity: 1;
}

BIN
images/download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

View File

@ -22,38 +22,40 @@ if ($code_id) {
<div class="control">
<div class="buttons">
<a class="tab button source group left" accesskey="1" href="#source">Code</a>
<a class="tab button preview group right gap" accesskey="2" href="#preview">Preview</a>
<a title="Revert" class="button light group left enable" id="revert" href="#"><img class="enabled" src="/images/revert.png" /><img class="disabled" src="/images/revert-disabled.png" /></a>
<a class="tab button preview group right gap" accesskey="2" href="#preview" title="Run with alerts, prompts, etc">Render</a>
<a title="Revert" class="button light group left" id="revert" href="#"><img class="enabled" src="/images/revert.png" /><img class="disabled" src="/images/revert-disabled.png" /></a>
<?php if ($code_id) : ?>
<a id="jsbinurl" class="button group light left" href="<?=HOST . $code_id?>"><?=HOST . $code_id?></a>
<?php else : ?>
<a id="save" class="button save group left right" href="/save">Save</a>
<a id="save" class="button save group left" href="/save">Save</a>
<?php endif ?>
<?php if ($code_id) : ?><a id="save" class="button gap light save group right" href="<?=$code_id_path?>/save">Save changes</a><?php endif ?>
<a id="stream" class="button left right" href="#stream">Stream</a>
<?php if ($code_id) : ?><a id="save" class="button light save group" href="<?=$code_id_path?>/save">Save</a><?php endif ?>
<a id="download" class="button download group right light gap" href="">Download</a>
<span id="panelsvisible" class="gap">View:
<input type="checkbox" data-panel="javascript" id="showjavascript"><label for="showjavascript">JavaScript</label>
<input type="checkbox" data-panel="html" id="showhtml"><label for="showhtml">HTML</label>
<input type="checkbox" data-panel="live" id="showlive"><label for="showlive">Real-time preview</label>
</span>
</div>
</div>
<!-- <div class="starting">
</div> -->
<div class="help">
<ul class="flat">
<li><a id="startingpoint" href="#"><span>Save as my template</span></a></li>
<!-- <li><a class="video" href="/about">About</a></li>
<li><a class="video" href="#">Ajax Debugging</a></li> -->
<li><a href="/help">Help &amp; tutorials</a></li>
<li><a target="_blank" href="http://jsbin.tumblr.com">Help &amp; tutorials</a></li>
</ul>
</div>
</div>
<div id="bin" class="stretch">
<div id="bin" class="stretch" style="opacity: 0">
<div id="source" class="binview stretch">
<div class="code stretch javascript">
<div class="label"><p><strong id="jslabel">JavaScript</strong><span> (<span class="hide">hide</span><span class="show">show</span> HTML)</span></p></div>
<textarea id="javascript"></textarea>
<div class="label"><p><strong id="jslabel">JavaScript</strong><!-- <span> (<span class="hide">hide</span><span class="show">show</span> HTML)</span> --></p></div>
<div class="editbox">
<textarea id="javascript"></textarea>
</div>
</div>
<div class="code stretch html">
<div class="label">
<p>HTML<span> (<span class="hide">hide</span><span class="show">show</span> JavaScript)</span></p>
<p>HTML<!-- <span> (<span class="hide">hide</span><span class="show">show</span> JavaScript)</span> --></p>
<label for="library">Include</label>
<select id="library">
<option value="none">None</option>
@ -67,7 +69,9 @@ if ($code_id) {
<option value="ext">Ext js</option>
</select>
</div>
<textarea id="html"></textarea>
<div class="editbox">
<textarea id="html"></textarea>
</div>
</div>
</div>
<div id="live" class="stretch livepreview"><span class="close"></span></div>
@ -76,8 +80,11 @@ if ($code_id) {
<input type="hidden" name="method" />
</form>
</div>
<div id="help"><p><a href="/help/index.html">Help Menu</a></p><div id="content"></div></div>
<?php
<div id="tip"><p>You can jump to the latest bin by adding <code>/latest</code> to your URL</p><a class="dismiss" href="#">Dismiss x</a></div>
<!-- <div id="help"><p><a href="/help/index.html">Help Menu</a></p><div id="content"></div></div> -->
<script>
<?php
/*
// construct the correct query string, if we're injecting the html or JS
$qs = '';
if (isset($_GET['js']) || isset($_GET['html']) || (@$_POST['inject'] && isset($_POST['html'])) ) {
@ -95,18 +102,36 @@ if (@$_GET['js']) {
if (@$_GET['html']) {
$qs .= 'html=' . rawurlencode(stripslashes($_GET['html']));
}
*/
if (@$_POST['inject'] && @$_POST['html']) :
$jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'));
$html = '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $_POST['html']) . '"';
?>
<script>var template = { html : <?=$html?>, javascript: '' };</script>
<?php else : ?>
<script src="<?=$code_id_path ?>/source/<?=$qs?>"></script>
<?php endif ?>
<script src="http://forbind.net/js/?apikey=2796bc83070164231a3ab8c90227dbca"></script>
var template = { html : <?=$html?>, javascript: '' };
<?php else :
/* <script src="<?=$code_id_path ?>/source/<?=$qs?>"></script> */
list($code_id, $revision) = getCodeIdParams($request);
$edit_mode = false;
if ($code_id) {
list($latest_revision, $html, $javascript) = getCode($code_id, $revision);
} else {
list($html, $javascript) = defaultCode();
}
$url = HOST . $code_id . ($revision == 1 ? '' : '/' . $revision);
if (!$ajax) {
echo 'var template = ';
}
// doubles as JSON
echo '{"url":"' . $url . '","html" : ' . encode($html) . ',"javascript":' . encode($javascript) . '}';
endif ?>
</script>
<script>jsbin = { version: "<?=VERSION?>" };</script>
<script src="/js/<?=VERSION?>/jsbin.js"></script>
<script>jsbin.version = "<?=VERSION?>";</script>
<?php if (!OFFLINE) : ?>
<script>
var _gaq = _gaq || [];

View File

@ -1,10 +1,13 @@
//= require "storage"
//= require "events"
//= require "navigation"
//= require "save"
//= require "file-drop"
//= require "errors"
//= require "download"
//= require "../render/live"
//= require "tips"
this.livePreview = function () {
$('#live').trigger('toggle');
};
var debug = false,
var debug = jsbin.settings.debug === undefined ? false : jsbin.settings.debug,
documentTitle = null, // null = JS Bin
$bin = $('#bin'),
loadGist,
$document = $(document),
@ -13,17 +16,24 @@ var debug = false,
sessionStorage.setItem('html', editors.html.getCode());
sessionStorage.setItem('url', template.url);
localStorage.setItem('settings', JSON.stringify(jsbin.settings));
var panel = getFocusedPanel();
sessionStorage.setItem('panel', panel);
try { // this causes errors in IE9 - so we'll use a try/catch to get through it
sessionStorage.setItem('line', editors[panel].currentLine());
sessionStorage.setItem('character', editors[panel].cursorPosition().character);
sessionStorage.setItem('line', editors[panel].getCursor().line);
sessionStorage.setItem('character', editors[panel].getCursor().ch);
} catch (e) {
sessionStorage.setItem('line', 0);
sessionStorage.setItem('character', 0);
}
};
//= require "storage"
//= require "events"
//= require "navigation"
//= require "save"
//= require "file-drop"
$(window).unload(unload);
@ -49,9 +59,26 @@ if (window.location.hash == '#preview') {
}
$document.one('jsbinReady', function () {
if (localStorage && localStorage.getItem('livepreview') == 'true') { // damn string coersion
$('#live').trigger('show');
// if (localStorage && localStorage.getItem('livepreview') == 'true') { // damn string coersion
// $('#live').trigger('show');
// }
$('.code.html').splitter();
$live.splitter();
for (panel in jsbin.settings.show) {
if (jsbin.settings.show[panel]) {
$('#show' + panel).attr('checked', 'checked')[0].checked = true;
} else {
$('#show' + panel).removeAttr('checked')[0].checked = false;
}
}
for (panel in jsbin.settings.show) {
updatePanel(panel, jsbin.settings.show[panel]);
}
$bin.removeAttr('style').addClass('ready');
});
// if a gist has been requested, lazy load the gist library and plug it in
@ -70,39 +97,10 @@ if (/gist\/\d+/.test(window.location.pathname) && (!sessionStorage.getItem('java
}
}
$('div.label p').click(function () {
// determine which side was clicked
var panel = $(this).closest('.code').is('.javascript') ? 'javascript' : 'html',
otherpanel = panel == 'javascript' ? 'html' : 'javascript',
mustshow = $bin.is('.' + panel + '-only'),
speed = 150,
animatePanel = animateOtherPanel = {};
if ($bin.is('.' + panel + '-only')) { // showing the panel
// only the html tab could have been clicked
animatePanel = panel == 'html' ? { left: '50%', width: '50%' } : { left: '0%', width: '50%' };
animateOtherPanel = otherpanel == 'javascript' ? { left: '0%' } : { left: '50%' };
$bin.find('div.' + panel).animate(animatePanel, speed);
$bin.find('div.' + otherpanel).show().animate(animateOtherPanel, speed, function () {
$bin.removeClass(panel + '-only');
localStorage && localStorage.removeItem('visible-panel');
});
} else { // hiding other panel
animatePanel = panel == 'html' ? { left: '0%', width: '100%' } : { width: '100%' };
animateOtherPanel = otherpanel == 'javascript' ? { left: '-50%' } : { left: '100%' };
$bin.find('div.' + panel).animate(animatePanel, speed);
$bin.find('div.' + otherpanel).animate(animateOtherPanel, speed, function () {
$(this).hide();
$bin.addClass(panel + '-only');
// makes me sad, but we have to put this in a try/catch because Safari
// sometimes throws an error when using localStorage, then jQuery goes
// in to an infinite loop if an animation callback throws an exeception!
try {
// we're not reading 'true', only that it's been set
localStorage && localStorage.setItem('visible-panel', panel);
} catch (e) {}
});
$document.keydown(function (event) {
if (event.metaKey && event.which == 83) {
$('#save').click();
event.preventDefault();
}
});

View File

@ -1,17 +1,10 @@
window.jsbin = {};
// once these features are live, they come out of the jsbin beta box
(function () {
var $body = $('body');
//= require "download"
this.on = function () {
localStorage.setItem('beta', 'true');
$body.addClass('beta');
enableDownload();
enableLive();
};
this.off = function () {
@ -22,95 +15,5 @@ window.jsbin = {};
this.active = localStorage.getItem('beta') == 'true' || false;
if (this.active) this.on();
//= require "stream"
// expose...for now
window.stream = this.stream;
//= require "../render/live"
this.livePreview = function () {
$('#live').trigger('toggle');
};
//= require "../vendor/jshint/jshint"
//= require "../vendor/jquery.tipsy"
this.jshint = function () {
var source = editors.javascript.getCode();
var ok = JSHINT(source);
return ok ? true : JSHINT.data();
};
var $error = $('<em>errors</em>').hide();
$('#jslabel').append($error);
// modify JSHINT to only return errors that are of value (to me, for displaying)
JSHINT._data = JSHINT.data;
JSHINT.data = function (onlyErrors) {
var data = JSHINT._data(),
errors = [];
if (onlyErrors && data.errors) {
for (var i = 0; i < data.errors.length; i++) {
if (data.errors[i] !== null && data.errors[i].evidence) { // ignore JSHINT just quitting
errors.push(data.errors[i]);
}
}
return {
errors: errors
};
} else {
return data;
}
};
$error.tipsy({
title: function () {
var html = ['<ul>'],
errors = JSHINT.data(true).errors;
for (var i = 0; i < errors.length; i++) {
html.push('Line ' + errors[i].line + ': ' + errors[i].evidence + ' --- ' + errors[i].reason);
}
return html.join('<li>') + '</ul>';
},
gravity: 'nw',
html: true
});
$error.click(function () {
var errors = JSHINT.data(true).errors;
if (errors.length) {
var line = editors.javascript.nthLine(errors[0].line);
editors.javascript.jumpToLine(line);
editors.javascript.selectLines(line, 0, editors.javascript.nthLine(errors[0].line + 1), 0);
return false;
}
});
var checkForErrors = function () {
var jshint = jsbin.jshint(),
errors = '';
if (jshint === true) {
$error.text('').hide();
} else {
errors = JSHINT.data(true).errors.length;
errors = errors == 1 ? '1 error' : errors + ' errors';
$error.text('(' + errors + ')').show();
}
};
$(document).bind('codeChange', throttle(checkForErrors, 1000));
$(document).bind('jsbinReady', checkForErrors);
}).call(jsbin);
function throttle(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
//= require "stream"
}).call(jsbin);

View File

@ -1,6 +1,5 @@
function enableDownload() {
$('#save').removeClass('right gap').after('<a id="download" class="button download group right light gap" href="">Download</a>');
(function () {
var $revert = $('#revert');
$('#download').click(function (event) {
event.preventDefault();
@ -15,4 +14,6 @@ function enableDownload() {
}); // triggers via ajax
}
});
}
})();

107
js/chrome/errors.js Normal file
View File

@ -0,0 +1,107 @@
//= require "../vendor/jshint/jshint"
//= require "../vendor/jquery.tipsy"
var jshint = function () {
var source = editors.javascript.getCode();
var ok = JSHINT(source);
return ok ? true : JSHINT.data();
};
var detailsSupport = 'open' in document.createElement('details');
var $error = $('<details><summary>errors</summary></details>').hide();
$('#source .javascript').append($error);
$error.find('summary').click(function () {
if (!detailsSupport) {
$(this).nextAll().toggle();
$error[0].open = !$error[0].open;
}
// trigger a resize after the click has completed and the details is close
setTimeout(function () {
$document.trigger('sizeeditors');
}, 10);
});
if (!detailsSupport) {
$error[0].open = false;
}
// modify JSHINT to only return errors that are of value (to me, for displaying)
JSHINT._data = JSHINT.data;
JSHINT.data = function (onlyErrors) {
var data = JSHINT._data(),
errors = [];
if (onlyErrors && data.errors) {
for (var i = 0; i < data.errors.length; i++) {
if (data.errors[i] !== null && data.errors[i].evidence) { // ignore JSHINT just quitting
errors.push(data.errors[i]);
}
}
return {
errors: errors
};
} else {
data.errors = [];
return data;
}
};
// $error.tipsy({
// title: function () {
// var html = ['<ul>'],
// errors = JSHINT.data(true).errors;
// for (var i = 0; i < errors.length; i++) {
// html.push('Line ' + errors[i].line + ': ' + errors[i].evidence + ' --- ' + errors[i].reason);
// }
//
// return html.join('<li>') + '</ul>';
// },
// gravity: 'nw',
// html: true
// });
$error.delegate('li', 'click', function () {
var errors = JSHINT.data(true).errors;
if (errors.length) {
var i = $error.find('li').index(this);
editors.javascript.setSelection({ line: errors[i].line - 1, ch: 0 }, { line: errors[i].line - 1 });
editors.javascript.focus();
// var line = editors.javascript.nthLine(errors[0].line);
// editors.javascript.jumpToLine(line);
// editors.javascript.selectLines(line, 0, editors.javascript.nthLine(errors[0].line + 1), 0);
return false;
}
});
var checkForErrors = function () {
var hint = jshint(),
jshintErrors = JSHINT.data(true),
errors = '',
visible = $error.is(':visible');
if (hint === true && visible) {
$error.hide();
$document.trigger('sizeeditors');
} else if (jshintErrors.errors.length) {
var html = ['<ol>'],
errors = jshintErrors.errors;
for (var i = 0; i < errors.length; i++) {
html.push('Line ' + errors[i].line + ': ' + errors[i].evidence + ' --- ' + errors[i].reason);
}
html = html.join('<li>') + '</ol>';
$error.find('summary').text(jshintErrors.errors.length == 1 ? '1 error' : jshintErrors.errors.length + ' errors');
$error.find('ol').remove();
if (!detailsSupport && $error[0].open == false) html = $(html).hide();
$error.append(html).show();
$document.trigger('sizeeditors');
}
};
$(document).bind('codeChange', throttle(checkForErrors, 1000));
$(document).bind('jsbinReady', checkForErrors);

View File

@ -1,22 +1,76 @@
$('#startingpoint').click(function () {
if (localStorage) {
localStorage.setItem('saved-javascript', editors.javascript.getCode());
localStorage.setItem('saved-html', editors.html.getCode());
// $('#startingpoint').click(function () {
// if (localStorage) {
// localStorage.setItem('saved-javascript', editors.javascript.getCode());
// localStorage.setItem('saved-html', editors.html.getCode());
//
// // fade text out - then show "saved", then bring it back in again
// $(this).find('span:first').fadeOut(200, function () {
// $(this).next().fadeIn(200).animate({ foo: 1 }, 1000, function () {
// $(this).fadeOut(200, function () {
// $(this).prev().fadeIn(150);
// });
// });
// });
// }
// return false;
// }).find('span').after('<span style="display: none;">Saved</span>');
var $htmlpanel = $('.code.html'),
htmlsplitter = null;
// fade text out - then show "saved", then bring it back in again
$(this).find('span:first').fadeOut(200, function () {
$(this).next().fadeIn(200).animate({ foo: 1 }, 1000, function () {
$(this).fadeOut(200, function () {
$(this).prev().fadeIn(150);
});
});
});
function updatePanel(panel, show) {
jsbin.settings.show[panel] = show;
htmlsplitter = htmlsplitter || $htmlpanel.data().splitter;
if (panel == 'live') {
$('#live').trigger(show ? 'show' : 'hide');
htmlsplitter.trigger('init'); // update the position of the html splitter
} else {
var $panel = $bin.find('.code.' + panel)[show ? 'show' : 'hide']();
if (!show) {
htmlsplitter.hide();
} else {
htmlsplitter.show();
}
var $otherpanel = panel == 'html' ? $bin.find('.code.javascript') : $bin.find('.code.html'),
visible = $panelsvisible.filter(':not([data-panel="live"]):checked').length,
$othercheckbox = $panelsvisible.filter('[data-panel=' + (panel == 'html' ? 'javascript' : 'html') + ']');
// logic was only revealed by going through every possible combination. Hey, it was late :(
if (visible === 1 && show == false) {
// stretch
$othercheckbox.attr('disabled', 'disabled');
if (panel == 'html') { // only JavaScript remains
$otherpanel.data('style', { 'right': $otherpanel.css('right') });
$otherpanel.css('right', '0');
} else if (panel == 'javascript') { // only HTML remains
$otherpanel.data('style', {'left' : $otherpanel.css('left') });
$otherpanel.css('left', '0');
}
} else {
$othercheckbox.removeAttr('disabled');
// restore CSS positions
$otherpanel.attr('style', $otherpanel.data('style'));
}
if (show) {
editors[panel].refresh();
}
htmlsplitter.trigger('init'); // on show or hide - recalc the splitter position
}
return false;
}).find('span').after('<span style="display: none;">Saved</span>');
}
var $panelsvisible = $('#panelsvisible input').click(function () {
var checked = this.checked,
panel = $(this).data('panel');
updatePanel(panel, checked);
});
var $revert = $('#revert').click(function () {
if ($revert.is(':not(.enable)')) {
return false;
}
@ -39,51 +93,49 @@ var $revert = $('#revert').click(function () {
return false;
});
var $stream = $('#stream').click(function () {
stream.create();
return false;
});
$('#control .tab').click(function (event) {
event.preventDefault();
// event.preventDefault();
$('body').removeClass('source preview').addClass(this.hash.substr(1));
if ($(this).is('.preview')) {
$('#preview iframe').remove();
$('#preview').append('<iframe class="stretch"></iframe>');
renderPreview();
} else {
// remove iframe and thus removing any (I *think*) memory resident JS
$('#preview iframe').remove();
editors[getFocusedPanel()].focus();
}
});
$('#control div.help a:last').click(function () {
$(window).trigger('togglehelp');
return false;
});
// $('#control div.help a:last').click(function () {
// $(window).trigger('togglehelp');
// return false;
// });
$('#help a:host(' + window.location.host + ')').live('click', function () {
$('#help #content').load(this.href + '?' + Math.random());
return false;
});
// $('#help a:host(' + window.location.host + ')').live('click', function () {
// $('#help #content').load(this.href + '?' + Math.random());
// return false;
// });
var helpOpen = false;
$(window).bind('togglehelp', function () {
var s = 100, right = helpOpen ? 0 : 300;
if (helpOpen == false) {
$('#help #content').load('/help/index.html?' + Math.random());
}
$bin.find('> div').animate({ right: right }, { duration: s });
$('#control').animate({ right: right }, { duration: s });
$('#help').animate({ right: helpOpen ? -300 : 0 }, { duration: s});
helpOpen = helpOpen ? false : true;
});
$(document).keyup(function (event) {
if (helpOpen && event.keyCode == 27) {
$(window).trigger('togglehelp');
}
});
// var helpOpen = false;
// $(window).bind('togglehelp', function () {
// var s = 100, right = helpOpen ? 0 : 300;
//
// if (helpOpen == false) {
// $('#help #content').load('/help/index.html?' + Math.random());
// }
// $bin.find('> div').animate({ right: right }, { duration: s });
// $('#control').animate({ right: right }, { duration: s });
//
// $('#help').animate({ right: helpOpen ? -300 : 0 }, { duration: s});
//
// helpOpen = helpOpen ? false : true;
// });
//
// $(document).keyup(function (event) {
// if (helpOpen && event.keyCode == 27) {
// $(window).trigger('togglehelp');
// }
// });

View File

@ -28,7 +28,7 @@ function saveCode(method, ajax, ajaxCallback) {
if (window.history && window.history.pushState) {
window.history.pushState(null, data.edit, data.edit);
$('#jsbinurl').attr('href', data.edit).text(data.url);
$('#jsbinurl').attr('href', data.url).text(data.url);
} else {
window.location = data.edit;
}

86
js/chrome/splitter.js Normal file
View File

@ -0,0 +1,86 @@
$.fn.splitter = function () {
var $document = $(document);
var splitterSettings = JSON.parse(localStorage.getItem('splitterSettings') || '[]');
return this.each(function () {
var $el = $(this),
guid = $.fn.splitter.guid++,
$parent = $el.parent(),
$prev = $el.prev(),
$handle = $('<div class="resize"></div>'),
$blocker = $('<div class="block" />').css({ cursor: 'pointer', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, 'z-index': 99999, width: '100%', height: '100%' }),
dragging = false,
width = $parent.width(),
left = $parent.offset().left,
settings = splitterSettings[guid] || {};
function moveSplitter(posX) {
var x = posX - left,
split = 100 / width * x;
if (split > 10 && split < 90) {
$el.css('left', split + '%');
$prev.css('right', (100 - split) + '%');
$handle.css({
left: split + '%'
});
settings.x = posX;
splitterSettings[guid] = settings;
console.log('set: ', JSON.stringify(splitterSettings));
localStorage.setItem('splitterSettings', JSON.stringify(splitterSettings));
}
}
$document.mouseup(function () {
dragging = false;
$blocker.remove();
$handle.css('opacity', '0');
}).mousemove(function (event) {
if (dragging) {
moveSplitter(event.pageX);
}
});
$handle.mousedown(function (e) {
dragging = true;
$('body').append($blocker);
// TODO layer on div to block iframes from stealing focus
width = $parent.width();
left = $parent.offset().left;
e.preventDefault();
}).hover(function () {
$handle.css('opacity', '1');
}, function () {
if (!dragging) {
$handle.css('opacity', '0');
}
});
$handle.bind('init', function (event, x) {
$handle.css({
top: 0,
// left: (100 / width * $el.offset().left) + '%',
bottom: 0,
width: 4,
opacity: 0,
position: 'absolute',
cursor: 'pointer',
'border-left': '1px solid rgba(218, 218, 218, 0.5)',
'z-index': 99999
});
if ($el.is(':hidden')) {
$handle.hide();
} else {
moveSplitter(x || $el.offset().left);
}
}).trigger('init', settings.x || $el.offset().left);
$prev.css('width', 'auto');
$el.data('splitter', $handle);
$el.before($handle);
});
};
$.fn.splitter.guid = 0;

View File

@ -17,7 +17,7 @@ var requiresCookies = (function () {
// Firefox with Cookies disabled triggers a security error when we probe window.sessionStorage
// currently we're just disabling all the session features if that's the case.
var sessionStorage, localStorage;
var sessionStorage = window.sessionStorage, localStorage = window.localStorage;
if (!requiresCookies && window.sessionStorage) {
sessionStorage = window.sessionStorage;

View File

@ -1,191 +1,245 @@
(function (global) {
var $stream = $('<div id="streaming"><span class="msg"></span><span class="n"></span><span class="listen"> (click here to <span class="resume">resume</span><span class="pause">pause</span>)</span></div>').prependTo('body'),
streaming = false,
$body = $('body'),
key = null,
captureTimer = null,
last = {},
owner = false;
//= require "../vendor/diff_match_patch_uncompressed"
function capture() {
var javascript = editors.javascript.getCode(),
html = editors.html.getCode(),
changed = false,
msg = {};
if (javascript != last.javascript) {
msg.javascript = javascript;
changed = true;
var context = this;
var script = document.createElement('script');
script.src = 'http://forbind.net/js/';
document.body.appendChild(script);
setTimeout(function forbindReady() {
if (typeof window.forbind !== 'undefined') {
forbind.apikey = '2796bc83070164231a3ab8c90227dbca';
console.log('forbind ready');
initForbind(context);
} else {
setTimeout(forbindReady, 20);
}
if (html != last.html) {
msg.html = html;
changed = true;
}
if (changed) {
last = {
javascript: javascript,
html: html
};
msg.panel = getFocusedPanel();
msg.line = editors[msg.panel].currentLine();
msg.character = editors[msg.panel].cursorPosition().character;
forbind.send(msg);
}
}
}, 20);
forbind.on({
join: function (event) {
$body.addClass('streaming').removeClass('pausestream');
streaming = true;
if (event.isme) {
$('#stream').fadeOut('fast').prev().addClass('right');
}
if (event.isme && event.readonlykey) {
owner = true;
sessionStorage.setItem('streamwritekey', event.readonlykey);
sessionStorage.setItem('streamkey', key);
function initForbind(global) {
var $stream = $('<div id="streaming"><span class="msg"></span><span class="n"></span><span class="listen"> (click here to <span class="resume">resume</span><span class="pause">pause</span>)</span></div>').prependTo('body'),
streaming = false,
$body = $('body'),
key = null,
captureTimer = null,
last = {},
owner = false;
function changes(lang, code) {
var msg = {},
diff,
patch,
result;
var type, editorTimer = { javascript: null, html: null };
// this code is completely over the top - need to simplify
$stream.find('.msg').html('streaming on <a href="/?stream=' + key + '">http://jsbin.com/?stream=' + key + '</a> to #');
$stream.removeClass('listen');
for (type in editors) {
(function (type) {
try {
$(editors[type].win.document).bind('keyup', function () {
if (streaming) {
clearTimeout(editorTimer[type]);
editorTimer[type] = setTimeout(capture, 250);
}
});
} catch (e) {}
})(type);
}
$(document).bind('codeChange', capture);
}
updateCount(event);
},
leave: function (event) {
if (event.isme) {
if (!owner) {
$body.addClass('pausestream');
streaming = false;
$stream.one('click', function () {
window.location.search.replace(/stream=(.+?)\b/, function (n, key) {
global.stream.join(key);
});
});
} else {
$body.removeClass('streaming');
owner = false;
sessionStorage.removeItem('streamkey');
sessionStorage.removeItem('streamwritekey');
}
}
updateCount(event);
},
message: function (msg) {
var code = msg.data;
if (code.javascript) {
editors.javascript.setCode(code.javascript);
}
if (code.html) {
editors.html.setCode(code.html);
$(document).trigger('codeChange');
}
// update preview if required
if ($body.is('.preview')) {
$('#preview').append('<iframe class="stretch"></iframe>');
renderPreview();
if (last[lang] === undefined) {
msg.text = code;
msg.diff = false;
} else {
var focused = editors[code.panel];
focused.focus();
focused.selectLines(focused.nthLine(code.line), code.character);
diff = new diff_match_patch();
// 1. get diffs
patch = diff.patch_make(last[lang], code);
// 2. apply patch to old javascript
result = diff.patch_apply(patch, last[lang]);
// 3. if it matches, then send diff
if (result[0] == code) {
msg.text = diff.patch_toText(patch);
msg.diff = true;
// 4. otherwise, send entire code
} else {
msg.text = code;
msg.diff = false;
}
}
},
error: function (data) {
console.log('error in forbind', data);
last[lang] = code;
return msg;
}
});
function updateCount(data) {
if (owner) {
var txt = (data.total - 1) == 1 ? ' user' : ' users';
$stream.find('.n').html((data.total - 1) + txt);
function capture() {
var javascript = editors.javascript.getCode(),
html = editors.html.getCode(),
changed = false,
cursor,
msg = {};
msg.javascript = changes('javascript', javascript);
msg.html = changes('html', html);
if (msg.html.text || msg.javascript.text) {
msg.panel = getFocusedPanel();
cursor = editors[msg.panel].getCursor();
msg.line = cursor.line;
msg.ch = cursor.ch;
console.log('sending', msg);
forbind.send(msg);
}
}
}
global.stream = {
create: function () {
key = (Math.abs(~~(Math.random()*+new Date))).toString(32); // OTT?
if (typeof window.forbind !== 'undefined') forbind.on({
join: function (event) {
$body.addClass('streaming').removeClass('pausestream');
streaming = true;
if (event.isme) {
$('#stream').fadeOut('fast').prev().addClass('right');
}
if (event.isme && event.readonlykey) {
owner = true;
sessionStorage.setItem('streamwritekey', event.readonlykey);
sessionStorage.setItem('streamkey', key);
var type, editorTimer = { javascript: null, html: null };
// this code is completely over the top - need to simplify
$stream.find('.msg').html('streaming on <a href="/?stream=' + key + '">http://jsbin.com/?stream=' + key + '</a> to #');
$stream.removeClass('listen');
// for (type in editors) {
// (function (type) {
// try {
// $(editors[type].win.document).bind('keyup', throttle(function () {
// if (streaming) {
// console.log('capture?');
// capture();
// }
// }, 250));
// } catch (e) {}
// })(type);
// }
$(document).bind('codeChange', throttle(capture, 250));
}
updateCount(event);
},
leave: function (event) {
if (event.isme) {
if (!owner) {
$body.addClass('pausestream');
streaming = false;
$stream.one('click', function () {
window.location.search.replace(/stream=(.+?)\b/, function (n, key) {
global.stream.join(key);
});
});
} else {
$body.removeClass('streaming');
owner = false;
sessionStorage.removeItem('streamkey');
sessionStorage.removeItem('streamwritekey');
}
}
updateCount(event);
},
message: function (event) {
var msg = event.data;
updateCode(msg.javascript, 'javascript');
updateCode(msg.html, 'html');
// update preview if required
if ($body.is('.preview')) {
$('#preview').remove('iframe').append('<iframe class="stretch"></iframe>');
renderPreview();
} else {
var focused = editors[msg.panel];
focused.focus();
focused.setSelection({ line: msg.line, ch: msg.ch });
$(document).trigger('codeChange'); // does this bubble to our send function?
}
},
error: function (data) {
console.log('error in forbind', data);
}
});
function updateCode(msg, lang) {
var diff, patch, result, code;
if (msg.text) {
if (msg.diff) {
diff = new diff_match_patch();
code = editors[lang].getCode();
console.log(msg.text);
var patch = diff.patch_fromText(msg.text);
var result = diff.patch_apply(patch, code);
editors[lang].setCode(result[0]);
} else {
editors[lang].setCode(msg.text);
}
}
}
function updateCount(data) {
if (owner) {
var txt = (data.total - 1) == 1 ? ' user' : ' users';
$stream.find('.n').html((data.total - 1) + txt);
}
}
window.stream = global.stream = {
create: function () {
key = (Math.abs(~~(Math.random()*+new Date))).toString(32); // OTT?
forbind.create(key);
forbind.create(key);
return key;
},
join: function (key) {
forbind.join(key);
return key;
},
join: function (key) {
forbind.join(key);
owner = false;
sessionStorage.removeItem('streamkey');
sessionStorage.removeItem('streamwritekey');
owner = false;
sessionStorage.removeItem('streamkey');
sessionStorage.removeItem('streamwritekey');
$stream.addClass('listen');
$stream.addClass('listen');
$(document).one('keyup', function (event) {
if (streaming && event.which == 27) {
$(document).one('keyup', function (event) {
if (streaming && event.which == 27) {
global.stream.leave();
}
});
$stream.one('click', function () {
global.stream.leave();
}
});
});
$stream.one('click', function () {
global.stream.leave();
});
for (var type in editors) {
try {
$(editors[type].win.document).one('keyup', function (event) {
if (event.which == 27) {
global.stream.leave();
}
});
} catch (e) {
// because it sometimes throw an error on reconnecting trying to read win.document
for (var type in editors) {
try {
$(editors[type].win.document).one('keyup', function (event) {
if (event.which == 27) {
global.stream.leave();
}
});
} catch (e) {
// because it sometimes throw an error on reconnecting trying to read win.document
}
}
}
$stream.find('.msg').html('following live stream...');
},
leave: function () {
forbind.leave();
$stream.find('.msg').html('following live stream...');
},
leave: function () {
forbind.leave();
}
};
window.location.search.replace(/stream=(.+?)\b/, function (n, key) {
global.stream.join(key);
});
if (sessionStorage.getItem('streamkey')) {
key = sessionStorage.getItem('streamkey');
forbind.join(key, sessionStorage.getItem('streamwritekey') || undefined);
}
};
window.location.search.replace(/stream=(.+?)\b/, function (n, key) {
global.stream.join(key);
});
if (sessionStorage.getItem('streamkey')) {
key = sessionStorage.getItem('streamkey');
forbind.join(key, sessionStorage.getItem('streamwritekey') || undefined);
}
})(this);
}

15
js/chrome/tips.js Normal file
View File

@ -0,0 +1,15 @@
var $html = $(document.documentElement);
$('#tip a.dismiss').click(function () {
$html.removeClass('showtip');
$(window).resize();
sessionStorage.setItem('tips', 'false');
return false;
});
var showTips = sessionStorage.getItem('tips');
if (showTips === null) {
// $html.addClass('showtip');
}
// remove this setting after a few days (or a new set of tips come in)

190
js/editors/autocomplete.js Normal file
View File

@ -0,0 +1,190 @@
// Minimal event-handling wrapper.
function stopEvent() {
if (this.preventDefault) {this.preventDefault(); this.stopPropagation();}
else {this.returnValue = false; this.cancelBubble = true;}
}
function addStop(event) {
if (!event.stop) event.stop = stopEvent;
return event;
}
function connect(node, type, handler) {
function wrapHandler(event) {handler(addStop(event || window.event));}
if (typeof node.addEventListener == "function")
node.addEventListener(type, wrapHandler, false);
else
node.attachEvent("on" + type, wrapHandler);
}
function forEach(arr, f) {
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
}
function startTagComplete(editor) {
// We want a single cursor position.
if (editor.somethingSelected()) return;
// Find the token at the cursor
var cur = editor.getCursor(false), token = editor.getTokenAt(cur), tprop = token;
// If it's not a 'word-style' token, ignore the token.
if (!/^[\w$_]*$/.test(token.string)) {
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
className: token.string == "." ? "js-property" : null};
}
// If it is a property, find out what it is a property of.
while (tprop.className == "js-property") {
tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
if (tprop.string != ".") return;
tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
if (!context) var context = [];
context.push(tprop);
}
function insert(str) {
editor.replaceRange(str, {line: cur.line, ch: token.start}, {line: cur.line, ch: token.end});
}
insert('<></>');
editor.focus();
editor.setCursor({ line: cur.line, ch: token.end });
return true;
}
function startComplete(editor) {
// We want a single cursor position.
if (editor.somethingSelected()) return;
// Find the token at the cursor
var cur = editor.getCursor(false), token = editor.getTokenAt(cur), tprop = token;
// If it's not a 'word-style' token, ignore the token.
if (token.string == '') return;
if (!/^[\w$_]*$/.test(token.string)) {
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
className: token.string == "." ? "js-property" : null};
}
// If it is a property, find out what it is a property of.
while (tprop.className == "js-property") {
tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
if (tprop.string != ".") return;
tprop = editor.getTokenAt({line: cur.line, ch: tprop.start});
if (!context) var context = [];
context.push(tprop);
}
if (token.string == '' && context === undefined) return;
var completions = getCompletions(token, context, editor);
if (!completions.length) return;
function insert(str) {
editor.replaceRange(str, {line: cur.line, ch: token.start}, {line: cur.line, ch: token.end});
}
// When there is only one completion, use it directly.
if (completions.length == 1) {insert(completions[0]); return true;}
// Build the select widget
var complete = document.createElement("div");
complete.className = "completions";
var sel = complete.appendChild(document.createElement("select"));
sel.multiple = true;
for (var i = 0; i < completions.length; ++i) {
var opt = sel.appendChild(document.createElement("option"));
opt.appendChild(document.createTextNode(completions[i]));
}
sel.firstChild.selected = true;
sel.size = Math.min(10, completions.length);
var pos = editor.cursorCoords();
complete.style.left = pos.x + "px";
complete.style.top = pos.yBot + "px";
complete.style.position = 'absolute';
complete.style.outline = 'none';
complete.className = 'autocomplete';
document.body.appendChild(complete);
// Hack to hide the scrollbar.
if (completions.length <= 10) {
complete.style.width = (sel.clientWidth - 1) + "px";
}
var done = false;
function close() {
if (done) return;
done = true;
complete.parentNode.removeChild(complete);
}
function pick() {
insert(sel.options[sel.selectedIndex].value);
close();
setTimeout(function(){editor.focus();}, 50);
}
function pickandclose() {
pick()
setTimeout(function () { editor.focus(); }, 50);
}
connect(sel, "blur", close);
connect(sel, "keydown", function(event) {
var code = event.keyCode;
// Enter and space
if (code == 13 || code == 32) { event.stop(); pick();}
// Escape
else if (code == 27) {event.stop(); close(); editor.focus();}
else if (code != 38 && code != 40) {close(); editor.focus(); setTimeout(function () { startComplete(editor) }, 50);}
});
connect(sel, "dblclick", pick);
sel.focus();
// Opera sometimes ignores focusing a freshly created node
if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100);
return true;
}
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
"toUpperCase toLowerCase split concat match replace search").split(" ");
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
var funcProps = "prototype apply call bind".split(" ");
var keywords = ("break case catch continue debugger default delete do else false finally for function " +
"if in instanceof new null return switch throw true try typeof var void while with").split(" ");
function getCompletions(token, context, editor) {
var found = [], start = token.string;
function maybeAdd(str) {
if (str && str != start && str.indexOf(start) == 0 && found.indexOf(str) === -1) found.push(str);
}
function gatherCompletions(obj) {
if (typeof obj == "string") forEach(stringProps, maybeAdd);
else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
else if (obj instanceof Function) forEach(funcProps, maybeAdd);
for (var name in obj) maybeAdd(name);
}
if (context) {
// If this is a property, see if it belongs to some object we can
// find in the current environment.
var obj = context.pop(), base;
if (obj.className == "js-variable")
base = window[obj.string];
else if (obj.className == "js-string")
base = "";
else if (obj.className == "js-atom")
base = 1;
while (base != null && context.length)
base = base[context.pop().string];
if (base != null) gatherCompletions(base);
}
else {
// If not, just look in the window object and any local scope
// (reading into JS mode internals to get at the local variables)
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
gatherCompletions(window);
forEach(keywords, maybeAdd);
}
// also look up symbols in the current document
var code = editor.getValue().split(/\W/);
forEach(code, maybeAdd);
return found;
}

View File

@ -1,12 +1,18 @@
// used to generate the ../vendor/codemirror/basefiles.js
//= require "../vendor/codemirror/util"
//= require "../vendor/codemirror/stringstream"
//= require "../vendor/codemirror/undo"
//= require "../vendor/codemirror/editor"
//= require "../vendor/codemirror/tokenize"
//= require "../vendor/codemirror/select"
//= require "../vendor/codemirror/parsexml"
//= require "../vendor/codemirror/parsecss"
//= require "../vendor/codemirror/tokenizejavascript"
//= require "../vendor/codemirror/parsejavascript"
//= require "../vendor/codemirror/parsehtmlmixed"
// = require "../vendor/codemirror/util"
// = require "../vendor/codemirror/stringstream"
// = require "../vendor/codemirror/undo"
// = require "../vendor/codemirror/editor"
// = require "../vendor/codemirror/tokenize"
// = require "../vendor/codemirror/select"
// = require "../vendor/codemirror/parsexml"
// = require "../vendor/codemirror/parsecss"
// = require "../vendor/codemirror/tokenizejavascript"
// = require "../vendor/codemirror/parsejavascript"
// = require "../vendor/codemirror/parsehtmlmixed"
//= require "../vendor/codemirror2/codemirror"
//= require "../vendor/codemirror2/xml"
//= require "../vendor/codemirror2/css"
//= require "../vendor/codemirror2/javascript"
//= require "../vendor/codemirror2/htmlmixed"

View File

@ -1,68 +1,62 @@
//= require <codemirror>
// = require <codemirror>
//= require "codemirror"
//= require "mobileCodeMirror"
//= require "library"
//= require "unsaved"
//= require "autocomplete"
var focusPanel = 'javascript';
var editors = {};
editors.html = CodeMirror.fromTextArea('html', {
basefiles: ['basefiles.js'],
window.editors = editors;
editors.html = CodeMirror.fromTextArea(document.getElementById('html'), {
parserfile: [],
stylesheet: ["/css/codemirror.css", "/css/htmlcodeframe.css"],
path: '/js/vendor/codemirror/',
tabMode: 'shift',
iframeClass: 'stretch codeframe',
initCallback: function () {
setupEditor('html');
}
mode: 'text/html',
onChange: changecontrol,
theme: jsbin.settings.theme
});
editors.javascript = CodeMirror.fromTextArea('javascript', {
basefiles: ['basefiles.js'],
parserfile: ['parsejavascript.js'], // forces a switch back to JS parsing
stylesheet: ["/css/codemirror.css", "/css/codeframe.css"],
path: '/js/vendor/codemirror/',
iframeClass: 'stretch codeframe javascript',
editors.javascript = CodeMirror.fromTextArea(document.getElementById('javascript'), {
mode: 'javascript',
tabMode: 'shift',
initCallback: function () {
setupEditor('javascript');
}
onChange: changecontrol,
theme: jsbin.settings.theme
});
setupEditor('javascript');
setupEditor('html');
var editorsReady = setInterval(function () {
if (editors.html.ready && editors.javascript.ready) {
clearInterval(editorsReady);
editors.ready = true;
if (typeof editors.onReady == 'function') editors.onReady();
$(document).trigger('jsbinReady');
$document.bind('sizeeditors', function () {
var $el = $(editors.html.win),
top = 0, //$el.offset().top,
height = $('#bin').height();
$el.height(height - top);
$(editors.javascript.win).height(height - top - $error.filter(':visible').height());
editors.javascript.refresh();
editors.html.refresh();
});
$(window).resize(function () {
setTimeout(function () {
$document.trigger('sizeeditors');
}, 100);
});
$document.trigger('sizeeditors');
$document.trigger('jsbinReady');
}
}, 100);
var oninputSupported = (function () {
var input = document.createElement('input');
var support = "oninput" in input && "A";
if ( !support ) {
input.setAttribute("oninput", "return;");
support = typeof input.oninput === "function" && "B";
}
if ( !support ) {
try {
var e = document.createEvent("KeyboardEvent");
e.initKeyEvent("keypress", true, true, window, false, false, false, false, 0, "e".charCodeAt(0));
document.body.appendChild(input);
input.addEventListener("input", function(e) { support = "C"; e.preventDefault(); e.stopPropagation(); }, false);
input.focus();
input.dispatchEvent(e);
document.body.removeChild(input);
} catch( e ) {}
}
})();
function focused(event) {
focusPanel = this.id;
$('#bin').toggleClass('javascript', this.id == 'javascript');
$(editors.html.win.document).find('body').removeClass('focus');
$(editors.javascript.win.document).find('body').removeClass('focus');
$(this).find('body').addClass('focus');
function focused(editor, event) {
focusPanel = editor.id;
}
function getFocusedPanel() {
@ -73,18 +67,36 @@ function setupEditor(panel) {
var e = editors[panel],
focusedPanel = sessionStorage.getItem('panel');
e.wrapping.style.position = 'static';
e.wrapping.style.height = 'auto';
e.win.document.id = panel;
$(e.win.document).bind('keydown', keycontrol);
$(e.win.document).bind(oninputSupported ? 'input' : 'keyup', changecontrol);
$(e.win.document).focus(focused);
// overhang from CodeMirror1
e.setCode = function (str) {
e.setValue(str);
};
e.getCode = function () {
return e.getValue();
};
e.currentLine = function () {
var pos = e.getCursor();
return pos.line;
};
e.setOption('onChange', changecontrol);
e.setOption('onKeyEvent', keycontrol);
e.setOption('onFocus', focused);
e.id = panel;
e.win = e.getWrapperElement();
$(e.win).click(function () {
e.focus();
});
var $label = $('.code.' + panel + ' > .label');
if (document.body.className.indexOf('ie6') === -1) {
$(e.win.document).bind('scroll', function (event) {
if (this.body.scrollTop > 10) {
e.setOption('onScroll', function (event) {
if (e.win.scrollTop > 10) {
$label.stop().animate({ opacity: 0 }, 50, function () {
$(this).hide();
});
@ -98,18 +110,20 @@ function setupEditor(panel) {
e.ready = true;
if (focusedPanel == panel || focusedPanel == null && panel == 'javascript') {
// e.selectLines(e.nthLine(sessionStorage.getItem('line')), sessionStorage.getItem('character'));
e.focus();
e.selectLines(e.nthLine(sessionStorage.getItem('line')), sessionStorage.getItem('character'));
e.setCursor({ line: (sessionStorage.getItem('line') || 0) * 1, ch: (sessionStorage.getItem('character') || 0) * 1 });
}
}
function populateEditor(panel) {
// populate - should eventually use: session, saved data, local storage
var data = sessionStorage.getItem(panel), // session code
saved = localStorage.getItem('saved-' + panel), // user template
sessionURL = sessionStorage.getItem('url'),
changed = false;
if (data == template[panel]) { // restored from original saved
editors[panel].setCode(data);
} else if (data && sessionURL == template.url) { // try to restore the session first - only if it matches this url
@ -164,33 +178,94 @@ function accessKey(event) {
return on;
}
function keycontrol(event) {
var ctrl = accessKey(event);
if (ctrl && event.which == 39 && this.id == 'javascript') {
function keycontrol(panel, event) {
var ctrl = event.ctrlKey; //accessKey(event);
if (ctrl && event.which == 39 && panel.id == 'javascript') {
// go right
editors.html.focus();
return false;
} else if (ctrl && event.which == 37 && this.id == 'html') {
event.stop();
} else if (ctrl && event.which == 37 && panel.id == 'html') {
// go left
editors.javascript.focus();
return false;
event.stop();
} else if (ctrl && event.which == 49) { // 49 == 1 key
$('#control a.source').click();
return false;
event.stop();
} else if (event.which == 191 && event.shiftKey && event.metaKey) {
// show help
console.log('showing help - TBI');
event.stop();
} else if (ctrl && event.which == 50) {
$('#control a.preview').click();
return false;
event.stop();
} else if (event.which == 27) {
event.stop();
return startComplete(panel);
} else if (event.which == 190 && event.altKey && event.metaKey && panel.id == 'html') {
// auto close the element
if (panel.somethingSelected()) return;
// Find the token at the cursor
var cur = panel.getCursor(false), token = panel.getTokenAt(cur), tprop = token;
// If it's not a 'word-style' token, ignore the token.
if (!/^[\w$_]*$/.test(token.string)) {
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
className: token.string == "." ? "js-property" : null};
}
panel.replaceRange('</' + token.state.htmlState.context.tagName + '>', {line: cur.line, ch: token.end}, {line: cur.line, ch: token.end});
event.stop();
} else if (event.which == 188 && event.ctrlKey && event.shiftKey) {
// start a new tag
event.stop();
return startTagComplete(panel);
} else if (event.which == 191 && event.metaKey) {
// auto close the element
if (panel.somethingSelected()) return;
var cur = panel.getCursor(false),
token = panel.getTokenAt(cur),
type = token && token.state && token.state.token ? token.state.token.name : 'javascript',
line = panel.getLine(cur.line);
if (type == 'css') {
if (line.match(/\s*\/\*/) !== null) {
// already contains comment - remove
panel.setLine(cur.line, line.replace(/\/\*\s?/, '').replace(/\s?\*\//, ''));
} else {
// panel.replaceRange('// ', {line: cur.line, ch: 0}, {line: cur.line, ch: 0});
panel.setLine(cur.line, '/* ' + line + ' */');
}
} else if (type == 'javascript') {
// FIXME - could put a JS comment next to a <script> tag
if (line.match(/\s*\/\//) !== null) {
// already contains comment - remove
panel.setLine(cur.line, line.replace(/(\s*)\/\/\s?/, '$1'));
} else {
// panel.replaceRange('// ', {line: cur.line, ch: 0}, {line: cur.line, ch: 0});
panel.setLine(cur.line, '// ' + line);
}
} else if (type == 'html') {
if (line.match(/\s*<!--/) !== null) {
// already contains comment - remove
panel.setLine(cur.line, line.replace(/<!--\s?/, '').replace(/\s?-->/, ''));
} else {
// panel.replaceRange('// ', {line: cur.line, ch: 0}, {line: cur.line, ch: 0});
panel.setLine(cur.line, '<!-- ' + line + ' -->');
}
}
event.stop();
}
return true;
// return true;
}
function changecontrol(event) {
// sends message to the document saying that a key has been pressed, we'll ignore the control keys
if (! ({ 16:1, 17:1, 18:1, 20:1, 27:1, 37:1, 38:1, 39:1, 40:1, 91:1, 93:1 })[event.which] ) {
// if (! ({ 16:1, 17:1, 18:1, 20:1, 27:1, 37:1, 38:1, 39:1, 40:1, 91:1, 93:1 })[event.which] ) {
$(document).trigger('codeChange');
}
// }
return true;
}
}

View File

@ -43,8 +43,8 @@ Libraries.prototype.init = function () {
scripts: [
{ text: 'jQuery latest', url: 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js' },
{ text: 'jQuery WIP (via git)', url: 'http://code.jquery.com/jquery-git.js' },
{ text: 'jQuery 1.5.2', url: 'http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js' },
{ text: 'jQuery 1.4.4', url: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js' }
{ text: 'jQuery 1.5.2', url: 'http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js' },
{ text: 'jQuery 1.4.4', url: 'http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js' }
]
},
jqueryui : {
@ -66,8 +66,9 @@ Libraries.prototype.init = function () {
others: {
text: 'Others',
scripts: [
{ text: 'underscore', url: 'http://documentcloud.github.com/underscore/underscore-min.js'},
{ text: 'Rapha&euml;l', url: 'https://github.com/DmitryBaranovskiy/raphael/raw/master/raphael-min.js'}
{ text: 'Modernizr', url: 'http://cdnjs.cloudflare.com/ajax/libs/modernizr/2.0.4/modernizr.min.js'},
{ text: 'underscore', url: 'http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.1.6/underscore-min.js'},
{ text: 'Rapha&euml;l', url: 'http://cdnjs.cloudflare.com/ajax/libs/raphael/1.5.2/raphael-min.js'}
]
},
scriptaculous: {

View File

@ -16,23 +16,28 @@ $('#library').bind('init', function () {
$select.html( html.join('') ).val(selected);
}).trigger('init');
var state = {};
$('#library').bind('change', function () {
var libIndex = [],
lib = {},
state = {},
re,
i,
code = editors.html.getCode();
// strip existing libraries out
var addAdjust = code.match(/<(script|link) class="jsbin"/g);
if (addAdjust == null) addAdjust = [];
code = code.replace(/<script class="jsbin".*><\/script>\n?/g, '');
code = code.replace(/<link class="jsbin".*\/>\n?/g, '');
if (this.value != 'none') {
// to restore
// to restore (note - the adjustment isn't quite 100% right yet)
state = {
line: editors.html.currentLine(),
character: editors.html.cursorPosition().character
character: editors.html.getCursor().ch,
add: 1 - addAdjust.length
};
libIndex = this.value.split('-');
@ -41,15 +46,21 @@ $('#library').bind('change', function () {
// all has to happen in reverse order because we're going directly after <head>
code = code.replace('<head', "<head>\n<" + 'script class="jsbin" src="' + lib.scripts[libIndex[1]].url + '"><' + '/script');
if (lib.requires) {
state.add++;
code = code.replace('<head', "<head>\n<" + 'script class="jsbin" src="' + lib.requires + '"><' + '/script');
}
if (lib.style) {
state.add++;
code = code.replace('<head', "<head>\n<" + 'link class="jsbin" href="' + lib.style + '" rel="stylesheet" type="text/css" /');
}
state.line += state.add;
} else {
state.line -= state.add;
}
editors.html.setCode(code);
editors.html.focus();
editors.html.selectLines(editors.html.nthLine(state.line), state.character);
editors.html.setCursor({ line: state.line, ch: state.character });
});

View File

@ -6,9 +6,14 @@ $(document).bind('codeChange', function (event, revert, onload) {
$revert.removeClass('enable');
}
updateTitle(revert, onload);
});
function updateTitle(revert, onload) {
var title = !documentTitle ? 'JS Bin' : documentTitle;
if (!revert && !/\*$/.test(document.title)) {
if (/debug/i.test(document.title)) {
document.title = 'JS Bin - [unsaved]';
document.title = title + ' - [unsaved]';
}
document.title += '*';
@ -17,5 +22,7 @@ $(document).bind('codeChange', function (event, revert, onload) {
}
} else if (revert && /\*$/.test(document.title)) {
document.title = document.title.replace(/\*$/, '');
} else {
document.title = title;
}
});
}

View File

@ -5,7 +5,23 @@ jQuery.expr[':'].host = function(obj, index, meta, stack) {
return obj.host == meta[3];
};
// jQuery plugins
//= require "chrome/splitter"
(function (window, document, undefined) {
function throttle(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
//= require "chrome/storage"
window.jsbin.settings = JSON.parse(localStorage.getItem('settings') || '{ "show": { "html": true, "javascript": true }, "theme": "default" }');
//= require "vendor/json2"
//= require "editors/editors"
//= require "render/render"
//= require "chrome/beta"

View File

@ -16,7 +16,7 @@ ConsoleContext.prototype = {
var context = this.executable ? this.context() : this.context;
var re = new RegExp('console\.' + method + '\\((.*?)\\)');
// if the log was triggered via a jQuery.Event then it came from /within/ the preview
if (!(arguments.callee.caller.caller.arguments[0] instanceof jQuery.Event) && context) {
if (arguments.callee.caller.caller.arguments.length > 0 && !(arguments.callee.caller.caller.arguments[0] instanceof jQuery.Event) && context) {
context.eval('console.' + method + '(' + arguments.callee.caller.caller.arguments[0].toString().match(re)[1] + ')');
} else {
this.original[method].apply(this.original, args);
@ -51,4 +51,4 @@ ConsoleContext.prototype = {
window.top.console = this.original;
}
}
};
};

View File

@ -24,7 +24,7 @@
var el = document.createElement('a');
set(el, { opacity: 0, position: 'fixed', top: 0, right: 0, padding: '5px', background: '#eee', color: '#212121', 'border-bottom-left-radius': '10px', MozBorderRadiusBottomleft: '10px', border: '1px solid #999', borderRight: 0, borderTop: 0, textDecoration: 'none', font: '12px "Helvetica Neue", Arial, Helvetica' });
set(el, { opacity: 0, position: 'fixed', top: 0, right: 0, padding: '5px', background: '#eee url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAeCAYAAADtlXTHAAAAM0lEQVQIHWM4c+bMf6Z///4xMP39+5eB6f///wwQLlgMjYsQwykBVoIg0NShcbGZB3IGAKmKWgUE0gb7AAAAAElFTkSuQmCC) repeat-x', color: '#000', 'text-shadow': '0px -1px 0 #ccc', 'border-bottom-left-radius': '10px', MozBorderRadiusBottomleft: '10px', border: '1px solid #999', borderRight: 0, borderTop: 0, textDecoration: 'none', font: '12px "Helvetica Neue", Arial, Helvetica' });
el.innerHTML = 'Edit using JS Bin';
el.href = window.location.pathname + (window.location.pathname.substr(-1) == '/' ? '' : '/') + 'edit';

View File

@ -1,36 +1,46 @@
var $live = $('#live'),
$body = $('body'),
throttledPreview = throttle(renderLivePreview, 100);
showlive = $('#showlive')[0],
throttledPreview = throttle(renderLivePreview, 200);
//= require "consoleContext"
var hijackedConsole = new ConsoleContext(function () {
return $('#live iframe').length ? $('#live iframe')[0].contentWindow : null;
});
$body.delegate('#control .button.live', 'click', function () {
$live.trigger('toggle');
});
///= require "consoleContext"
// var hijackedConsole = new ConsoleContext(function () {
// return $('#live iframe').length ? $('#live iframe')[0].contentWindow : null;
// });
// could chain - but it's more readable like this
$live.bind('show', function () {
// hijackedConsole.activate();
$body.addClass('live');
showlive.checked = true;
localStorage && localStorage.setItem('livepreview', true);
var data = $live.data();
if (data.splitter) {
data.splitter.show().trigger('init');
}
// start timer
$(document).bind('codeChange.live', throttledPreview);
renderLivePreview();
//hijackedConsole.activate();
}).bind('hide', function () {
// hijackedConsole.deactivate();
$(document).unbind('codeChange.live');
localStorage && localStorage.removeItem('livepreview');
$body.removeClass('live');
//hijackedConsole.deactivate();
showlive.checked = false;
$('#source').css('right', 0);
var data = $live.data();
if (data.splitter) {
data.splitter.hide();
}
}).bind('toggle', function () {
$live.trigger($body.is('.live') ? 'hide' : 'show');
});
function enableLive() {
$('#control .buttons .preview').after('<a id="showlive" class="button live group right left light gap" href="#">Live</a>');
// $('#control .buttons .preview').after('<a id="showlive" class="button live group right left light gap" href="#">Live</a>');
}
function two(s) {
@ -38,10 +48,10 @@ function two(s) {
}
function renderLivePreview() {
var oldframe = $live.find('iframe').remove();
var frame = $live.append('<iframe class="stretch"></iframe>').find('iframe')[0],
var source = getPreparedCode(),
oldframe = $live.find('iframe').remove(),
frame = $live.append('<iframe class="stretch"></iframe>').find('iframe')[0],
document = frame.contentDocument || frame.contentWindow.document,
source = getPreparedCode(),
window = document.defaultView || document.parentWindow,
d = new Date();
@ -49,9 +59,13 @@ function renderLivePreview() {
window.alert = function () {};
window.prompt = function () {};
window.confirm = function () {};
// window.XMLHttpRequest = function () {};
if (!useCustomConsole) console.log('--- refreshing live preview @ ' + [two(d.getHours()),two(d.getMinutes()),two(d.getSeconds())].join(':') + ' ---');
// strip autofocus from the markup - prevents the focus switching out of the editable area
source = source.replace(/(<.*?\s)(autofocus)/g, '$1');
document.open();
if (debug) {
@ -67,5 +81,5 @@ function renderLivePreview() {
}
$live.find('.close').click(function () {
$live.trigger('hide');
updatePanel('live', false);
});

View File

@ -41,15 +41,29 @@ function getPreparedCode() {
} else if (/%code%/.test(source)) {
parts = source.split('%code%');
source = parts[0] + js + parts[1];
} else {
} else if (js) {
parts = source.split('</body>');
source = parts[0] + "<script src=\"http://jsbin.com/js/render/console.js\"></script>\n<script>\ntry {\n" + js + "\n} catch (e) {" + (window.console === undefined ? '_' : 'window.top.') + "console.error(e)}\n</script>\n</body>" + parts[1];
source = parts[0];
var close = parts.length == 2 ? '</body>' + parts[1] : '';
if (useCustomConsole) {
source += "<script src=\"http://jsbin.com/js/render/console.js\"></script>\n<script>\n";
}
source += "<script>\ntry {\n" + js + "\n} catch (e) {" + (window.console === undefined ? '_' : 'window.top.') + "console.error(e)}\n</script>\n" + close;
}
// specific change for rendering $(document).ready() because iframes doesn't trigger ready (TODO - really test in IE, may have been fixed...)
if (/\$\(document\)\.ready/.test(source)) {
source = source.replace(/\$\(document\)\.ready/, 'window.onload = ');
}
// read the element out of the source code and plug it in to our document.title
var newDocTitle = source.match(/<title>(.*)<\/title>/i);
if (newDocTitle !== null && newDocTitle[1] !== documentTitle) {
documentTitle = newDocTitle[1];
updateTitle();
}
return source;
}
@ -71,4 +85,4 @@ function renderPreview() {
win.write(source);
}
win.close();
}
}

2155
js/vendor/diff_match_patch_uncompressed.js vendored Normal file

File diff suppressed because it is too large Load Diff