/*! jquery-mobile-angular-adapter - v1.3.2-SNAPSHOT - 2013-06-27 * https://github.com/opitzconsulting/jquery-mobile-angular-adapter * Copyright (c) 2013 Tobias Bosch; Licensed MIT */ (function(factory) { if (typeof define === "function" && define.amd) { define(["jquery", "angular", "jquery.mobile"], factory); } else { factory(window.jQuery, window.angular); } })(function($, angular) { (function ($) { function patch(obj, fnName, callback) { var _old = obj[fnName]; obj[fnName] = function () { return callback(_old, this, arguments); }; } function collectEventListeners(callback) { var unbindCalls = [], cleanupCalls = [], recursive = false, i; patchBindFn("on", "off"); try { callback(); } finally { for (i=0; i * Usage: Create a decorator or a factory for the $precompile service. */ (function ($, angular) { var ng = angular.module('ng'); ng.provider("$precompile", $precompileProvider); ng.config(['$provide', precompileCompileDecorator]); ng.config(['$compileProvider', '$provide', precompileTemplateDirectives]); return; // ------------------ function $precompileProvider() { var handlers = []; return { addHandler: function(handler) { handlers.push(handler); }, $get: ["$injector", function($injector) { return function(element) { var i; for (i=0; i' + html + ''); $precompile($template.contents()); return $template.html(); } function precompileTemplateDirectives ($compileProvider, $provide) { var directiveTemplateUrls = {}; // Hook into the registration of directives to: // - preprocess template html // - mark urls from templateUrls so we can preprocess it later in $http var _directive = $compileProvider.directive; $compileProvider.directive = function (name, factory) { var newFactory = function ($precompile, $injector) { var res = $injector.invoke(factory); if (res.template) { res.template = precompileHtmlString(res.template, $precompile); } else if (res.templateUrl) { directiveTemplateUrls[res.templateUrl] = true; } return res; }; return _directive.call(this, name, ['$precompile', '$injector', newFactory]); }; // preprocess $http results for templateUrls. $provide.decorator('$http', ['$q', '$delegate', '$precompile', function ($q, $http, $precompile) { var _get = $http.get; $http.get = function (url) { var res = _get.apply(this, arguments); if (directiveTemplateUrls[url]) { var _success = res.success; res.success = function(callback) { var newCallback = function() { var args = Array.prototype.slice.call(arguments); var content = args[0]; args[0] = precompileHtmlString(content, $precompile); return callback.apply(this, args); }; return _success(newCallback); }; } return res; }; return $http; }]); } })($, angular); (function(angular) { var ng = angular.module('ng'); ng.config(['$provide', function($provide) { $provide.decorator('$rootScope', ['$delegate', scopeReconnectDecorator]); $provide.decorator('$rootScope', ['$delegate', scopePostDigestDecorator]); $provide.decorator('$rootScope', ['$delegate', scopeReentranceDecorator]); }]); function scopeReconnectDecorator($rootScope) { $rootScope.$disconnect = function() { if (this.$root === this) { return; // we can't disconnect the root node; } var parent = this.$parent; this.$$disconnected = true; // See Scope.$destroy if (parent.$$childHead === this) { parent.$$childHead = this.$$nextSibling; } if (parent.$$childTail === this) { parent.$$childTail = this.$$prevSibling; } if (this.$$prevSibling) { this.$$prevSibling.$$nextSibling = this.$$nextSibling; } if (this.$$nextSibling) { this.$$nextSibling.$$prevSibling = this.$$prevSibling; } this.$$nextSibling = this.$$prevSibling = null; }; $rootScope.$reconnect = function() { if (this.$root === this) { return; // we can't disconnect the root node; } var child = this; if (!child.$$disconnected) { return; } var parent = child.$parent; child.$$disconnected = false; // See Scope.$new for this logic... child.$$prevSibling = parent.$$childTail; if (parent.$$childHead) { parent.$$childTail.$$nextSibling = child; parent.$$childTail = child; } else { parent.$$childHead = parent.$$childTail = child; } }; return $rootScope; } function scopePostDigestDecorator($rootScope) { var preListeners = [], postOneListeners = [], postAlwaysListeners = [], _digest = $rootScope.$digest; $rootScope.$preDigest = function(callback) { addListener(this, '$$preDigestListeners', callback); }; $rootScope.$postDigestOne = function(callback) { addListener(this, '$$postDigestOneListeners', callback); }; $rootScope.$postDigestAlways = function(callback) { addListener(this, '$$postDigestAlwaysListeners', callback); }; $rootScope.$digest = function() { var i, res, redigest = true; while (redigest) { redigest = false; loopListeners(this, '$$preDigestListeners', []); res = _digest.apply(this, arguments); loopListeners(this, '$$postDigestOneListeners', [requireRedigest], true); loopListeners(this, '$$postDigestAlwaysListeners', [requireRedigest]); } return res; function requireRedigest() { redigest = true; } }; return $rootScope; function addListener(self, property, listener) { var id, listeners; if (!self.hasOwnProperty(property)) { self[property] = []; } self[property].push(listener); } function loopListeners(self, property, args, clearAfterCalling) { var i, listeners; if (self.hasOwnProperty(property)) { listeners = self[property]; if (clearAfterCalling) { self[property] = []; } for (i=0; i 0) { // element contains pages. // create temporary pages for the non widget markup, that we destroy afterwards. // This is ok as non widget markup does not hold state, i.e. no permanent reference to the page. pages.page(); } else { element.parent().trigger("create"); } }); }); // Destroy the temporary pages again pages.page("destroy"); } } var emptyPage; function connectToDocumentAndPage(jqmNgWidget, node, callback) { if (!node.parentNode) { return callback(); } // search the top most element for node. while (node.parentNode && node.parentNode.nodeType === 1) { node = node.parentNode; } var oldParentNode = node.parentNode; if (oldParentNode !== document) { if (!emptyPage) { emptyPage = $('
'); createPagesWithoutPageCreateEvent(jqmNgWidget, emptyPage); } $("body").append(emptyPage); emptyPage.append(node); } try { return callback(); } finally { if (oldParentNode !== document) { if (oldParentNode) { oldParentNode.appendChild(node); } // Don't use remove, as this would destroy the page widget also, // but we want to cache it! emptyPage[0].parentNode.removeChild(emptyPage[0]); } } } /** * Special directive for pages, as they need an own scope. */ function ngmPageDirective(jqmNgWidget, $timeout) { return { restrict: 'A', scope: true, compile: function(tElement, tAttrs) { tElement.removeAttr("ngm-page"); return { pre: function(scope, iElement, iAttrs) { if (!$.mobile.pageContainer) { $.mobile.pageContainer = iElement.parent().addClass("ui-mobile-viewport"); } // Create the page widget without the pagecreate-Event. // This does no dom transformation, so it's safe to call this in the prelink function. createPagesWithoutPageCreateEvent(jqmNgWidget, iElement); lastCreatedPages.push(scope); } }; } }; } function createPagesWithoutPageCreateEvent(jqmNgWidget, pages) { jqmNgWidget.preventJqmWidgetCreation(function() { var oldPrefix = $.mobile.page.prototype.widgetEventPrefix; $.mobile.page.prototype.widgetEventPrefix = 'noop'; pages.page(); $.mobile.page.prototype.widgetEventPrefix = oldPrefix; }); } // If jqm loads a page from an external source, angular needs to compile it too! function initExternalJqmPagesOnLoad($rootScope, $compile, jqmNgWidget, $browser) { jqmNgWidget.patchJq('page', function() { if (!jqmNgWidget.preventJqmWidgetCreation() && !this.data($.mobile.page.prototype.widgetFullName)) { if (this.attr("data-" + $.mobile.ns + "external-page")) { correctRelativeLinks(this); $compile(this)($rootScope); } } return $.fn.orig.page.apply(this, arguments); }); function correctRelativeLinks(page) { // correct the relative links in this page relative // to the page url. // For external links, jqm already does this when // the page is loaded. However, normal links // are adjusted in jqm via their default jqm click handler. // As we use our own default click handler (see ngmRouting.js), // we need to adjust normal links ourselves. var pageUrl = page.jqmData("url"), pagePath = $.mobile.path.get(pageUrl), ABSOULTE_URL_RE = /^(\w+:|#|\/)/, EMPTY_RE = /^(\#|#|\/)/; page.find("a").each(function() { var $this = $(this), thisUrl = $this.attr("href"); if (thisUrl && thisUrl.length > 0 && !ABSOULTE_URL_RE.test(thisUrl)) { $this.attr("href", pagePath + thisUrl); } }); } } })($, angular); (function($, angular) { var ng = angular.module('ng'), execFlags = {}; $.fn.orig = {}; ng.provider("jqmNgWidget", ["$compileProvider", jqmNgWidgetProvider]); ng.run(["jqmNgWidget", function(jqmNgWidget) { jqmNgWidget._init(); }]); enableDomManipDelegate("after"); enableDomManipDelegate("before"); enableDomManipDelegate("css", function(attrName, value) { // if the element is shown/hidden, delegate this to the wrapper // (see ng-show). Only catch the setter! return attrName === 'display' && arguments.length >= 2; }); enableDomManipDelegate("remove"); return; // -------------- /** * @ngdoc object * @name ng.jqmNgWidgetProvider * * @description * Helper service for creating a custom directive for a jqm widget. * The provider contains a method for registering widgets, * and the service provides methods for refreshing the widget. */ function jqmNgWidgetProvider($compileProvider) { var widgetDefs = {}, widgetInstances = {}; var provider = { /** * @name ng.jgmNgWidgetProvider#widget * @methodOf ng.jgmNgWidgetProvider * * @description * Registers a directive for a jqm widget. * * @param {string} widgetName jqm widget name, e.g. 'dialog'. * @param {function()} directive injectable function, that returns an object with the following properties: *