<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/css/bootstrap-combined.min.css" rel="stylesheet">
<script>document.write("<base href=\"" + document.location + "\" />");</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js"></script>
<script src="http://code.angularjs.org/1.0.2/angular-resource.js"></script>
<script src="app.js"></script>
<script src="modal.js"></script>
<script src="loading.js"></script>
</head>
<body>
<div ng-app="project">
<h2>JavaScript Projects</h2>
<div data-loading></div>
<div ng-view></div>
<!-- CACHE FILE: list.html -->
<script type="text/ng-template" id="list.html">
<input type="text" ng-model="search" class="search-query" placeholder="Search">
<table>
<thead>
<tr>
<th>Project</th>
<th>Description</th>
<th><a href="#/new"><i class="icon-plus-sign"></i></a></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="project in projects | filter:search | orderBy:'name'">
<td><a href="{{project.site}}" target="_blank">{{project.name}}</a></td>
<td>{{project.description}}</td>
<td>
<a href="#/edit/{{project._id.$oid}}"><i class="icon-pencil"></i></a>
</td>
</tr>
</tbody>
</table>
</script>
<!-- CACHE FILE: detail.html -->
<script type="text/ng-template" id="detail.html">
<form name="myForm">
<div class="control-group" ng-class="{error: myForm.name.$invalid}">
<label>Name</label>
<input type="text" name="name" ng-model="project.name" required>
<span ng-show="myForm.name.$error.required" class="help-inline">
Required</span>
</div>
<div class="control-group" ng-class="{error: myForm.site.$invalid}">
<label>Website</label>
<input type="url" name="site" ng-model="project.site" required>
<span ng-show="myForm.site.$error.required" class="help-inline">
Required</span>
<span ng-show="myForm.site.$error.url" class="help-inline">
Not a URL</span>
</div>
<label>Description</label>
<textarea name="description" ng-model="project.description"></textarea>
<br>
<a href="#/list" class="btn">Cancel</a>
<button ng-click="save()" ng-disabled="isClean() || myForm.$invalid"
class="btn btn-primary">Save</button>
<button ng-click="destroy()"
ng-show="project._id" class="btn btn-danger">Delete</button>
</form>
</script>
</div>
</body>
</html>
angular.module('project', ['mongolab', 'LoadingModule']).
config(function($routeProvider) {
$routeProvider.
when('/list', {controller:ListCtrl, templateUrl:'list.html'}).
when('/edit/:projectId', {controller:EditCtrl, templateUrl:'detail.html'}).
when('/new', {controller:CreateCtrl, templateUrl:'detail.html'}).
otherwise({redirectTo:'/list'});
});
function ListCtrl($scope, Project) {
$scope.projects = Project.query();
}
function CreateCtrl($scope, $location, Project) {
$scope.save = function() {
Project.save($scope.project, function(project) {
$location.path('/edit/' + project._id.$oid);
});
};
}
function EditCtrl($scope, $location, $routeParams, Project) {
var self = this;
Project.get({id: $routeParams.projectId}, function(project) {
self.original = project;
$scope.project = new Project(self.original);
});
$scope.isClean = function() {
return angular.equals(self.original, $scope.project);
};
$scope.destroy = function() {
self.original.destroy(function() {
$location.path('/list');
});
};
$scope.save = function() {
$scope.project.update(function() {
$location.path('/list');
});
};
}
// This is a module for cloud persistance in mongolab - https://mongolab.com
angular.module('mongolab', ['ngResource']).
factory('Project', function($resource) {
var Project = $resource('https://api.mongolab.com/api/1/databases' +
'/angularjs/collections/projects/:id',
{ apiKey: '4f847ad3e4b08a2eed5f3b54' }, {
update: { method: 'PUT' }
}
);
Project.prototype.update = function(cb) {
return Project.update({id: this._id.$oid},
angular.extend({}, this, {_id:undefined}), cb);
};
Project.prototype.destroy = function(cb) {
return Project.remove({id: this._id.$oid}, cb);
};
return Project;
});
/* Put your css in here */
#Loading {
display: none;
line-height: 108px;
font-size: 20px;
font-weight: bold;
text-align: center;
height: 150px;
width: 300px;
border: 1px solid gray;
left:50%;
top:50%;
}
angular.module('LoadingModule', ['ui.bootstrap.modal'])
.config(function ($httpProvider) {
$httpProvider.responseInterceptors.push('onCompleteInterceptor');
})
.factory('onCompleteInterceptor', function ($q, $window, LoadingSrv) {
return function (promise) {
return promise.then(function (response) {
// do something on success
LoadingSrv.requestCount--;
return response;
}, function (response) {
// do something on error
LoadingSrv.requestCount--;
return $q.reject(response);
});
};
})
.service('onStartInterceptor', function (LoadingSrv) {
this.startSending = function (data, headersGetter) {
LoadingSrv.requestCount++;
return data;
};
})
.factory('LoadingSrv', function () {
return {
requestCount: 0,
isLoadingShown: function () {
return this.requestCount > 0;
}
};
})
.run(function ($http, onStartInterceptor) {
$http.defaults.transformRequest.push(onStartInterceptor.startSending);
})
.directive('loading', function (LoadingSrv) {
return {
restrict: 'A',
replace: true,
template: '<div modal width="250" height="140" show="isshown" options="options" id="Loading"><div class="modal-body">Loading</div></div>',
controller: function ($scope, $element, $attrs, LoadingSrv) {
$scope.$watch(function () { return LoadingSrv.requestCount; }, function (newVal) {
$scope.isshown = LoadingSrv.isLoadingShown();
}, true);
$scope.options = {
backdrop: true,
escape: false
};
}
};
});
// Angular UI , Warning this is not the official version. It is customized. -maxisam
angular.module('ui.bootstrap.modal', []).directive('modal', ['$parse', function ($parse) {
var body = angular.element(document.getElementsByTagName('body')[0]);
var defaultOpts = {
backdrop: true,
escape: true
};
return {
restrict: 'ECA',
link: function (scope, elm, attrs) {
var backdropEl;
var opts = angular.extend(defaultOpts, scope.$eval(attrs.uiOptions || attrs.bsOptions || attrs.options));
var shownExpr = attrs.modal || attrs.show;
var setClosed;
if (attrs.close) {
setClosed = function () {
scope.$apply(attrs.close);
};
} else {
setClosed = function () {
scope.$apply(function () {
$parse(shownExpr).assign(scope, false);
});
};
}
elm.addClass('modal');
if (opts.backdrop && !backdropEl) {
backdropEl = angular.element('<div class="modal-backdrop ngModal"></div>');
backdropEl.css('display', 'none');
elm.parent().append(backdropEl);
}
function setSize(scope, elm, width, height) {
var footerH = $(elm).find('.modal-footer').outerHeight();
width && elm.css({ 'width': width + 'px', 'margin-left': (-width / 2) + 'px' });
height && elm.css({ 'height': height + 'px', 'margin-top': (-height / 2) + 'px' });
//console.log(headerFooterH);
height && scope.$watch(function (s) {
return elm.find('.modal-header').height();
}, function (newVal) {
elm.find('.modal-body').css({
'height': function () {
return (height - footerH - $(elm).find('.modal-header').outerHeight(true) - 30) + 'px';
}
});
}, true);
height || scope.$watch(function (s) {
return elm.height();
}, function (newVal) {
elm.css({
'margin-top': function () {
return (-newVal / 2) + 'px';
}
});
}, true);
}
function setShown(shown) {
scope.$apply(function () {
model.assign(scope, shown);
});
}
function escapeClose(evt) {
if (evt.which === 27) { setClosed(); }
}
function clickClose() {
setClosed();
}
function close() {
if (opts.escape) { body.unbind('keyup', escapeClose); }
if (opts.backdrop) {
backdropEl.css('display', 'none').removeClass('in');
backdropEl.unbind('click', clickClose);
}
elm.css('display', 'none').removeClass('in');
body.removeClass('modal-open');
}
function open() {
if (opts.escape) { body.bind('keyup', escapeClose); }
if (opts.backdrop) {
backdropEl.css('display', 'block').addClass('in');
backdropEl.bind('click', clickClose);
}
elm.css('display', 'block').addClass('in');
body.addClass('modal-open');
setSize(scope, elm, attrs.width, attrs.height);
}
scope.$watch(shownExpr, function (isShown, oldShown) {
if (isShown) {
open();
} else {
close();
}
});
}
};
}]);