diff options
author | Randy Bertram <rbertram@us.ibm.com> | 2015-02-18 08:29:16 -0500 |
---|---|---|
committer | Randy Bertram <rbertram@us.ibm.com> | 2015-02-20 09:50:47 -0500 |
commit | 1e15e5bbb55b838bc4289ebc5c39d9c4dd9cb1e4 (patch) | |
tree | 5084c8f6a6fdd76c4ebc8345389b3849972e39e8 | |
parent | c5a716cb22ca9b4ef75a91ac6bd43fa582a42f69 (diff) | |
download | xstatic-magic-search-1e15e5bbb55b838bc4289ebc5c39d9c4dd9cb1e4.tar.gz |
Initializing with MagicSearch 0.1.5.9
First commit of MagicSearch code
Change-Id: Idc51127a72a0a80aab706690db26387cc9f72b81
-rw-r--r-- | .gitignore | 9 | ||||
-rwxr-xr-x | MANIFEST.in | 8 | ||||
-rwxr-xr-x | README.txt | 21 | ||||
-rwxr-xr-x | setup.py | 27 | ||||
-rwxr-xr-x | xstatic/__init__.py | 1 | ||||
-rwxr-xr-x | xstatic/pkg/__init__.py | 1 | ||||
-rwxr-xr-x | xstatic/pkg/magic_search/__init__.py | 49 | ||||
-rwxr-xr-x | xstatic/pkg/magic_search/data/magic_search.css | 85 | ||||
-rwxr-xr-x | xstatic/pkg/magic_search/data/magic_search.html | 49 | ||||
-rwxr-xr-x | xstatic/pkg/magic_search/data/magic_search.js | 334 | ||||
-rwxr-xr-x | xstatic/pkg/magic_search/data/magic_search.scss | 87 | ||||
-rwxr-xr-x | xstatic/pkg/magic_search/data/magic_search_bootstrap.html | 50 | ||||
-rwxr-xr-x | xstatic/pkg/magic_search/data/magic_search_bootstrap.js | 28 |
13 files changed, 749 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b3085b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.pyc +*.sw? +*.sqlite3 +.DS_STORE +*.egg-info +.venv +.tox +build +dist diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100755 index 0000000..1ce94ae --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,8 @@ +include README.txt +recursive-include xstatic/pkg/magic_search * + +global-exclude *.pyc +global-exclude *.pyo +global-exclude *.orig +global-exclude *.rej + diff --git a/README.txt b/README.txt new file mode 100755 index 0000000..24bf752 --- /dev/null +++ b/README.txt @@ -0,0 +1,21 @@ +XStatic-MagicSearch +------------------- + +MagicSearch is an AngularJS directive that provides a UI for both faceted +filtering and as-you-type filtering. It is intended for filtering tables, +such as an AngularJS smart-table, but it can be used in any situation +where you can provide it with facets/options and consume its events. + +MagicSearch was initially developed by David Kavanagh for Eucalyptus. + + +MagicSearch javascript library packaged for setuptools (easy_install) / pip. + +This package is intended to be used by **any** project that needs these files. + +It intentionally does **not** provide any extra code except some metadata +**nor** has any extra requirements. You MAY use some minimal support code from +the XStatic base package, if you like. + +You can find more info about the xstatic packaging way in the package `XStatic`. + diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..b8765cd --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +from xstatic.pkg import magic_search as xs + +# The README.txt file should be written in reST so that PyPI can use +# it to generate your project's PyPI page. +long_description = open('README.txt').read() + +from setuptools import setup, find_packages + +setup( + name=xs.PACKAGE_NAME, + version=xs.PACKAGE_VERSION, + description=xs.DESCRIPTION, + long_description=long_description, + classifiers=xs.CLASSIFIERS, + keywords=xs.KEYWORDS, + maintainer=xs.MAINTAINER, + maintainer_email=xs.MAINTAINER_EMAIL, + license=xs.LICENSE, + url=xs.HOMEPAGE, + platforms=xs.PLATFORMS, + packages=find_packages(), + namespace_packages=['xstatic', 'xstatic.pkg', ], + include_package_data=True, + zip_safe=False, + install_requires=[], # nothing! :) + # if you like, you MAY use the 'XStatic' package. +) diff --git a/xstatic/__init__.py b/xstatic/__init__.py new file mode 100755 index 0000000..de40ea7 --- /dev/null +++ b/xstatic/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/xstatic/pkg/__init__.py b/xstatic/pkg/__init__.py new file mode 100755 index 0000000..de40ea7 --- /dev/null +++ b/xstatic/pkg/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/xstatic/pkg/magic_search/__init__.py b/xstatic/pkg/magic_search/__init__.py new file mode 100755 index 0000000..9d03d34 --- /dev/null +++ b/xstatic/pkg/magic_search/__init__.py @@ -0,0 +1,49 @@ +""" +XStatic resource package + +See package 'XStatic' for documentation and basic tools. +""" + +DISPLAY_NAME = 'Magic-Search' # official name, upper/lowercase allowed, no spaces +PACKAGE_NAME = 'XStatic-%s' % DISPLAY_NAME # name used for PyPi + +NAME = __name__.split('.')[-1] # package name (e.g. 'foo' or 'foo_bar') + # please use a all-lowercase valid python + # package name + +VERSION = '0.1.5' # version of the packaged files, please use the upstream + # version number +BUILD = '9' # our package build number, so we can release new builds + # with fixes for xstatic stuff. +PACKAGE_VERSION = VERSION + '.' + BUILD # version used for PyPi + +DESCRIPTION = "%s %s (XStatic packaging standard)" % (DISPLAY_NAME, VERSION) + +PLATFORMS = 'any' +CLASSIFIERS = [] +KEYWORDS = '%s xstatic' % NAME + +# XStatic-* package maintainer: +MAINTAINER = 'Randy Bertram' +MAINTAINER_EMAIL = 'rbertram@us.ibm.com' + +# this refers to the project homepage of the stuff we packaged: +HOMEPAGE = 'https://github.com/eucalyptus/magic-search' + +# this refers to all files: +LICENSE = '(same as %s)' % DISPLAY_NAME + +from os.path import join, dirname +BASE_DIR = join(dirname(__file__), 'data') +# linux package maintainers just can point to their file locations like this: +#BASE_DIR = '/usr/share/javascript/jquery' + +LOCATIONS = { + # CDN locations (if no public CDN exists, use an empty dict) + # if value is a string, it is a base location, just append relative + # path/filename. if value is a dict, do another lookup using the + # relative path/filename you want. + # your relative path/filenames should usually be without version + # information, because either the base dir/url is exactly for this + # version or the mapping will care for accessing this version. +} diff --git a/xstatic/pkg/magic_search/data/magic_search.css b/xstatic/pkg/magic_search/data/magic_search.css new file mode 100755 index 0000000..ef63fea --- /dev/null +++ b/xstatic/pkg/magic_search/data/magic_search.css @@ -0,0 +1,85 @@ +/* Copyright 2014-2015 Eucalyptus Systems, Inc. */ +/*----------------------------------------- + Colors + ----------------------------------------- */ +/*----------------------------------------- + Item list + ----------------------------------------- */ +/*----------------------------------------- + Magic Search bar + ----------------------------------------- */ +/* line 30, ../src/magic_search.scss */ +.search-bar { + position: relative; + border: 1px solid black; + background-color: white; + margin-bottom: 0.5rem; + padding: 4px; + height: auto; +} +/* line 37, ../src/magic_search.scss */ +.search-bar i.fi-filter { + color: #444444; + position: absolute; + top: 0.5rem; + left: 0.65rem; +} +/* line 46, ../src/magic_search.scss */ +.search-bar #search-main-area { + position: relative; + margin-left: 1.65rem; + margin-right: 1.65rem; + cursor: text; +} +/* line 14, ../src/magic_search.scss */ +.search-bar .item-list { + margin-bottom: 6px; +} +/* line 16, ../src/magic_search.scss */ +.search-bar .item-list .item { + color: #333; + background-color: #e6e7e8; + margin-right: 8px; +} +/* line 20, ../src/magic_search.scss */ +.search-bar .item-list .item a { + color: white; +} +/* line 53, ../src/magic_search.scss */ +.search-bar .item-list { + margin-bottom: 2px; +} +/* line 56, ../src/magic_search.scss */ +.search-bar #search-selected { + background-color: white; + color: #444444; +} +/* line 60, ../src/magic_search.scss */ +.search-bar #search-entry { + display: inline-block; + height: 1.5rem; +} +/* line 64, ../src/magic_search.scss */ +.search-bar #search-input { + width: 220px; + border: 0; + box-shadow: none; + height: 1.5rem; + padding: 3px; + background-color: white; +} +/* line 75, ../src/magic_search.scss */ +.search-bar .match { + font-weight: bold; +} +/* line 78, ../src/magic_search.scss */ +.search-bar i.cancel { + color: #444444; + position: absolute; + top: 0.5rem; + right: 0.65rem; +} +/* line 80, ../src/magic_search.scss */ +.search-bar i.cancel:hover { + color: darkred; +} diff --git a/xstatic/pkg/magic_search/data/magic_search.html b/xstatic/pkg/magic_search/data/magic_search.html new file mode 100755 index 0000000..e32e795 --- /dev/null +++ b/xstatic/pkg/magic_search/data/magic_search.html @@ -0,0 +1,49 @@ +<!--! Magic Searchbar --> +<div id="magic-search" magic-overrides> + <div class="search-bar"> + <i class="fi-filter fa fa-filter go" ng-class="{'has-items': currentSearch.length > 0}"></i> + <div id="search-main-area"> + <span class="item-list"> + <span class="label radius secondary item" + ng-repeat="facet in currentSearch" ng-cloak="cloak"> + <span> + {{ facet.label[0] }}:<b>{{ facet.label[1] }}</b> + </span> + <a class="remove" ng-click="removeFacet($index, $event)" title="{{ strings.remove }}"> + <i class="fi-x fa fa-times"></i> + </a> + </span> + </span> + <span id="search-selected" class="label" ng-cloak="" ng-show="facetSelected"> + {{ facetSelected.label[0] }}: + </span> + <div id="search-entry" is-open="isMenuOpen"> + <input id="search-input" type="text" data-dropdown="facet-drop" dropdown-toggle + placeholder="{{ strings.prompt }}" autocomplete="off" + ng-class="{'has-items': currentSearch.length > 0}" /> + <ul id="facet-drop" class="f-dropdown dropdown-menu" data-dropdown-content=""> + <li ng-repeat="facet in filteredObj" ng-show="!facetSelected"> + <a ng-click="facetClicked($index, $event, facet.name)" + ng-show="!isMatchLabel(facet.label)">{{ facet.label }}</a> + <a ng-click="facetClicked($index, $event, facet.name)" + ng-show="isMatchLabel(facet.label)"> + {{ facet.label[0] }}<span class="match">{{ facet.label[1] }}</span>{{ facet.label[2] }} + </a> + </li> + <li ng-repeat="option in filteredOptions" ng-show="facetSelected"> + <a ng-click="optionClicked($index, $event, option.key)" + ng-show="!isMatchLabel(option.label)"> + {{ option.label }} + </a> + <a ng-click="optionClicked($index, $event, option.key)" + ng-show="isMatchLabel(option.label)"> + {{ option.label[0] }}<span class="match">{{ option.label[1] }}</span>{{ option.label[2] }} + </a> + </ul> + </div> + </div> + <a ng-click="clearSearch()" ng-show="currentSearch.length > 0" title="{{ strings.cancel }}"> + <i class="fi-x fa fa-times cancel"></i> + </a> + </div> +</div> diff --git a/xstatic/pkg/magic_search/data/magic_search.js b/xstatic/pkg/magic_search/data/magic_search.js new file mode 100755 index 0000000..943eda9 --- /dev/null +++ b/xstatic/pkg/magic_search/data/magic_search.js @@ -0,0 +1,334 @@ +/** + * @fileOverview Magic Search JS + * @requires AngularJS + * + */ + +// Allow the module to be pre-defined with additional dependencies +try{ + angular.module('MagicSearch'); +} catch (exception) { + angular.module('MagicSearch', []); +} + +angular.module('MagicSearch') + .directive('magicSearch', function($compile) { + return { + restrict: 'E', + scope: { + facets_json: '@facets', + filter_keys: '=filterKeys', + strings: '=strings' + }, + templateUrl: function (scope, elem) { + return elem.template; + }, + controller: function ($scope, $timeout) { + $scope.currentSearch = []; + $scope.initSearch = function() { + // Parse facets JSON and convert to a list of facets. + $scope.facetsJson = $scope.facets_json.replace(/__apos__/g, "\'").replace(/__dquote__/g, '\\"').replace(/__bslash__/g, "\\"); + $scope.facetsObj = JSON.parse($scope.facetsJson); + // set facets selected and remove them from facetsObj + var initialFacets = window.location.search; + if (initialFacets.indexOf('?') === 0) { + initialFacets = initialFacets.slice(1); + } + initialFacets = initialFacets.split('&'); + if (initialFacets.length > 1 || initialFacets[0].length > 0) { + $timeout(function() { + $scope.strings['prompt'] = ''; + }); + } + angular.forEach(initialFacets, function(facet, idx) { + var facetParts = facet.split('='); + angular.forEach($scope.facetsObj, function(value, idx) { + if (value.name == facetParts[0]) { + if (value.options === undefined) { + $scope.currentSearch.push({'name':facet, 'label':[value.label, facetParts[1]]}); + // allow free-form facets to remain + } + else { + angular.forEach(value.options, function(option, idx) { + if (option.key == facetParts[1]) { + $scope.currentSearch.push({'name':facet, 'label':[value.label, option.label]}); + $scope.deleteFacetSelection(facetParts); + } + }); + } + } + }); + }); + $scope.filteredObj = $scope.facetsObj; + }; + // removes a facet from the menu + $scope.deleteFacetSelection = function(facet_parts) { + angular.forEach($scope.facetsObj.slice(), function(facet, idx) { + if (facet.name == facet_parts[0]) { + if (facet.options === undefined) { + return; // allow free-form facets to remain + } + for (var i=0; i<facet.options.length; i++) { + var option = facet.options[i]; + if (option.key == facet_parts[1]) { + $scope.facetsObj[idx].options.splice($scope.facetsObj[idx].options.indexOf(option), 1); + } + } + if (facet.options.length === 0) { + $scope.facetsObj.splice($scope.facetsObj.indexOf(facet), 1); + } + } + }); + }; + $('#search-input').on('keydown', function($event) { // handle ctrl-char input + var key = $event.keyCode || $event.charCode; + if (key == 9) { // prevent default when we can. + $event.preventDefault(); + } + }); + $('#search-input').on('keyup', function($event) { // handle ctrl-char input + if ($event.metaKey == true) { + return; + } + var search_val = $('#search-input').val(); + var key = $event.keyCode || $event.charCode; + if (key == 9) { // tab, so select facet if narrowed down to 1 + if ($scope.facetSelected === undefined) { + if ($scope.filteredObj.length != 1) return; + $scope.facetClicked(0, '', $scope.filteredObj[0].name); + } + else { + if ($scope.filteredOptions.length != 1) return; + $scope.optionClicked(0, '', $scope.filteredOptions[0].key); + $scope.resetState(); + } + $timeout(function() { + $('#search-input').val(''); + }); + return; + } + if (key == 27) { // esc, so cancel and reset everthing + $timeout(function() { + $scope.hideMenu(); + $('#search-input').val(''); + }); + $scope.resetState(); + $scope.$emit('textSearch', '', $scope.filter_keys); + return; + } + if (key == 13) { // enter, so accept value + // if tag search, treat as regular facet + if ($scope.facetSelected && $scope.facetSelected.options === undefined) { + var curr = $scope.facetSelected; + curr.name = curr.name + '=' + search_val; + curr.label[1] = search_val; + $scope.currentSearch.push(curr); + $scope.resetState(); + $scope.emitQuery(); + $scope.showMenu(); + } + // if text search treat as search + else { + for (i=0; i<$scope.currentSearch.length; i++) { + if ($scope.currentSearch[i].name.indexOf('text') === 0) { + $scope.currentSearch.splice(i, 1); + } + } + $scope.currentSearch.push({'name':'text='+search_val, 'label':[$scope.strings['text'], search_val]}); + $scope.$apply(); + $scope.hideMenu(); + $('#search-input').val(''); + $scope.$emit('textSearch', search_val, $scope.filter_keys); + } + $scope.filteredObj = $scope.facetsObj; + } + else { + if (search_val === '') { + $scope.filteredObj = $scope.facetsObj; + $scope.$emit('textSearch', '', $scope.filter_keys); + } + else { + $scope.filterFacets(search_val); + } + } + }); + $('#search-input').on('keypress', function($event) { // handle character input + var search_val = $('#search-input').val(); + var key = $event.which || $event.keyCode || $event.charCode; + if (key != 8 && key != 46 && key != 13 && key != 9 && key != 27) { + search_val = search_val + String.fromCharCode(key).toLowerCase(); + } + if (search_val == ' ') { // space and field is empty, show menu + $scope.showMenu(); + $timeout(function() { + $('#search-input').val(''); + }); + return; + } + if (search_val === '') { + $scope.filteredObj = $scope.facetsObj; + $scope.$emit('textSearch', '', $scope.filter_keys); + return; + } + if (key != 8 && key != 46) { + $scope.filterFacets(search_val); + } + }); + $scope.filterFacets = function(search_val) { + // try filtering facets/options.. if no facets match, do text search + var i, idx, label; + var filtered = []; + if ($scope.facetSelected === undefined) { + $scope.filteredObj = $scope.facetsObj; + for (i=0; i<$scope.filteredObj.length; i++) { + var facet = $scope.filteredObj[i]; + idx = facet.label.toLowerCase().indexOf(search_val); + if (idx > -1) { + label = [facet.label.substring(0, idx), facet.label.substring(idx, idx + search_val.length), facet.label.substring(idx + search_val.length)]; + filtered.push({'name':facet.name, 'label':label, 'options':facet.options}); + } + } + if (filtered.length > 0) { + $scope.showMenu(); + $timeout(function() { + $scope.filteredObj = filtered; + }, 0.1); + } + else { + $scope.$emit('textSearch', search_val, $scope.filter_keys); + $scope.hideMenu(); + } + } + else { // assume option search + $scope.filteredOptions = $scope.facetOptions; + if ($scope.facetOptions === undefined) { // no options, assume free form text facet + return; + } + for (i=0; i<$scope.filteredOptions.length; i++) { + var option = $scope.filteredOptions[i]; + idx = option.label.toLowerCase().indexOf(search_val); + if (idx > -1) { + label = [option.label.substring(0, idx), option.label.substring(idx, idx + search_val.length), option.label.substring(idx + search_val.length)]; + filtered.push({'key':option.key, 'label':label}); + } + } + if (filtered.length > 0) { + $scope.showMenu(); + $timeout(function() { + $scope.filteredOptions = filtered; + }, 0.1); + } + } + }; + // enable text entry when mouse clicked anywhere in search box + $('#search-main-area').on("click", function($event) { + $('#search-input').trigger("focus"); + if ($scope.facetSelected === undefined) { + $scope.showMenu(); + } + }); + // when facet clicked, add 1st part of facet and set up options + $scope.facetClicked = function($index, $event, name) { + $scope.hideMenu(); + var facet = $scope.filteredObj[$index]; + var label = facet.label; + if (Array.isArray(label)) { + label = label.join(''); + } + $scope.facetSelected = {'name':facet.name, 'label':[label, '']}; + if (facet.options !== undefined) { + $scope.filteredOptions = $scope.facetOptions = facet.options; + $scope.showMenu(); + } + $timeout(function() { + $('#search-input').val(''); + }); + $scope.strings['prompt'] = ''; + $timeout(function() { + $('#search-input').focus(); + }); + }; + // when option clicked, complete facet and send event + $scope.optionClicked = function($index, $event, name) { + $scope.hideMenu(); + var curr = $scope.facetSelected; + curr.name = curr.name + '=' + name; + curr.label[1] = $scope.filteredOptions[$index].label; + if (Array.isArray(curr.label[1])) { + curr.label[1] = curr.label[1].join(''); + } + $scope.currentSearch.push(curr); + $scope.resetState(); + $scope.emitQuery(); + $scope.showMenu(); + }; + // send event with new query string + $scope.emitQuery = function(removed) { + var query = ''; + for (var i=0; i<$scope.currentSearch.length; i++) { + if ($scope.currentSearch[i].name.indexOf('text') !== 0) { + if (query.length > 0) query = query + "&"; + query = query + $scope.currentSearch[i].name; + } + } + if (removed !== undefined && removed.indexOf('text') === 0) { + $scope.$emit('textSearch', '', $scope.filter_keys); + } + else { + $scope.$emit('searchUpdated', query); + if ($scope.currentSearch.length > 0) { + var newFacet = $scope.currentSearch[$scope.currentSearch.length-1].name; + $scope.deleteFacetSelection(newFacet.split('=')); + } + } + }; + // remove facet and either update filter or search + $scope.removeFacet = function($index, $event) { + var removed = $scope.currentSearch[$index].name; + $scope.currentSearch.splice($index, 1); + if ($scope.facetSelected === undefined) { + $scope.emitQuery(removed); + } + else { + $scope.resetState(); + $('#search-input').val(''); + } + // facet re-enabled by reload + }; + // clear entire searchbar + $scope.clearSearch = function() { + if ($scope.currentSearch.length > 0) { + $scope.currentSearch = []; + $scope.facetsObj = JSON.parse($scope.facetsJson); + $scope.resetState(); + $scope.$emit('searchUpdated', ''); + $scope.$emit('textSearch', '', $scope.filter_keys); + } + }; + $scope.isMatchLabel = function(label) { + return Array.isArray(label); + }; + $scope.resetState = function() { + $('#search-input').val(''); + $scope.filteredObj = $scope.facetsObj; + $scope.facetSelected = undefined; + $scope.facetOptions = undefined; + $scope.filteredOptions = undefined + }; + // showMenu and hideMenu depend on foundation's dropdown. They need + // to be modified to work with another dropdown implemenation (i.e. bootstrap) + $scope.showMenu = function() { + $timeout(function() { + if ($('#facet-drop').hasClass('open') === false) { + $('#search-input').trigger('click'); + } + }); + }; + $scope.hideMenu = function() { + $(document).foundation('dropdown', 'closeall'); + }; + $scope.initSearch(); + } + }; + }) +; diff --git a/xstatic/pkg/magic_search/data/magic_search.scss b/xstatic/pkg/magic_search/data/magic_search.scss new file mode 100755 index 0000000..4993fe3 --- /dev/null +++ b/xstatic/pkg/magic_search/data/magic_search.scss @@ -0,0 +1,87 @@ +/* Copyright 2014-2015 Eucalyptus Systems, Inc. */ + +/*----------------------------------------- + Colors + ----------------------------------------- */ +$textcolor: #444; +$background: white; +$itembackground: #e6e7e8; + +/*----------------------------------------- + Item list + ----------------------------------------- */ +@mixin item-list { + .item-list { + margin-bottom: 6px; + .item { + color: #333; + background-color: $itembackground; + margin-right: 8px; + a { + color: white; + } + } + } +} + +/*----------------------------------------- + Magic Search bar + ----------------------------------------- */ +.search-bar { + position: relative; + border: 1px solid black; + background-color: $background; + margin-bottom: 0.5rem; + padding: 4px; + height: auto; + i.fi-filter { + color: $textcolor; + position: absolute; + top: 0.5rem; + left: 0.65rem; + //&.has-items { + // margin-top: 6px; + //} + } + #search-main-area { + position: relative; + margin-left: 1.65rem; + margin-right: 1.65rem; + cursor: text; + } + @include item-list; + .item-list { + margin-bottom: 2px; + } + #search-selected { + background-color: $background; + color: $textcolor; + } + #search-entry { + display: inline-block; + height: 1.5rem; + } + #search-input { + width: 220px; + border: 0; + box-shadow: none; + height: 1.5rem; + padding: 3px; + background-color: $background; + //&.has-items { + // margin-top: 6px; + //} + } + .match { + font-weight: bold; + } + i.cancel { + color: $textcolor; + &:hover { + color: darkred; + } + position: absolute; + top: 0.5rem; + right: 0.65rem; + } +} diff --git a/xstatic/pkg/magic_search/data/magic_search_bootstrap.html b/xstatic/pkg/magic_search/data/magic_search_bootstrap.html new file mode 100755 index 0000000..b8639b0 --- /dev/null +++ b/xstatic/pkg/magic_search/data/magic_search_bootstrap.html @@ -0,0 +1,50 @@ +<!--! Magic Searchbar --> +<div id="magic-search" magic-overrides> + <div class="search-bar"> + <i class="fi-filter fa fa-filter go" ng-class="{'has-items': currentSearch.length > 0}"></i> + <div id="search-main-area"> + <span class="item-list"> + <span class="label radius secondary item" + ng-repeat="facet in currentSearch" ng-cloak="cloak"> + <span> + {{ facet.label[0] }}:<b>{{ facet.label[1] }}</b> + </span> + <a class="remove" ng-click="removeFacet($index, $event)" title="{{ strings.remove }}"> + <i class="fi-x fa fa-times"></i> + </a> + </span> + </span> + <span id="search-selected" class="label" ng-cloak="" ng-show="facetSelected"> + {{ facetSelected.label[0] }}: + </span> + <!-- For bootstrap, the dropdown attribute is moved from input up to div. --> + <div id="search-entry" dropdown is-open="isMenuOpen"> + <input id="search-input" type="text" dropdown-toggle + placeholder="{{ strings.prompt }}" autocomplete="off" + ng-class="{'has-items': currentSearch.length > 0}" /> + <ul id="facet-drop" class="f-dropdown dropdown-menu" data-dropdown-content=""> + <li ng-repeat="facet in filteredObj" ng-show="!facetSelected"> + <a ng-click="facetClicked($index, $event, facet.name)" + ng-show="!isMatchLabel(facet.label)">{{ facet.label }}</a> + <a ng-click="facetClicked($index, $event, facet.name)" + ng-show="isMatchLabel(facet.label)"> + {{ facet.label[0] }}<span class="match">{{ facet.label[1] }}</span>{{ facet.label[2] }} + </a> + </li> + <li ng-repeat="option in filteredOptions" ng-show="facetSelected"> + <a ng-click="optionClicked($index, $event, option.key)" + ng-show="!isMatchLabel(option.label)"> + {{ option.label }} + </a> + <a ng-click="optionClicked($index, $event, option.key)" + ng-show="isMatchLabel(option.label)"> + {{ option.label[0] }}<span class="match">{{ option.label[1] }}</span>{{ option.label[2] }} + </a> + </ul> + </div> + </div> + <a ng-click="clearSearch()" ng-show="currentSearch.length > 0" title="{{ strings.cancel }}"> + <i class="fi-x fa fa-times cancel"></i> + </a> + </div> +</div> diff --git a/xstatic/pkg/magic_search/data/magic_search_bootstrap.js b/xstatic/pkg/magic_search/data/magic_search_bootstrap.js new file mode 100755 index 0000000..69810fe --- /dev/null +++ b/xstatic/pkg/magic_search/data/magic_search_bootstrap.js @@ -0,0 +1,28 @@ +angular.module('MagicSearch', ['ui.bootstrap']) + .directive('magicOverrides', function() { + return { + restrict: 'A', + controller: function($scope) { + // showMenu and hideMenu depend on foundation's dropdown. They need + // to be modified to work with another dropdown implemenation. + // For bootstrap, they are not needed at all. + $scope.showMenu = function() { + $scope.isMenuOpen = true; + }; + $scope.hideMenu = function() { + $scope.isMenuOpen = false; + }; + $scope.isMenuOpen = false; + + // remove the following when magic_search.js handles changing the facets/options + $scope.$watch('facets_json', function(newVal, oldVal) { + if (newVal === oldVal) { + return; + } + $scope.currentSearch = []; + $scope.initSearch(); + }); + + } + }; + });
\ No newline at end of file |