<html ng-app="app">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body ng-controller="demoController">
<button ng-click="undo()" ng-if="canUndo">Undo</button>
<button ng-click="redo()" ng-if="canRedo">Redo</button>
<div scrolly="increaseLimit()" class="scrollit" >
<table class="table table-bordered" ng-keypress="onKeyPress($event)">
<!-- {{ itemsArr }} -->
<thead>
<tr>
<th>Operation Description</th>
<th>Requirements</th>
<th>Potential Failure Mode</th>
<th>Potential Effects of failure mode</th>
</tr>
</thead>
<tbody ng-repeat="(opIndex, items) in itemsArr | limitTo:limit">
<tr ng-repeat="Requirement in items.Requirements" >
<td ng-style="items.isItemDeleted && {'background-color': '#f2dede'}" rowspan="{{items.Requirements.length}}" ng-if="$first">
<span ng-hide="items.itemEditing" ng-click="editItem(items,'itemEditing')" >{{items.operation}}</span>
<input ng-show="items.itemEditing" ng-model="items.operation" ng-blur="doneEditing(items,'itemEditing')" autofocus />
<button ng-if="!items.isItemDeleted" type="button" style="float: right;" class="btn btn-success btn-circle" ng-click="addNewItem(opIndex)"> <span class="glyphicon glyphicon-plus"></span></button>
<button ng-if="!items.isItemDeleted" type="button" style="float: right;" class="btn btn-danger btn-circle" ng-click="deleteItem(opIndex)"> <span class="glyphicon glyphicon-minus"></span></button>
</td>
<td ng-style="Requirement.isDeleted && {'background-color': '#f2dede'} || Requirement.isNewRequirement && {'background-color': '#dff0d8'}"
rowspan="{{Requirement.effectsLength}}" ng-if="Requirement.PC!=''">
<span ng-hide="Requirement.pcEditing" ng-click="editItem(Requirement,'pcEditing')">{{Requirement.PC}}</span>
<input ng-show="Requirement.pcEditing" ng-model="Requirement.PC" ng-blur="doneEditing(Requirement,'pcEditing')" autofocus />
<button ng-if="!Requirement.isDeleted" type="button" style="float: right;" class="btn btn-success btn-circle" ng-click="addNewRequirement(opIndex,$index,Requirement)"> <span class="glyphicon glyphicon-plus"></span></button>
<button ng-if="!Requirement.isDeleted" type="button" style="float: right;" class="btn btn-danger btn-circle" ng-click="deleteRequirement(items.opid,opIndex,Requirement.pcid,Requirement.fmid,$index)"> <span class="glyphicon glyphicon-minus"></span></button>
</td>
<td ng-style="Requirement.isDeleted && {'background-color': '#f2dede'} || Requirement.isNewRequirement && {'background-color': '#dff0d8'}"
rowspan="{{Requirement.effectsLength}}" ng-if="Requirement.FM!=''">
<span ng-hide="Requirement.fmEditing" ng-click="editItem(Requirement,'fmEditing')">{{Requirement.FM}}</span>
<input ng-show="Requirement.fmEditing" ng-model="Requirement.FM" ng-blur="doneEditing(Requirement,'fmEditing')" autofocus />
<button ng-if="!Requirement.isDeleted" type="button" style="float: right;" class="btn btn-success btn-circle" ng-click="addNewRequirement(opIndex,$index,Requirement)"> <span class="glyphicon glyphicon-plus"></span></button>
<button ng-if="!Requirement.isDeleted" type="button" style="float: right;" class="btn btn-danger btn-circle" ng-click="deleteRequirement(items.opid,opIndex,Requirement.pcid,Requirement.fmid,$index)"> <span class="glyphicon glyphicon-minus"></span></button>
</td>
<td ng-style="Requirement.isDeleted && {'background-color': '#f2dede'} || Requirement.isEffectsDeleted && {'background-color': '#f2dede'}
|| Requirement.isNewRequirement && {'background-color': '#dff0d8'} || Requirement.isNewEffect && {'background-color': '#dff0d8'} ">
<span ng-hide="Requirement.effectEditing" ng-click="editItem(Requirement,'effectEditing')">{{Requirement.effect}}</span>
<input ng-show="Requirement.effectEditing" ng-model="Requirement.effect" ng-blur="doneEditing(Requirement,'effectEditing')" autofocus />
<button ng-if="!Requirement.isDeleted && !Requirement.isEffectsDeleted" type="button" style="float: right;" class="btn btn-primary btn-circle" ng-click="addNewEffect(items.opid,opIndex,Requirement.pcid,Requirement.fmid,$index)"> <span class="glyphicon glyphicon-plus"></span></button>
<button ng-if="!Requirement.isDeleted && !Requirement.isEffectsDeleted" type="button" style="float: right;" class="btn btn-danger btn-circle" ng-click="deleteEffect(opIndex,$index)"> <span class="glyphicon glyphicon-minus"></span></button>
</td>
</tr>
</tbody>
</table>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src=" https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-hotkeys/1.7.0/hotkeys.min.js"></script>
<script src="https://rawgit.com/Blitzen/Angular-Chronicle/master/chronicle.js"></script>
</body>
</html>
var app = angular.module('app', [
'Chronicle',
'cfp.hotkeys'
])
app.controller('demoController', [
'$scope','Chronicle','hotkeys','dataService',
function ($scope,Chronicle,hotkeys,dataService) {
begin();
function begin(){
$scope.opid = 1;
$scope.reqid = 3;
$scope.itemsArr = [{
'opid': '1',
'operation': 'opt des..',
'Requirements': [
{'pcid': '1','fmid': '1', 'PC': 'PC1','FM': 'FM1','effect': 'Potential Effects 111....', 'effectsLength': '4' },
{ 'pcid': '1','fmid': '1', 'PC': '', 'FM': '', 'effect': 'Potential Effects 222....'},
{'pcid': '1','fmid': '1', 'PC': '', 'FM': '','effect': 'Potential Effects 333....' },
{'pcid': '1', 'fmid': '1', 'PC': '','FM': '','effect': 'Potential Effects 123....' },
{'pcid': '2','fmid': '2','PC': 'PC2', 'FM': 'FM2','effect': 'Potential Effects 444....','effectsLength': '3'},
{ 'pcid': '2', 'fmid': '2','PC': '', 'FM': '','effect': 'Potential Effects 555....' },
{'pcid': '2','fmid': '2', 'PC': '', 'FM': '', 'effect': 'Potential Effects 666....' },
{'pcid': '3', 'fmid': '3', 'PC': 'PC3','FM': 'FM3','effect': 'Potential Effects 777....','effectsLength': '3' },
{'pcid': '3', 'fmid': '3', 'PC': '', 'FM': '', 'effect': 'Potential Effects 888....' },
{ 'pcid': '3','fmid': '3', 'PC': '', 'FM': '','effect': 'Potential Effects 999....'}
]
}];
$scope.limit = 5;
dataService.getData().then(function (result) {
console.log("loading done...!!!",result.data);
$scope.itemsArr = result.data;
redoUndo();
});
}
function redoUndo(){
//redo undo functionality
$scope.canUndo = false;
$scope.canRedo = false;
//Creation of chronicle object
$scope.numChronicle = Chronicle.record('itemsArr', $scope);
$scope.canUndoRedo = function(){
$scope.canUndo = $scope.numChronicle.canUndo();
$scope.canRedo = $scope.numChronicle.canRedo();
};
$scope.numChronicle.addOnAdjustFunction($scope.canUndoRedo);
$scope.numChronicle.addOnUndoFunction($scope.canUndoRedo);
$scope.numChronicle.addOnRedoFunction($scope.canUndoRedo);
$scope.undo = function(){
$scope.numChronicle.undo()
};
$scope.redo = function(){
$scope.numChronicle.redo()
};
hotkeys.add({
combo: 'ctrl+z',
description: 'undo',
callback: function() {
$scope.numChronicle.undo();
}
});
hotkeys.add({
combo: 'ctrl+y',
description: 'redo',
callback: function() {
$scope.numChronicle.redo();
}
});
}
$scope.increaseLimit = function(){
$scope.limit += 10;
console.log("limit :=",$scope.limit)
}
$scope.addNewItem = function (opIndex) {
$scope.opid++;
var newOperation = {
'opid': $scope.opid,
'operation': '<new operation>',
'Requirements': [{
'pcid': $scope.reqid,
'fmid': $scope.reqid,
'PC': '<New PC>',
'FM': '<New FM>',
'effect': '<New Effect>',
'effectsLength': '1',
'isNewRequirement': true,
'isNewEffect': true
}]
};
$scope.itemsArr.splice(parseInt(opIndex) + 1, 0, newOperation);//.push(newOperation);
};
$scope.addNewRequirement = function (opIndex,requirementId,equirementObj) {
$scope.reqid++;
var newRequirement = {
'pcid': $scope.reqid,
'fmid': $scope.reqid,
'PC': '<New PC>',
'FM': '<New FM>',
'effect': '<New Effect>',
'effectsLength': '1',
'isNewRequirement': true,
'isNewEffect': true
};
$scope.itemsArr[opIndex].Requirements.splice(parseInt(requirementId) + parseInt(equirementObj.effectsLength), 0, newRequirement); //.push(newRequirement);
};
$scope.addNewEffect = function (opid, opIndex, pcid, fmid, requirementId) {
angular.forEach($scope.itemsArr[opIndex].Requirements, function (obj, index) {
if (obj.pcid == pcid && obj.fmid == fmid && obj.effectsLength != undefined) {
$scope.itemsArr[opIndex].Requirements[index].effectsLength = parseInt(obj.effectsLength) + 1;
}
});
var newRequirement = {
'pcid': pcid,
'fmid': fmid,
'PC': '',
'FM': '',
'effect': '<New Effect>',
'isNewEffect': true
};
$scope.itemsArr[opIndex].Requirements.splice(parseInt(requirementId) + 1, 0, newRequirement);
};
$scope.deleteRequirement = function (opid, opIndex, pcid, fmid, requirementId) {
//console.log(opid,opIndex,pcid,fmid, requirementId)
angular.forEach($scope.itemsArr[opIndex].Requirements, function (obj, index) {
if (obj.pcid == pcid && obj.fmid == fmid) {
$scope.itemsArr[opIndex].Requirements[index].isDeleted = true;
}
});
};
$scope.deleteEffect = function (opIndex, requirementId) {
$scope.itemsArr[opIndex].Requirements[requirementId].isEffectsDeleted = true;
};
$scope.deleteItem = function (opIndex) {
$scope.itemsArr[opIndex].isItemDeleted = true;
angular.forEach($scope.itemsArr[opIndex].Requirements, function (obj, index) {
$scope.itemsArr[opIndex].Requirements[index].isDeleted = true;
});
};
$scope.editItem = function (item, prop) {
if (!item.isDeleted && !item.isItemDeleted)
item[prop] = true;
};
$scope.doneEditing = function (item, prop) {
item[prop] = false; //dong some background ajax calling for persistence...
};
}
]);
app.directive('scrolly', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var raw = element[0];
console.log('loading directive');
element.bind('scroll', function () {
console.log('in scroll');
console.log(raw.scrollTop + raw.offsetHeight);
console.log(raw.scrollHeight);
if (raw.scrollTop + raw.offsetHeight > raw.scrollHeight) {
scope.$apply(attrs.scrolly);
}
});
}
};
});
app.factory("dataService", function($q) {
return {
getData:getData
}
function getData(){
var deferred = $q.defer();
var itemsArr =[];
var datasize = 50;
//poor performance when data is large
//var datasize = 5000;
console.log("start...");
for(var i=1;i<=datasize;i++){
var tempObj ={};
tempObj.opid = i;
tempObj.operation = 'opt des..'+i;
tempObj.Requirements = [
{'pcid': '1','fmid': '1', 'PC': 'PC1','FM': 'FM1','effect': 'Potential Effects 111....', 'effectsLength': '4' },
{ 'pcid': '1','fmid': '1', 'PC': '', 'FM': '', 'effect': 'Potential Effects 222....'},
{'pcid': '1','fmid': '1', 'PC': '', 'FM': '','effect': 'Potential Effects 333....' },
{'pcid': '1', 'fmid': '1', 'PC': '','FM': '','effect': 'Potential Effects 123....' },
{'pcid': '2','fmid': '2','PC': 'PC2', 'FM': 'FM2','effect': 'Potential Effects 444....','effectsLength': '3'},
{ 'pcid': '2', 'fmid': '2','PC': '', 'FM': '','effect': 'Potential Effects 555....' },
{'pcid': '2','fmid': '2', 'PC': '', 'FM': '', 'effect': 'Potential Effects 666....' },
{'pcid': '3', 'fmid': '3', 'PC': 'PC3','FM': 'FM3','effect': 'Potential Effects 777....','effectsLength': '3' },
{'pcid': '3', 'fmid': '3', 'PC': '', 'FM': '', 'effect': 'Potential Effects 888....' },
{ 'pcid': '3','fmid': '3', 'PC': '', 'FM': '','effect': 'Potential Effects 999....'}
]
itemsArr.push(tempObj);
}
deferred.resolve({
data: itemsArr
});
return deferred.promise;
}
});
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. |