From cb600112c8a6f3a1e4e4d938a1b50d1d19a53a5f Mon Sep 17 00:00:00 2001
From: Thomas Steur <thomas.steur@googlemail.com>
Date: Fri, 14 Feb 2014 02:21:55 +0100
Subject: [PATCH] actually use the angular based site selector, some more
 improvements, bugfixes

---
 .../javascripts/jsTrackingGenerator.js        |  10 +-
 .../templates/trackingCodeGenerator.twig      |  20 +-
 plugins/CoreHome/CoreHome.php                 |   6 +-
 plugins/CoreHome/javascripts/autocomplete.js  | 243 ------------------
 .../CoreHome/javascripts/filters/filters.js   |   6 +
 plugins/CoreHome/javascripts/piwikApp.js      |   2 +-
 .../{dependencies => services}/PiwikApi.js    |   0
 .../CoreHome/javascripts/services/piwik.js    |  14 +
 .../javascripts/siteselector/controller.js    |  86 ++-----
 .../javascripts/siteselector/directives.js    |  63 +++--
 .../javascripts/siteselector/partial.html     |  40 ++-
 .../javascripts/siteselector/services.js      |  62 +++++
 plugins/CoreHome/templates/_siteSelect.twig   |   6 -
 .../CoreHome/templates/_siteSelectHeader.twig |   4 +-
 .../UsersManager/javascripts/usersManager.js  |   2 +-
 .../UsersManager/javascripts/usersSettings.js |   2 +-
 plugins/UsersManager/templates/index.twig     |  16 +-
 .../UsersManager/templates/userSettings.twig  |  16 +-
 18 files changed, 197 insertions(+), 401 deletions(-)
 delete mode 100644 plugins/CoreHome/javascripts/autocomplete.js
 rename plugins/CoreHome/javascripts/{dependencies => services}/PiwikApi.js (100%)
 create mode 100644 plugins/CoreHome/javascripts/services/piwik.js
 create mode 100644 plugins/CoreHome/javascripts/siteselector/services.js
 delete mode 100644 plugins/CoreHome/templates/_siteSelect.twig

diff --git a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js
index 352802783a..3a1d32e0dc 100644
--- a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js
+++ b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js
@@ -188,7 +188,7 @@
         var generateJsCode = function () {
             // get params used to generate JS code
             var params = {
-                idSite: $('#js-tracker-website').find('.custom_select_main_link').attr('data-siteid'),
+                idSite: $('#js-tracker-website').attr('siteid'),
                 groupPageTitlesByDomain: $('#javascript-tracking-group-by-domain').is(':checked'),
                 mergeSubdomains: $('#javascript-tracking-all-subdomains').is(':checked'),
                 mergeAliasUrls: $('#javascript-tracking-all-aliases').is(':checked'),
@@ -281,7 +281,7 @@
         var generateImageTrackerLink = function () {
             // get data used to generate the link
             var generateDataParams = {
-                idSite: $('#image-tracker-website').find('.custom_select_main_link').attr('data-siteid'),
+                idSite: $('#image-tracker-website').attr('siteid'),
                 actionName: $('#image-tracker-action-name').val(),
                 piwikHost: piwikHost,
                 piwikPath: piwikPath
@@ -331,7 +331,7 @@
         };
 
         // on image link tracker site change, change available goals
-        $('#image-tracker-website').bind('piwik:siteSelected', function (e, site) {
+        $('#image-tracker-website').bind('change', function (e, site) {
             getSiteData(site.id, '#image-tracking-code-options', function () {
                 resetGoalSelectItems(site.id, 'image-tracker-goal');
                 generateImageTrackerLink();
@@ -339,7 +339,7 @@
         });
 
         // on js link tracker site change, change available goals
-        $('#js-tracker-website').bind('piwik:siteSelected', function (e, site) {
+        $('#js-tracker-website').bind('change', function (e, site) {
             $('.current-site-name', '#optional-js-tracking-options').each(function () {
                 $(this).text(site.name);
             });
@@ -400,7 +400,7 @@
 
         // initial generation
         getSiteData(
-            $('#js-tracker-website').find('.custom_select_main_link').attr('data-siteid'),
+            $('#js-tracker-website').attr('siteid'),
             '#js-code-options,#image-tracking-code-options',
             function () {
                 var imageTrackerSiteId = $('#image-tracker-website').find('.custom_select_main_link').attr('data-siteid');
diff --git a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig
index 8eaee0132b..d61e2b00ea 100644
--- a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig
+++ b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig
@@ -27,15 +27,13 @@
         {# website #}
         <label class="website-label"><strong>{{ 'General_Website'|translate }}</strong></label>
 
-        <div ng-controller="SiteSelectorController"
-             piwik-site-selector
+        <div piwik-site-selector
+             siteid="{{ idSite }}"
              sitename="{{ defaultReportSiteName }}"
-             selectedsiteid="{{ idSite }}"
              showallsitesitem="0"
              switchsiteonselect="0"
-             selectorid="js-tracker-website"
-             showselectedsite="1"
-             class="sites_autocomplete"></div>
+             id="js-tracker-website"
+             showselectedsite="1"></div>
 
         <br/><br/><br/>
     </div>
@@ -201,15 +199,13 @@
     <div>
         {# website #}
         <label class="website-label"><strong>{{ 'General_Website'|translate }}</strong></label>
-        <div ng-controller="SiteSelectorController"
-             piwik-site-selector
+        <div piwik-site-selector
+             siteid="{{ idSite }}"
              sitename="{{ defaultReportSiteName }}"
-             selectedsiteid="{{ idSite }}"
              showallsitesitem="0"
              switchsiteonselect="0"
-             selectorid="image-tracker-website"
-             showselectedsite="1"
-             class="sites_autocomplete"></div>
+             id="image-tracker-website"
+             showselectedsite="1"></div>
 
         <br/><br/><br/>
     </div>
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index a8959c8f95..eb04915474 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -69,6 +69,7 @@ class CoreHome extends \Piwik\Plugin
         $jsFiles[] = "libs/jquery/mwheelIntent.js";
         $jsFiles[] = "libs/javascript/sprintf.js";
         $jsFiles[] = "libs/angularjs/angular.js";
+        $jsFiles[] = "libs/angularjs/angular-sanitize.js";
         $jsFiles[] = "plugins/Zeitgeist/javascripts/piwikHelper.js";
         $jsFiles[] = "plugins/Zeitgeist/javascripts/ajaxHelper.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/require.js";
@@ -80,7 +81,6 @@ class CoreHome extends \Piwik\Plugin
         $jsFiles[] = "plugins/CoreHome/javascripts/menu.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/menu_init.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/calendar.js";
-        $jsFiles[] = "plugins/CoreHome/javascripts/autocomplete.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/sparkline.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/corehome.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/top_controls.js";
@@ -92,8 +92,10 @@ class CoreHome extends \Piwik\Plugin
         $jsFiles[] = "plugins/CoreHome/javascripts/notification_parser.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/piwikApp.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/filters/filters.js";
-        $jsFiles[] = "plugins/CoreHome/javascripts/dependencies/PiwikApi.js";
+        $jsFiles[] = "plugins/CoreHome/javascripts/services/PiwikApi.js";
+        $jsFiles[] = "plugins/CoreHome/javascripts/services/piwik.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/directives/directives.js";
+        $jsFiles[] = "plugins/CoreHome/javascripts/siteselector/services.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/siteselector/controller.js";
         $jsFiles[] = "plugins/CoreHome/javascripts/siteselector/directives.js";
     }
diff --git a/plugins/CoreHome/javascripts/autocomplete.js b/plugins/CoreHome/javascripts/autocomplete.js
deleted file mode 100644
index ca9f3b9587..0000000000
--- a/plugins/CoreHome/javascripts/autocomplete.js
+++ /dev/null
@@ -1,243 +0,0 @@
-/*!
- * Piwik - Web Analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-function switchSite(id, name, showAjaxLoading, idCanBeAll) {
-    var $mainLink = $('.custom_select_main_link').attr('data-loading', 1);
-
-    if (id == 'all'
-        && !idCanBeAll
-    ) {
-        broadcast.propagateNewPage('module=MultiSites&action=index');
-    }
-    else {
-        $('.sites_autocomplete input').val(id);
-        $mainLink
-            .find('span')
-            .text(name);
-        broadcast.propagateNewPage('segment=&idSite=' + id, showAjaxLoading);
-    }
-    return false;
-}
-
-$(function () {
-
-    var reset = function (selector) {
-        $('.websiteSearch', selector).val('');
-        $('.custom_select_ul_list', selector).show();
-        $(".siteSelect.ui-autocomplete,.reset", selector).hide();
-    };
-
-    // sets up every un-inited site selector widget
-    piwik.initSiteSelectors = function () {
-        function getUrlForWebsiteId(idSite) {
-            var idSiteParam = 'idSite=' + idSite;
-            var newParameters = 'segment=&' + idSiteParam;
-            var hash = broadcast.isHashExists() ? broadcast.getHashFromUrl() : "";
-            return piwikHelper.getCurrentQueryStringWithParametersModified(newParameters)
-                    + '#' + piwikHelper.getQueryStringWithParametersModified(hash.substring(1), newParameters);
-        }
-
-        $('.sites_autocomplete').each(function () {
-            var selector = $(this);
-
-            if (selector.attr('data-inited') == 1) {
-                return;
-            }
-
-            selector.attr('data-inited', 1);
-
-            var setSelectedWebsiteAndName = function (idSite, name) {
-                var mainLinkElem = $(".custom_select_main_link", selector),
-                    mainLinkSpan = $('span', mainLinkElem);
-
-                mainLinkElem.attr('data-siteid', idSite);
-                mainLinkSpan.text(name);
-            };
-
-            var websiteSearch = $('.websiteSearch', selector);
-
-            // when the search input is clicked, clear the input
-            websiteSearch.click(function () {
-                $(this).val('');
-            });
-
-            // when a key is released over the search input when empty, reset the selector
-            //
-            websiteSearch.keyup(function (e) {
-                if (e.keyCode == 27) {
-                    $('.custom_select_block', selector).removeClass('custom_select_block_show');
-                    return false;
-                }
-
-                if (!$(this).val()) {
-                    reset(selector);
-                }
-            });
-
-            // setup the autocompleter
-            websiteSearch.autocomplete({
-                minLength: 1,
-                source: '?module=SitesManager&action=getSitesForAutocompleter',
-                appendTo: $('.custom_select_container', selector),
-                select: function (event, ui) {
-                    event.preventDefault();
-                    if (ui.item.id > 0) {
-                        // autocomplete.js allows item names to be HTML, so we have to entity the site name in PHP.
-                        // to avoid double encoding, we decode before setting text.
-                        // note: use of $.html() would not be future-proof.
-                        ui.item.name = piwikHelper.htmlDecode(ui.item.name);
-
-                        // set attributes of selected site display (what shows in the box)
-                        $('.custom_select_main_link', selector)
-                            .attr('data-siteid', ui.item.id)
-                            .html($('<span/>').text(ui.item.name));
-
-                        // hide the dropdown
-                        $('.custom_select_block', selector).removeClass('custom_select_block_show');
-
-                        // fire the site selected event
-                        selector.trigger('piwik:siteSelected', ui.item);
-                    }
-                    else {
-                        reset(selector);
-                    }
-
-                    return false;
-                },
-                focus: function (event, ui) {
-                    $('.websiteSearch', selector).val(ui.item.name);
-                    return false;
-                },
-                search: function (event, ui) {
-                    $('.reset', selector).show();
-                    $('.custom_select_main_link', selector).attr('data-loading', 1);
-                },
-                open: function (event, ui) {
-                    var widthSitesSelection = +$('.custom_select_ul_list', selector).width();
-
-                    $('.custom_select_main_link', selector).attr('data-loading', 0);
-
-                    var maxSitenameWidth = $('.max_sitename_width', selector);
-                    if (widthSitesSelection > maxSitenameWidth.val()) {
-                        maxSitenameWidth.val(widthSitesSelection);
-                    }
-                    else {
-                        maxSitenameWidth = +maxSitenameWidth.val(); // convert to int
-                    }
-
-                    $('.custom_select_ul_list', selector).hide();
-
-                    // customize jquery-ui's autocomplete positioning
-                    var cssToRemove = {float: 'none', position: 'static'};
-                    $('.siteSelect.ui-autocomplete', selector)
-                        .show().width(widthSitesSelection).css(cssToRemove)
-                        .find('li,a').each(function () {
-                            $(this).css(cssToRemove);
-                        });
-
-                    $('.custom_select_block_show', selector).width(widthSitesSelection);
-                }
-            }).data("ui-autocomplete")._renderItem = function (ul, item) {
-                $(ul).addClass('siteSelect');
-                var linkUrl = getUrlForWebsiteId(item.id);
-                var link = $("<a></a>").html(item.label).attr('href', linkUrl),
-                    listItem = $('<li></li>');
-
-                listItem.data("item.ui-autocomplete", item)
-                    .append(link)
-                    .appendTo(ul);
-
-                return listItem;
-            };
-
-            // when the reset button is clicked, reset the site selector
-            $('.reset', selector).click(reset);
-
-            // when mouse button is released on body, check if it is not over the site selector, and if not
-            // close it
-            $('body').on('mouseup', function (e) {
-                var closestSelector = $(e.target).closest('.sites_autocomplete');
-                if (!closestSelector.length || !closestSelector.is(selector)) {
-                    if ($('.custom_select_block', selector).hasClass('custom_select_block_show')) {
-                        reset(selector);
-                        $('.custom_select_block', selector).removeClass('custom_select_block_show');
-                    }
-                }
-            });
-
-            // set event handling code for non-jquery-autocomplete parts of widget
-            if ($('li', selector).length > 1) {
-
-                // event handler for when site selector is clicked. shows dropdown w/ first X sites
-                $(".custom_select", selector).click(function(e) {
-                    if(!$(e.target).parents('.custom_select_block').length) {
-                        $(".custom_select_block", selector).toggleClass("custom_select_block_show");
-                        $(".websiteSearch", selector).val("").focus();
-                    }
-                    return false;
-                });
-
-                $('.custom_select_block', selector).on('mouseenter', function() {
-                    $('.custom_select_ul_list > li > a', selector).each(function() {
-                        var idSite = $(this).attr('data-siteid');
-                        var linkUrl = getUrlForWebsiteId(idSite);
-                        $(this).attr('href', linkUrl);
-                    });
-                });
-
-                // change selection. fire's site selector's on select event and modifies the attributes
-                // of the selected link
-                $('.custom_select_ul_list li a', selector).each(function() {
-                    $(this).click(function (e) {
-                        var idsite = $(this).attr('data-siteid'),
-                            name = $(this).text();
-
-                        setSelectedWebsiteAndName(idsite, name);
-
-                        selector.trigger('piwik:siteSelected', {id: idsite, name: name});
-
-                        // close the dropdown
-                        $(".custom_select_block", selector).removeClass("custom_select_block_show");
-
-                        e.preventDefault();
-                    });
-                });
-            }
-
-            // handle multi-sites link click (triggers site selected event w/ id=all)
-            $('.custom_select_all', selector).click(function () {
-
-                setSelectedWebsiteAndName('all', $(this).text());
-
-                $(".custom_select_block", selector).toggleClass("custom_select_block_show");
-
-                selector.trigger('piwik:siteSelected', {id: 'all', name: $('.custom_select_all>a', selector).text()});
-            });
-
-            // handle submit button click
-            $('.but', selector).on('click', function (e) {
-                if (websiteSearch.val() != '') {
-                    websiteSearch.autocomplete('search', websiteSearch.val() + '%%%');
-                }
-                return false;
-            });
-
-            // if the data-switch-site-on-select attribute is set to 1 on the selector, set
-            // a default handler for piwik:siteSelected that switches the current site
-            // otherwise only update the input
-            selector.bind('piwik:siteSelected', function (e, site) {
-                if (1 == $(this).attr('data-switch-site-on-select')) {
-                    if (piwik.idSite !== site.id) {
-                        switchSite(site.id, site.name);
-                    }
-                } else {
-                    $('input', this).val(site.id);
-                }
-            });
-        });
-    };
-});
diff --git a/plugins/CoreHome/javascripts/filters/filters.js b/plugins/CoreHome/javascripts/filters/filters.js
index b5d8cb327b..f4ebc26774 100644
--- a/plugins/CoreHome/javascripts/filters/filters.js
+++ b/plugins/CoreHome/javascripts/filters/filters.js
@@ -10,3 +10,9 @@ piwikApp.filter('translate', function() {
         return _pk_translate(key);
     }
 });
+
+piwikApp.filter('htmldecode', function() {
+    return function(theString) {
+        return piwikHelper.htmlDecode(theString);
+    }
+});
diff --git a/plugins/CoreHome/javascripts/piwikApp.js b/plugins/CoreHome/javascripts/piwikApp.js
index 36dc2c55c4..57376fb346 100644
--- a/plugins/CoreHome/javascripts/piwikApp.js
+++ b/plugins/CoreHome/javascripts/piwikApp.js
@@ -1,2 +1,2 @@
-var piwikApp  = angular.module('piwikApp', []);
+var piwikApp  = angular.module('piwikApp', ['ngSanitize']);
 var customApp = angular.module('app', []);
diff --git a/plugins/CoreHome/javascripts/dependencies/PiwikApi.js b/plugins/CoreHome/javascripts/services/PiwikApi.js
similarity index 100%
rename from plugins/CoreHome/javascripts/dependencies/PiwikApi.js
rename to plugins/CoreHome/javascripts/services/PiwikApi.js
diff --git a/plugins/CoreHome/javascripts/services/piwik.js b/plugins/CoreHome/javascripts/services/piwik.js
new file mode 100644
index 0000000000..a1b84be81b
--- /dev/null
+++ b/plugins/CoreHome/javascripts/services/piwik.js
@@ -0,0 +1,14 @@
+/*!
+ * Piwik - Web Analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+
+piwikApp.service('piwik', function () {
+
+    piwik.helper    = piwikHelper;
+    piwik.broadcast = broadcast;
+    return piwik;
+});
diff --git a/plugins/CoreHome/javascripts/siteselector/controller.js b/plugins/CoreHome/javascripts/siteselector/controller.js
index 9c3e2c9daf..2998bc2f92 100644
--- a/plugins/CoreHome/javascripts/siteselector/controller.js
+++ b/plugins/CoreHome/javascripts/siteselector/controller.js
@@ -1,81 +1,37 @@
-/*!
- * Piwik - Web Analytics
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
+piwikApp.controller('SiteSelectorController', ['$scope', 'siteSelectorModel', 'piwik', function($scope, siteSelectorModel, piwik){
 
-piwikApp.controller('SiteSelectorController', ['$scope', 'piwikApi', function($scope, piwikApi){
-    var filterLimit = 10;
+    $scope.model = siteSelectorModel;
+    $scope.selectedSite = {id: '', name: ''};
+    $scope.activeSiteId = piwik.idSite;
 
-    $scope.sites = [];
-    $scope.hasMultipleWebsites = false;
-    $scope.isLoading = false;
+    $scope.model.loadInitialSites();
 
     $scope.switchSite = function (site) {
         if (!$scope.switchSiteOnSelect || piwik.idSite == site.idsite) {
-            $scope.selector.selectedSiteId = site.idsite;
-            $scope.siteName = site.name;
+            $scope.selectedSite.id   = site.idsite;
+            $scope.selectedSite.name = site.name;
             return;
         }
 
-        if (site.idsite == 'all' && !$scope.showAllSitesItem) {
-            broadcast.propagateNewPage('module=MultiSites&action=index');
+        if (site.idsite == 'all') {
+            piwik.broadcast.propagateNewPage('module=MultiSites&action=index');
         } else {
-            broadcast.propagateNewPage($scope.getUrlForWebsiteId(site.idsite), false);
+            piwik.broadcast.propagateNewPage('segment=&idSite=' + site.idsite, false);
         }
     };
 
-    $scope.getUrlForWebsiteId = function (idSite) {
+    function getUrlForWebsiteId (idSite) {
         var idSiteParam   = 'idSite=' + idSite;
         var newParameters = 'segment=&' + idSiteParam;
-        var hash = broadcast.isHashExists() ? broadcast.getHashFromUrl() : "";
-        return piwikHelper.getCurrentQueryStringWithParametersModified(newParameters)
-            + '#' + piwikHelper.getQueryStringWithParametersModified(hash.substring(1), newParameters);
-    };
-
-    $scope.updateWebsitesList = function (websites) {
-        angular.forEach(websites, function (website) {
-            website.name = piwikHelper.htmlDecode(website.name);
-        });
-
-        $scope.sites = websites;
-
-        if (!$scope.siteName) {
-            $scope.siteName = websites[0].name;
-        }
-
-        $scope.hasMultipleWebsites = websites.length > 1;
+        var hash = piwik.broadcast.isHashExists() ? piwik.broadcast.getHashFromUrl() : "";
+        return piwik.helper.getCurrentQueryStringWithParametersModified(newParameters)
+            + '#' + piwik.helper.getQueryStringWithParametersModified(hash.substring(1), newParameters);
     };
+}]);
 
-    $scope.searchSite = function (term) {
-        if (!term) {
-            $scope.loadInitialSites();
-            return;
-        }
-        $scope.isLoading = true;
-        piwikApi.fetch({
-            method: 'SitesManager.getPatternMatchSites',
-            filter_limit: filterLimit,
-            pattern: term
-        }).then(function (response) {
-            $scope.updateWebsitesList(response);
-        }).finally(function () {
-            $scope.isLoading = false;
-        });
-    };
-
-    $scope.loadInitialSites = function () {
-        $scope.isLoading = true;
-        piwikApi.fetch({
-            method: 'SitesManager.getSitesWithAtLeastViewAccess',
-            filter_limit: filterLimit,
-            showColumns: 'name,idsite'
-        }).then(function (response) {
-            $scope.updateWebsitesList(response);
-        }).finally(function () {
-            $scope.isLoading = false;
-        });
-    }
-
-}]);
\ No newline at end of file
+/*!
+ * Piwik - Web Analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
diff --git a/plugins/CoreHome/javascripts/siteselector/directives.js b/plugins/CoreHome/javascripts/siteselector/directives.js
index bbe004ec30..0c60ed43d4 100644
--- a/plugins/CoreHome/javascripts/siteselector/directives.js
+++ b/plugins/CoreHome/javascripts/siteselector/directives.js
@@ -5,33 +5,50 @@
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  */
 
-piwikApp.directive('piwikSiteSelector', function($document){
+piwikApp.directive('piwikSiteSelector', function($document, piwik, $filter){
+
+    function getBool(attr, property, defaultValue)
+    {
+        if (angular.isDefined(attr[property])) {
+            return !!parseInt(attr[property]);
+        }
+
+        return defaultValue;
+    }
+
     return {
         restrict: 'A',
-        link: function(scope, element, attr, ctrl) {
-            // TODO pass Piwik as a depedency
-
-            // why not directly use camel case and for example site-name? If I remember correct this does not work
-            // in all of or required IE versions. Alternative can be to define a namespace attribute and prefix all
-            // attributes with piwik, eg. piwik-site-name. Again: Not sure if I remember correct, will have a look
-            // later
+        scope: true,
+        templateUrl: 'plugins/CoreHome/javascripts/siteselector/partial.html',
+        controller: 'SiteSelectorController',
+        compile: function (element, attrs) {
+            element.addClass('sites_autocomplete');
 
-            scope.allWebsitesLinkLocation = attr.allwebsiteslocation || 'bottom';
-            scope.showAutocomplete = attr.showautocomplete || true;
-            scope.siteName  = attr.sitename || piwik.siteName;
-            scope.selector.selectedSiteId = attr.selectedsiteid || piwik.idSite;
-            scope.inputName = attr.inputname || '';
-            scope.showSelectedSite = attr.showselectedsite || false;
-            scope.selectorId = attr.selectorid || false;
-            scope.switchSiteOnSelect = attr.switchsiteonselect || true;
-            scope.showAllSitesItem = attr.showallsitesitem || true;
+            return function (scope, element, attr, ctrl) {
 
-            /*
-             $scope.max_sitename_width = 130; // can be removed?
-             */
+                // why not directly use camel case and for example site-name? If I remember correct this does not work
+                // in all of or required IE versions. Alternative can be to define a namespace attribute and prefix all
+                // attributes with piwik, eg. piwik-site-name. Again: Not sure if I remember correct, will have a look
+                // later
+                scope.allSitesLocation = attr.allsiteslocation || 'bottom';
+                scope.allSitesText = attr.allsitestext || $filter('translate')('General_MultiSitesSummary');
+                scope.selectedSite = {id: attr.siteid || piwik.idSite, name: attr.sitename || ''};
+                scope.inputName    = attr.inputname || '';
+                scope.showAutocomplete   = getBool(attr, 'showautocomplete', true);
+                scope.showSelectedSite   = getBool(attr, 'showselectedsite', false);
+                scope.switchSiteOnSelect = getBool(attr, 'switchsiteonselect', true);
+                scope.showAllSitesItem   = getBool(attr, 'showallsitesitem', true);
+                /*
+                 $scope.max_sitename_width = 130; // can be removed?
+                 */
 
-        },
-        // scope: {showAutoComplete: '=showautocomplete', siteName: '=sitename'},
-        templateUrl: 'plugins/CoreHome/javascripts/siteselector/partial.html'
+                scope.$watch('selectedSite.id', function (newValue, oldValue, scope) {
+                    if (newValue != oldValue) {
+                        element.attr('siteid', newValue);
+                        element.trigger('change', scope.selectedSite);
+                    }
+                });
+            }
+        }
     }
 });
\ No newline at end of file
diff --git a/plugins/CoreHome/javascripts/siteselector/partial.html b/plugins/CoreHome/javascripts/siteselector/partial.html
index 6f121f052d..188b60785c 100644
--- a/plugins/CoreHome/javascripts/siteselector/partial.html
+++ b/plugins/CoreHome/javascripts/siteselector/partial.html
@@ -1,46 +1,40 @@
-<div ng-class="{'sites_autocomplete--dropdown': (hasMultipleWebsites || showAllSitesItem)}"
-     id="{{ selectorId }}">
-    <div piwik-focus-anywhere-but-here="selector.showSitesList=false" class="custom_select">
+<div ng-class="{'sites_autocomplete--dropdown': (model.hasMultipleWebsites || showAllSitesItem)}">
+    <div piwik-focus-anywhere-but-here="view.showSitesList=false" class="custom_select">
 
-        <input ng-if="inputName" ng-model="selector.selectedSiteId" type="hidden" name="{{ inputName }}" value=""/>
+        <input ng-if="inputName" type="hidden" name="{{ inputName }}" value="{{ selectedSite.id }}"/>
 
-        <a ng-click="selector.showSitesList=!selector.showSitesList; selector.showSitesList && loadInitialSites()" href="javascript:void(0)"
-           class="custom_select_main_link" ng-class="{'loading': isLoading}"
-           data-siteid="{{ selectedSiteId }}">
-            <span ng-bind="siteName"></span>
+        <a ng-click="view.showSitesList=!view.showSitesList; view.showSitesList && model.loadInitialSites()" href="javascript:void(0)"
+           class="custom_select_main_link" ng-class="{'loading': model.isLoading}">
+            <span ng-bind="selectedSite.name || model.firstSiteName"></span>
         </a>
 
-        <div ng-show="selector.showSitesList" class="custom_select_block">
+        <div ng-show="view.showSitesList" class="custom_select_block">
 
-            <div ng-click="siteName='Ddd'" ng-show="allWebsitesLinkLocation=='top' && showAllSitesItem" class="custom_select_all" style="clear: both;">
-                <a href="#">
-                    {{ allSitesItemText || 'General_MultiSitesSummary'|translate }}
-                </a>
+            <div ng-click="switchSite({idsite: 'all', name: allSitesText})" ng-if="allSitesLocation=='top' && showAllSitesItem" class="custom_select_all">
+                <a href="#" ng-bind-html="allSitesText"></a>
             </div>
 
             <div class="custom_select_container">
                 <ul class="custom_select_ul_list">
-                    <li ng-focus="selector.searchTerm=site.name" ng-click="selector.showSitesList=false; switchSite(site)" ng-repeat="site in sites" ng-hide="!showSelectedSite && piwik.idSite==site.idsite">
-                        <a href="javascript:void(0)" piwik-autocomplete-matched="selector.searchTerm">{{ site.name }}</a>
+                    <li ng-click="view.showSitesList=false; switchSite(site)" ng-repeat="site in model.sites" ng-hide="!showSelectedSite && activeSiteId==site.idsite">
+                        <a href="javascript:void(0)" piwik-autocomplete-matched="view.searchTerm">{{ site.name }}</a>
                     </li>
                 </ul>
-                <ul ng-show="!sites.length" class="ui-autocomplete ui-front ui-menu ui-widget ui-widget-content ui-corner-all siteSelect">
+                <ul ng-show="!model.sites.length" class="ui-autocomplete ui-front ui-menu ui-widget ui-widget-content ui-corner-all siteSelect">
                     <li class="ui-menu-item" style="float:none;position:static">
-                        <a class="ui-corner-all" style="float:none;position:static" tabindex="-1">{{ ('SitesManager_NotFound' | translate) + ' ' + selector.searchTerm }}</a>
+                        <a class="ui-corner-all" style="float:none;position:static" tabindex="-1">{{ ('SitesManager_NotFound' | translate) + ' ' + view.searchTerm }}</a>
                     </li>
                 </ul>
             </div>
 
-            <div ng-click="siteName='Ddd'" ng-show="allWebsitesLinkLocation=='bottom' && showAllSitesItem" class="custom_select_all" style="clear: both;">
-                <a href="#">
-                    {{ allSitesItemText || 'General_MultiSitesSummary'|translate }}
-                </a>
+            <div ng-click="switchSite({idsite: 'all', name: allSitesText})" ng-if="allSitesLocation=='bottom' && showAllSitesItem" class="custom_select_all">
+                <a href="javascript:void(0)" ng-bind-html="allSitesText"></a>
             </div>
 
             <div class="custom_select_search" ng-show="showAutocomplete">
-                <input type="text" ng-focus="selector.showSitesList" ng-click="selector.searchTerm=''" ng-model="selector.searchTerm" ng-change="searchSite(selector.searchTerm)" length="15" class="websiteSearch inp"/>
+                <input type="text" ng-click="view.searchTerm=''" ng-model="view.searchTerm" ng-change="model.searchSite(view.searchTerm)" length="15" class="websiteSearch inp"/>
                 <input type="submit" value="Search" class="but"/>
-                <img title="Clear" ng-show="selector.searchTerm" ng-click="selector.searchTerm=''; loadInitialSites()" class="reset" style="position: relative; top: 4px; left: -44px; cursor: pointer;"
+                <img title="Clear" ng-show="view.searchTerm" ng-click="view.searchTerm=''; model.loadInitialSites()" class="reset" style="position: relative; top: 4px; left: -44px; cursor: pointer;"
                      src="plugins/CoreHome/images/reset_search.png"/>
             </div>
         </div>
diff --git a/plugins/CoreHome/javascripts/siteselector/services.js b/plugins/CoreHome/javascripts/siteselector/services.js
new file mode 100644
index 0000000000..f936112c1d
--- /dev/null
+++ b/plugins/CoreHome/javascripts/siteselector/services.js
@@ -0,0 +1,62 @@
+
+piwikApp.factory('siteSelectorModel', function (piwikApi, $filter) {
+    var filterLimit = 10;
+
+    var model = {};
+    model.sites = [];
+    model.hasMultipleWebsites = false;
+    model.isLoading = false;
+    model.firstSiteName = '';
+
+    model.updateWebsitesList = function (websites) {
+
+        if (!websites || !websites.length) {
+            model.sites = [];
+            return;
+        }
+
+        angular.forEach(websites, function (website) {
+            website.name = $filter('htmldecode')(website.name);
+        });
+
+        model.sites = websites;
+
+        if (!model.firstSiteName) {
+            model.firstSiteName = websites[0].name;
+        }
+
+        model.hasMultipleWebsites = model.hasMultipleWebsites || websites.length > 1;
+    };
+
+    model.searchSite = function (term) {
+        if (!term) {
+            model.loadInitialSites();
+            return;
+        }
+        model.isLoading = true;
+        piwikApi.fetch({
+            method: 'SitesManager.getPatternMatchSites',
+            filter_limit: filterLimit,
+            pattern: term
+        }).then(function (response) {
+            model.updateWebsitesList(response);
+        }).finally(function () {
+            model.isLoading = false;
+        });
+    };
+
+    model.loadInitialSites = function () {
+        model.isLoading = true;
+        piwikApi.fetch({
+            method: 'SitesManager.getSitesWithAtLeastViewAccess',
+            filter_limit: filterLimit,
+            showColumns: 'name,idsite'
+        }).then(function (response) {
+            model.updateWebsitesList(response);
+        }).finally(function () {
+            model.isLoading = false;
+        });
+    }
+
+    return model;
+});
diff --git a/plugins/CoreHome/templates/_siteSelect.twig b/plugins/CoreHome/templates/_siteSelect.twig
deleted file mode 100644
index 63bf632f04..0000000000
--- a/plugins/CoreHome/templates/_siteSelect.twig
+++ /dev/null
@@ -1,6 +0,0 @@
-<div ng-controller="SiteSelectorController"
-     piwik-site-selector
-     showautocomplete="false"
-     sitename="test"
-     inputname="test"
-     class="sites_autocomplete"></div>
diff --git a/plugins/CoreHome/templates/_siteSelectHeader.twig b/plugins/CoreHome/templates/_siteSelectHeader.twig
index ba643376a3..b0ea798533 100644
--- a/plugins/CoreHome/templates/_siteSelectHeader.twig
+++ b/plugins/CoreHome/templates/_siteSelectHeader.twig
@@ -1,7 +1,5 @@
 <div class="top_bar_sites_selector {% if currentModule == 'CoreHome' %}sites_selector_in_dashboard{% endif %}">
     <label>{{ 'General_Website'|translate }}</label>
-    <div ng-controller="SiteSelectorController"
-         piwik-site-selector
-         class="sites_autocomplete"></div>
+    <div piwik-site-selector></div>
 
 </div>
\ No newline at end of file
diff --git a/plugins/UsersManager/javascripts/usersManager.js b/plugins/UsersManager/javascripts/usersManager.js
index 19a7817565..229753b47b 100644
--- a/plugins/UsersManager/javascripts/usersManager.js
+++ b/plugins/UsersManager/javascripts/usersManager.js
@@ -293,7 +293,7 @@ $(document).ready(function () {
     $('#superUserAccess .accessGranted, #superUserAccess .updateAccess').click(bindUpdateSuperUserAccess);
 
     // when a site is selected, reload the page w/o showing the ajax loading element
-    $('#usersManagerSiteSelect').bind('piwik:siteSelected', function (e, site) {
+    $('#usersManagerSiteSelect').bind('change', function (e, site) {
         if (site.id != piwik.idSite) {
             switchSite(
                 site.id,
diff --git a/plugins/UsersManager/javascripts/usersSettings.js b/plugins/UsersManager/javascripts/usersSettings.js
index a26652255d..d50f0321b0 100644
--- a/plugins/UsersManager/javascripts/usersSettings.js
+++ b/plugins/UsersManager/javascripts/usersSettings.js
@@ -24,7 +24,7 @@ function sendUserSettingsAJAX() {
     var defaultReport = $('input[name=defaultReport]:checked').val();
 
     if (defaultReport == 1) {
-        defaultReport = $('#userSettingsTable').find('.custom_select_main_link').attr('data-siteid');
+        defaultReport = $('#defaultReportSiteSelector').attr('siteid');
     }
     var postParams = {};
     postParams.alias = alias;
diff --git a/plugins/UsersManager/templates/index.twig b/plugins/UsersManager/templates/index.twig
index cfb1d76510..734aabbf23 100644
--- a/plugins/UsersManager/templates/index.twig
+++ b/plugins/UsersManager/templates/index.twig
@@ -12,14 +12,14 @@
         {% set applyAllSitesText %}
             <strong>{{ 'UsersManager_ApplyToAllWebsites'|translate }}</strong>
         {% endset %}
-        {% include "@CoreHome/_siteSelect.twig" with {
-            'siteName': defaultReportSiteName,
-            'idSite':idSiteSelected,
-            'allSitesItemText': applyAllSitesText,
-            'allWebsitesLinkLocation': 'top',
-            'siteSelectorId':"usersManagerSiteSelect",
-            'switchSiteOnSelect':false
-        } %}
+
+        <div piwik-site-selector
+             siteid="{{ idSiteSelected }}"
+             sitename="{{ defaultReportSiteName }}"
+             allsitestext="{{ applyAllSitesText|raw }}"
+             allsiteslocation="top"
+             id="usersManagerSiteSelect"
+             switchsiteonselect="0"></div>
     </section>
 </div>
 
diff --git a/plugins/UsersManager/templates/userSettings.twig b/plugins/UsersManager/templates/userSettings.twig
index aa64d90ccb..4fe5b9e7dd 100644
--- a/plugins/UsersManager/templates/userSettings.twig
+++ b/plugins/UsersManager/templates/userSettings.twig
@@ -43,14 +43,14 @@
                 {% else %}
                     {% set defaultReportIdSite=defaultReport %}
                 {% endif %}
-                {% include "@CoreHome/_siteSelect.twig" with {
-                    'siteName':defaultReportSiteName,
-                    'idSite':defaultReportIdSite,
-                    'switchSiteOnSelect':false,
-                    'showAllSitesItem':false,
-                    'showSelectedSite':true,
-                    'siteSelectorId': 'defaultReportSiteSelector'
-                } %}
+
+                <div piwik-site-selector
+                     siteid="{{ defaultReportIdSite }}"
+                     sitename="{{ defaultReportSiteName }}"
+                     switchsiteonselect="0"
+                     showallsitesitem="0"
+                     showselectedsite="1"
+                     id="defaultReportSiteSelector"></div>
             </fieldset>
         </td>
     </tr>
-- 
GitLab