source : PixLive.js

/*
 * angular-pixlive v1
 * (c) 2015-2016 Vidinoti https://vidinoti.com
 * License: MIT
 */
'use strict';
pixliveModule
    /**
     * @ngdoc directive
     * @name pxlView
     * @memberof pixlive
     * @param {service} $timeout Angular $timeout service
     * @param {service} $ionicPosition Ionic $ionicPosition service
     * @param {service} $ionicPlatform Ionic $ionicPlatform service
     * @param {service} $ionicBackdrop Ionic $ionicBackdrop service
     * @restrict E
     *
     * @description
     * Add an augmented reality view to your Ionic app.
     *
     * **Notice**: You should minimize the number of AR view included into your app to the minimum as this is CPU resource intensive. 
     * You should also avoid having two AR views visible at the same time as this will create unexpected behaviors.
     * 
     * **Warning**: This view has to be inside an `ion-view` element whose background has been set to transparent. Failing to do so will make the AR view invisible.
     * 
     * @example
     * <ion-view view-title="AR" style="background-color: transparent !important;">
     *  <pxl-view>
     *    <!-- Any overlay you want -->
     *  </pxl-view>
     * </ion-view>
     */
    .directive('pxlView', [
        '$timeout',
        '$ionicPosition',
        '$ionicPlatform',
        '$ionicBackdrop',
        function($timeout, $ionicPosition, $ionicPlatform, $ionicBackdrop) {
            return {
                restrict: 'E',
                require: '^?ionNavView',
                priority: 800,
                compile: function(element, attr) {
                    element.addClass('scroll-content ionic-scroll scroll-content-false');
                    function prelink($scope, $element, $attr, navViewCtrl) {
                        var parentScope = $scope.$parent;
                        $scope.$watch(function() {
                            return (parentScope.$hasHeader ? ' has-header' : '') +
                                (parentScope.$hasSubheader ? ' has-subheader' : '') +
                                (parentScope.$hasFooter ? ' has-footer' : '') +
                                (parentScope.$hasSubfooter ? ' has-subfooter' : '') +
                                (parentScope.$hasTabs ? ' has-tabs' : '') +
                                (parentScope.$hasTabsTop ? ' has-tabs-top' : '');
                        }, function(className, oldClassName) {
                            $element.removeClass(oldClassName);
                            $element.addClass(className);
                        });
                    }
                    function postlink($scope, $element, $attr, navViewCtrl) {
                        $scope.$on("$ionicView.beforeEnter", function(scopes, states) {
                            if ($scope.arView) {
                                $scope.arView.beforeEnter();
                            }
                        });
                        $scope.$on("$ionicView.afterEnter", function(scopes, states) {
                            if (!$scope.arView) {
                                $ionicPlatform.ready(function() {
                                    if (window.cordova && window.cordova.plugins && window.cordova.plugins.PixLive) {
                                        //FIXME: The timeout is a Dirty hack as on iOS, the status bar CSS style is applied after 
                                        //       this directive is loaded, hence we fail to get the proper Y value for the view.
                                        $scope.pixliveTimeout = $timeout(function() {
                                            var offset = $ionicPosition.offset($element);
                                            var y = offset.top;
                                            var x = offset.left;
                                            var width = offset.width;
                                            var height = offset.height;
                                            $scope.pixliveTimeout = null;
                                            $scope.arView = window.cordova.plugins.PixLive.createARView(x, y, width, height, true);
                                            if ($ionicBackdrop.isDisplayed() != false) {
                                                $scope.arView.disableTouch();
                                            } else {
                                                $scope.arView.enableTouch();
                                            }
                                            $scope.onResize = function() {
                                                var offset = $ionicPosition.offset($element);
                                                var y = offset.top;
                                                var x = offset.left;
                                                var width = offset.width;
                                                var height = offset.height;
                                                $scope.arView.resize(x, y, width, height);
                                            };
                                            $scope.onModalShown = function() {
                                                $scope.arView.disableTouch();
                                            };
                                            $scope.onModalHidden = function() {
                                                $scope.arView.enableTouch();
                                            };
                                            $scope.transferShown = function(){
                                                ionic.trigger('transfer.shown', {
                                                    target: window
                                                });
                                            };
                                            $scope.transferHidden = function(){
                                                ionic.trigger('transfer.hidden', {
                                                    target: window
                                                });
                                            };
                                            ionic.on('resize', $scope.onResize, window);
                                            ionic.on('backdrop.shown', $scope.onModalShown, window);
                                            ionic.on('backdrop.hidden', $scope.onModalHidden, window);
                                            $scope.$on('popover.shown', $scope.transferShown);
                                            $scope.$on('popover.hidden', $scope.transferHidden);
                                        }, 300);
                                    }
                                });
                            } else {
                                $scope.onResize();
                                $scope.arView.afterEnter();
                            }
                        });
                        $scope.$on("$ionicView.beforeLeave", function(scopes, states) {
                            if ($scope.pixliveTimeout) {
                                $timeout.cancel($scope.pixliveTimeout);
                                $scope.pixliveTimeout = null;
                            }
                            if ($scope.arView) {
                                $scope.arView.beforeLeave();
                            }
                        });
                        $scope.$on("$ionicView.afterLeave", function(scopes, states) {
                            if ($scope.arView) {
                                $scope.arView.afterLeave();
                            }
                        });
                        $scope.$on('$destroy', function() {
                            if ($scope.pixliveTimeout) {
                                $timeout.cancel($scope.pixliveTimeout);
                                $scope.pixliveTimeout = null;
                            }
                            if ($scope.arView) {
                                ionic.off('resize', $scope.onResize, window);
                                $scope.arView.destroy();
                            }
                            if ($scope.onModalShown) {
                                ionic.off('backdrop.shown', $scope.onModalShown, window);
                                $scope.onModalShown = null;
                            }
                            if ($scope.onModalHidden) {
                                ionic.off('backdrop.hidden', $scope.onModalHidden, window);
                                $scope.onModalHidden = null;
                            }
                        });
                    }
                    return {
                        pre: prelink,
                        post: postlink
                    };
                }
            };
        }
    ])
    .config(["$provide",
        function($provide) {
            // Use the `decorator` solution to substitute or attach behaviors to
            // original service instance; @see angular-mocks for more examples....
            $provide.decorator('$ionicBackdrop', ["$delegate",
                function($delegate) {
                    // Save the original $log.retain()
                    var retainFn = $delegate.retain;
                    var releaseFn = $delegate.release;
                    $delegate.backdropHolds = 0;
                    $delegate.addBackdropHolds = function(){
                        $delegate.backdropHolds++;
                        //Call the disable 
                        if ($delegate.backdropHolds == 1) {
                            ionic.trigger('backdrop.shown', {
                                target: window
                            });
                        }
                    };
                    $delegate.removeBackdropHolds = function(){ 
                        $delegate.backdropHolds--;
                        //Call the disable 
                        if ($delegate.backdropHolds == 0) {
                            ionic.trigger('backdrop.hidden', {
                                target: window
                            });
                        }
                    };
                    ionic.on('transfer.shown', $delegate.addBackdropHolds, window);
                    ionic.on('transfer.hidden', $delegate.removeBackdropHolds, window);
                    $delegate.retain = function() {
                        var args = [].slice.call(arguments);
                        // Call the original method
                        retainFn.apply(null, args)
                        $delegate.addBackdropHolds();
                    };
                    $delegate.release = function() {
                        var args = [].slice.call(arguments);
                        // Call the original method
                        releaseFn.apply(null, args)
                        $delegate.removeBackdropHolds();
                    };
                    $delegate.isDisplayed = function() {
                        return $delegate.backdropHolds>0;
                    };
                    return $delegate;
                }
            ]);
        }
    ]).config(["$provide",
        function($provide) {
            $provide.decorator('$ionicModal', ["$delegate","$q",
                function($delegate,$q) {
                    // Save the original $log.show()
                    var fromTemplate = $delegate.fromTemplate;
                    var fromTemplateUrl = $delegate.fromTemplateUrl;
                    var overrideShowHide = function (ret) {
                        // Save old methods
                        ret.showOld = ret.show;
                        ret.hideOld = ret.hide;
                        ret.show=function() {
                            ionic.trigger('transfer.shown', {
                                target: window
                            });
                            var args2 = [].slice.call(arguments);
                            return this.showOld.apply(this, args2);
                        };
                        ret.hide=function() {
                            ionic.trigger('transfer.hidden', {
                                target: window
                            });
                            var args2 = [].slice.call(arguments);
                            return this.hideOld.apply(this, args2);
                        };
                    };
                    $delegate.fromTemplate = function() {
                        var args = [].slice.call(arguments);
                        var ret = fromTemplate.apply(null, args);
                        overrideShowHide(ret);
                        return ret;
                    };
                    $delegate.fromTemplateUrl = function() {
                        var args = [].slice.call(arguments);
                        var deferred = $q.defer();
                        fromTemplateUrl.apply(null, args).then(function(modal) {
                            overrideShowHide(modal);
                            deferred.resolve(modal);
                        }, function(err) {
                            deferred.reject(err);
                        });
                        return deferred.promise;
                    };
                    return $delegate;
                }
            ]);
        }
    ]);