<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<base href="http://polygit.org/polymer+:master/components/">
<link href="polymer/polymer.html" rel="import">
</head>
<body>
<dom-module id="my-element">
<template>
<style>
:host {
display: block;
}
</style>
<h2>Hello [[id]]</h2>
<ul>
<li>propString: [[propString]]</li>
<li>
propArray:
<ol>
<template is="dom-repeat" items="[[propArray]]">
<li>[[item]]</li>
</template>
</ol>
</li>
<li>
propObject:
<ul>
<li>name: [[propObject.name]]</li>
<li>surname: [[propObject.surname]]</li>
</ul>
</li>
</ul>
</template>
<script>
if (!String.prototype.endsWith) {
String.prototype.endsWith = function(searchString, position) {
var subjectString = this.toString();
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.lastIndexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
}
(function() {
var SyncBehaviorInstances = [];
var SyncBehaviorLock = false;
SyncBehavior = {
attached: function() {
// Add instance
SyncBehaviorInstances.push(this);
// Add listeners
for(var property in this.properties) {
if('notify' in this.properties[property] && this.properties[property].notify) {
// Watch all properties with notify = true
var eventHanler = this._eventHandlerForPropertyType(this.properties[property].type.name);
this.listen(this, Polymer.CaseMap.camelToDashCase(property) + '-changed', eventHanler);
}
}
},
detached: function() {
// Remove instance
var index = SyncBehaviorInstances.indexOf(this);
if(index >= 0) {
SyncBehaviorInstances.splice(index, 1);
}
// Remove listeners
for(var property in this.properties) {
if('notify' in this.properties[property] && this.properties[property].notify) {
// Watch all properties with notify = true
var eventHanler = this._eventHandlerForPropertyType(this.properties[property].type.name);
this.unlisten(this, Polymer.CaseMap.camelToDashCase(property) + '-changed', eventHanler);
}
}
},
_eventHandlerForPropertyType: function(propertyType) {
switch(propertyType) {
case 'Array':
return '__syncArray';
case 'Object':
return '__syncObject';
default:
return '__syncPrimitive';
}
},
__syncArray: function(event, details) {
if(SyncBehaviorLock) {
return; // Prevent cycles
}
SyncBehaviorLock = true; // Lock
var target = event.target;
var prop = Polymer.CaseMap.dashToCamelCase(event.type.substr(0, event.type.length - 8));
if(details.path === undefined) {
// New array -> assign by reference
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
instance.set(prop, details.value);
}
});
} else if(details.path.endsWith('.splices')) {
// Array mutation -> apply notifySplices
var splices = details.value.indexSplices;
// for all other instances: assign reference if not the same, otherwise call 'notifySplices'
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
var instanceReference = instance.get(prop);
var targetReference = target.get(prop);
if(instanceReference !== targetReference) {
instance.set(prop, targetReference);
} else {
instance.notifySplices(prop, splices);
}
}
});
}
SyncBehaviorLock = false; // Unlock
},
__syncObject: function(event, details) {
var target = event.target;
var prop = Polymer.CaseMap.dashToCamelCase(event.type.substr(0, event.type.length - 8));
if(details.path === undefined) {
// New object -> assign by reference
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
instance.set(prop, details.value);
}
});
} else {
// Property change -> assign by reference if not the same, otherwise call 'notifyPath'
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
var instanceReference = instance.get(prop);
var targetReference = target.get(prop);
if(instanceReference !== targetReference) {
instance.set(prop, targetReference);
} else {
instance.notifyPath(details.path, details.value);
}
}
});
}
},
__syncPrimitive: function(event, details) {
var target = event.target;
var value = details.value;
var prop = Polymer.CaseMap.dashToCamelCase(event.type.substr(0, event.type.length - 8));
SyncBehaviorInstances.forEach(function(instance) {
if(instance !== target) {
instance.set(prop, value);
}
});
},
};
})();
Polymer({
is: 'my-element',
behaviors: [
SyncBehavior,
],
properties: {
id: {
type: String,
},
propString: {
type: String,
notify: true,
value: 'default value',
},
propArray: {
type: Array,
notify: true,
value: function() {
return ['a', 'b', 'c'];
},
},
propObject: {
type: Object,
notify: true,
value: function() {
return {'name': 'John', 'surname': 'Doe'};
},
},
},
pushToArray: function(item) {
this.push('propArray', item);
},
pushToNewArray: function(item) {
this.set('propArray', [item]);
},
popFromArray: function() {
this.pop('propArray');
},
setObjectName: function(name) {
this.set('propObject.name', name);
},
setNewObjectName: function(name) {
this.set('propObject', {'name': name, 'surname': 'unknown'});
},
});
</script>
</dom-module>
<template is="dom-bind">
<h4>Primitive type</h4>
propString: <input type="text" value="{{propString::input}}" />
<h4>Array type</h4>
Push to propArray: <input type="text" id="propArrayItem" /> <button onclick="_propArrayItem()">Push</button> <button onclick="_propNewArrayItem()">Push to NEW array</button> <button onclick="_propPopArrayItem()">Delete last element</button>
<h4>Object type</h4>
Set 'name' of propObject: <input type="text" id="propObjectName" /> <button onclick="_propObjectName()">Set</button> <button onclick="_propNewObjectName()">Set to NEW object</button> <br />
<script>
function _propArrayItem() {
one.pushToArray(propArrayItem.value);
}
function _propNewArrayItem() {
one.pushToNewArray(propArrayItem.value);
}
function _propPopArrayItem() {
one.popFromArray();
}
function _propObjectName() {
one.setObjectName(propObjectName.value);
}
function _propNewObjectName() {
one.setNewObjectName(propObjectName.value);
}
</script>
<my-element id="one" prop-string="{{propString}}"></my-element>
<my-element id="two"></my-element>
<my-element id="three"></my-element>
<my-element id="four"></my-element>
</template>
</body>
</html>
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |