UNPKG

angular-cached-resource

Version:

An AngularJS module to interact with RESTful resources, even when browser is offline

282 lines 9.75 kB
/** * angular-strap * @version v2.0.1 - 2014-04-10 * @link http://mgcrea.github.io/angular-strap * @author Olivier Louvignes (olivier@mg-crea.com) * @license MIT License, http://www.opensource.org/licenses/MIT */ 'use strict'; angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions']).provider('$modal', function () { var defaults = this.defaults = { animation: 'am-fade', backdropAnimation: 'am-fade', prefixClass: 'modal', prefixEvent: 'modal', placement: 'top', template: 'modal/modal.tpl.html', contentTemplate: false, container: false, element: null, backdrop: true, keyboard: true, html: false, show: true }; this.$get = [ '$window', '$rootScope', '$compile', '$q', '$templateCache', '$http', '$animate', '$timeout', '$sce', 'dimensions', function ($window, $rootScope, $compile, $q, $templateCache, $http, $animate, $timeout, $sce, dimensions) { var forEach = angular.forEach; var trim = String.prototype.trim; var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout; var bodyElement = angular.element($window.document.body); var htmlReplaceRegExp = /ng-bind="/gi; function ModalFactory(config) { var $modal = {}; // Common vars var options = $modal.$options = angular.extend({}, defaults, config); $modal.$promise = fetchTemplate(options.template); var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new(); if (!options.element && !options.container) { options.container = 'body'; } // Support scope as string options forEach([ 'title', 'content' ], function (key) { if (options[key]) scope[key] = $sce.trustAsHtml(options[key]); }); // Provide scope helpers scope.$hide = function () { scope.$$postDigest(function () { $modal.hide(); }); }; scope.$show = function () { scope.$$postDigest(function () { $modal.show(); }); }; scope.$toggle = function () { scope.$$postDigest(function () { $modal.toggle(); }); }; // Support contentTemplate option if (options.contentTemplate) { $modal.$promise = $modal.$promise.then(function (template) { var templateEl = angular.element(template); return fetchTemplate(options.contentTemplate).then(function (contentTemplate) { var contentEl = findElement('[ng-bind="content"]', templateEl[0]).removeAttr('ng-bind').html(contentTemplate); // Drop the default footer as you probably don't want it if you use a custom contentTemplate if (!config.template) contentEl.next().remove(); return templateEl[0].outerHTML; }); }); } // Fetch, compile then initialize modal var modalLinker, modalElement; var backdropElement = angular.element('<div class="' + options.prefixClass + '-backdrop"/>'); $modal.$promise.then(function (template) { if (angular.isObject(template)) template = template.data; if (options.html) template = template.replace(htmlReplaceRegExp, 'ng-bind-html="'); template = trim.apply(template); modalLinker = $compile(template); $modal.init(); }); $modal.init = function () { // Options: show if (options.show) { scope.$$postDigest(function () { $modal.show(); }); } }; $modal.destroy = function () { // Remove element if (modalElement) { modalElement.remove(); modalElement = null; } if (backdropElement) { backdropElement.remove(); backdropElement = null; } // Destroy scope scope.$destroy(); }; $modal.show = function () { scope.$emit(options.prefixEvent + '.show.before', $modal); var parent = options.container ? findElement(options.container) : null; var after = options.container ? null : options.element; // Fetch a cloned element linked from template modalElement = $modal.$element = modalLinker(scope, function (clonedElement, scope) { }); // Set the initial positioning. modalElement.css({ display: 'block' }).addClass(options.placement); // Options: animation if (options.animation) { if (options.backdrop) { backdropElement.addClass(options.backdropAnimation); } modalElement.addClass(options.animation); } if (options.backdrop) { $animate.enter(backdropElement, bodyElement, null, function () { }); } $animate.enter(modalElement, parent, after, function () { scope.$emit(options.prefixEvent + '.show', $modal); }); scope.$isShown = true; scope.$$phase || scope.$root.$$phase || scope.$digest(); // Focus once the enter-animation has started // Weird PhantomJS bug hack var el = modalElement[0]; requestAnimationFrame(function () { el.focus(); }); bodyElement.addClass(options.prefixClass + '-open'); if (options.animation) { bodyElement.addClass(options.prefixClass + '-with-' + options.animation); } // Bind events if (options.backdrop) { modalElement.on('click', hideOnBackdropClick); backdropElement.on('click', hideOnBackdropClick); } if (options.keyboard) { modalElement.on('keyup', $modal.$onKeyUp); } }; $modal.hide = function () { scope.$emit(options.prefixEvent + '.hide.before', $modal); $animate.leave(modalElement, function () { scope.$emit(options.prefixEvent + '.hide', $modal); bodyElement.removeClass(options.prefixClass + '-open'); if (options.animation) { bodyElement.addClass(options.prefixClass + '-with-' + options.animation); } }); if (options.backdrop) { $animate.leave(backdropElement, function () { }); } scope.$isShown = false; scope.$$phase || scope.$root.$$phase || scope.$digest(); // Unbind events if (options.backdrop) { modalElement.off('click', hideOnBackdropClick); backdropElement.off('click', hideOnBackdropClick); } if (options.keyboard) { modalElement.off('keyup', $modal.$onKeyUp); } }; $modal.toggle = function () { scope.$isShown ? $modal.hide() : $modal.show(); }; $modal.focus = function () { modalElement[0].focus(); }; // Protected methods $modal.$onKeyUp = function (evt) { evt.which === 27 && $modal.hide(); }; // Private methods function hideOnBackdropClick(evt) { if (evt.target !== evt.currentTarget) return; options.backdrop === 'static' ? $modal.focus() : $modal.hide(); } return $modal; } // Helper functions function findElement(query, element) { return angular.element((element || document).querySelectorAll(query)); } function fetchTemplate(template) { return $q.when($templateCache.get(template) || $http.get(template)).then(function (res) { if (angular.isObject(res)) { $templateCache.put(template, res.data); return res.data; } return res; }); } return ModalFactory; } ]; }).directive('bsModal', [ '$window', '$location', '$sce', '$modal', function ($window, $location, $sce, $modal) { return { restrict: 'EAC', scope: true, link: function postLink(scope, element, attr, transclusion) { // Directive options var options = { scope: scope, element: element, show: false }; angular.forEach([ 'template', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation' ], function (key) { if (angular.isDefined(attr[key])) options[key] = attr[key]; }); // Support scope as data-attrs angular.forEach([ 'title', 'content' ], function (key) { attr[key] && attr.$observe(key, function (newValue, oldValue) { scope[key] = $sce.trustAsHtml(newValue); }); }); // Support scope as an object attr.bsModal && scope.$watch(attr.bsModal, function (newValue, oldValue) { if (angular.isObject(newValue)) { angular.extend(scope, newValue); } else { scope.content = newValue; } }, true); // Initialize modal var modal = $modal(options); // Trigger element.on(attr.trigger || 'click', modal.toggle); // Garbage collection scope.$on('$destroy', function () { modal.destroy(); options = null; modal = null; }); } }; } ]);