Commit 70fb6665 authored by Ujvari Gergely's avatar Ujvari Gergely

Search feature rebased to current develop branch

- No longer part of the viewer
- Has its own controller: Search
- Own page: page_search.html
parent 210d68d6
...@@ -772,6 +772,10 @@ blockquote { ...@@ -772,6 +772,10 @@ blockquote {
} }
} }
.search-collapsed {
background-image: url("../images/plus_1.png");
}
//These are all the changes needed to collapse thread objects. //These are all the changes needed to collapse thread objects.
&.collapsed { &.collapsed {
display: block !important; display: block !important;
...@@ -1129,3 +1133,384 @@ h3.stream { ...@@ -1129,3 +1133,384 @@ h3.stream {
word-spacing: normal; word-spacing: normal;
margin-top: 0; margin-top: 0;
} }
//Visual Search////////////////////////////////
//Imported their css here
.collapsed-container {
height: 1em;
visibility: hidden;
}
.small-inner {
margin:0px 0.5em 0px 1em;
}
.search-container {
width: auto;
float: none;
overflow: hidden;
}
.visual_container {
margin-left: 3em;
}
.visual_search {
}
.VS-search .VS-icon {
background-repeat:no-repeat;
background-position:center center;
vertical-align:middle;
width:16px;
height:16px
}
.VS-search .VS-icon-cancel {
width:11px;
height:11px;
background-position:center 0;
background-image:url(../images/cancel_search.png?1311104738);
cursor:pointer
}
.VS-search .VS-icon-cancel:hover {
background-position:center -11px
}
.VS-search .VS-icon-search {
width:12px;
height:12px;
background-image:url(../images/search_glyph.png?1311104738)
}
.VS-search div, .VS-search span, .VS-search a,.VS-search img,
.VS-search ul,.VS-search li,.VS-search form,.VS-search label,.VS-interface ul,
.VS-interface li,.VS-interface {
margin:0;
padding:0;
border:0;
outline:0;
font-weight:inherit;
font-style:inherit;
font-size:100%;
font-family:inherit;
vertical-align:baseline
}
.VS-search :focus {
outline:0
}
.VS-search {
line-height:1;
color:black
}
.VS-search ol,.VS-search ul{
list-style:none
}
.VS-search{
font-family:Arial,sans-serif;
color:#373737;
font-size:12px
}
.VS-search input{
display:block;
border:none;
-moz-box-shadow:none;
-webkit-box-shadow:none;
box-shadow:none;
outline:none;
margin:0;
padding:4px;
background:transparent;
font-size:16px;
line-height:20px;
width:100%
}
.VS-interface,.VS-search .dialog,.VS-search input{
font-family:"Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,sans-serif!important;
line-height:1.1em
}
.VS-search .VS-search-box {
cursor:text;
position:relative;
background:transparent;
border:2px solid #ccc;
border-radius:16px;
-webkit-border-radius:16px;
-moz-border-radius:16px;
background-color:#fafafa;
-webkit-box-shadow:inset 0 0 3px #ccc;
-moz-box-shadow:inset 0 0 3px #ccc;
box-shadow:inset 0 0 3px #ccc;
min-height:28px;
height:1em;
//height:auto;
}
.VS-search .VS-placeholder {
position:absolute;
top:7px;
left:4px;
margin:0 20px 0 22px;
color:#808080;
font-size:14px
}
.VS-search .VS-search-box.VS-focus .VS-placeholder,
.VS-search .VS-search-box .VS-placeholder.VS-hidden {
display:none
}
.VS-search .VS-search-inner{
position:relative;
margin:0 20px 0 22px;
overflow:hidden
}
.VS-search input{
width:100px
}
.VS-search input,.VS-search .VS-input-width-tester {
padding:6px 0;
float:left;
color:#808080;
font:13px/17px Helvetica,Arial
}
.VS-search.VS-focus input{
color:#606060
}
.VS-search .VS-icon-search{
position:absolute;
left:9px;
top:8px
}
.VS-search .VS-icon-cancel{
position:absolute;
right:9px;
top:8px
}
.VS-search .search_facet {
float:left;
margin:0;
padding:0 0 0 14px;
position:relative;
border:1px solid transparent;
height:20px;margin:3px -3px 3px 0
}
.VS-search .search_facet.is_selected {
margin-left:-3px;
-webkit-border-radius:16px;
-moz-border-radius:16px;
border-radius:16px;
background-color:#d2e6fd;
background-image:-moz-linear-gradient(top,#d2e6fd,#b0d1f9);
background-image:-webkit-gradient(linear,left top,left bottom,from(#d2e6fd),to(#b0d1f9));
background-image:linear-gradient(top,#d2e6fd,#b0d1f9);
border:1px solid #6eadf5
}
.VS-search .search_facet .category{
float:left;
text-transform:uppercase;
font-weight:bold;
font-size:10px;
color:#808080;
padding:8px 0 5px;
line-height:13px;
cursor:pointer;
padding:4px 0 0
}
.VS-search .search_facet.is_selected .category{
margin-left:3px
}
.VS-search .search_facet .search_facet_input_container{
float:left
}
.VS-search .search_facet input{
margin:0;
padding:0;
color:#000;
font-size:13px;
line-height:16px;
padding:5px 0 5px 4px;
height:16px;
width:auto;
z-index:100;
position:relative;
padding-top:1px;
padding-bottom:2px;
padding-right:3px
}
.VS-search .search_facet.is_editing input,.VS-search .search_facet.is_selected input{
color:#000
}
.VS-search .search_facet .search_facet_remove{
position:absolute;
left:0;
top:4px
}
.VS-search .search_facet.is_selected .search_facet_remove{
opacity:.4;
left:3px;
filter:alpha(opacity=40);
background-position:center -11px
}
.VS-search .search_facet .search_facet_remove:hover{
opacity:1
}
.VS-search .search_facet.is_editing .category,
.VS-search .search_facet.is_selected .category{
color:#000
}
.VS-search .search_facet.search_facet_maybe_delete .category,
.VS-search .search_facet.search_facet_maybe_delete input{
color:darkred
}
.VS-search .search_input{
height:28px;
float:left;
margin-left:-1px
}
.VS-search .search_input input{
padding:6px 3px 6px 2px;
line-height:10px;
height:22px;
margin-top:-4px;
width:10px;
//width: 15em;
z-index:100;
min-width:4px;
position:relative
}
.VS-search .search_input.is_editing input{
color:#202020
}
.ui-helper-hidden-accessible{
display:none
}
.VS-interface.ui-autocomplete{
position:absolute;
border:1px solid #c0c0c0;
border-top:1px solid #d9d9d9;
background-color:#f6f6f6;
cursor:pointer;
z-index:10000;
padding:0;
margin:0;
width:auto;
min-width:80px;
max-width:220px;
max-height:240px;
overflow-y:auto;
overflow-x:hidden;
font-size:13px;
top:5px;
opacity:.97;
box-shadow:3px 4px 5px -2px rgba(0,0,0,0.5);
-webkit-box-shadow:3px 4px 5px -2px rgba(0,0,0,0.5);
-moz-box-shadow:3px 4px 5px -2px rgba(0,0,0,0.5)
}
.VS-interface.ui-autocomplete .ui-autocomplete-category{
text-transform:capitalize;
font-size:11px;
padding:4px 4px 4px;
border-top:1px solid #a2a2a2;
border-bottom:1px solid #a2a2a2;
background-color:#b7b7b7;
text-shadow:0 -1px 0 #999;
font-weight:bold;
color:white;
cursor:default
}
.VS-interface.ui-autocomplete .ui-menu-item{
float:none
}
.VS-interface.ui-autocomplete .ui-menu-item a{
color:#000;
outline:none;
display:block;
padding:3px 4px 5px;
border-radius:none;
line-height:1;
background-color:#f8f8f8;
background-image:-moz-linear-gradient(top,#f8f8f8,#f3f3f3);
background-image:-webkit-gradient(linear,left top,left bottom,from(#f8f8f8),to(#f3f3f3));
background-image:linear-gradient(top,#f8f8f8,#f3f3f3);
border-top:1px solid #fafafa;
border-bottom:1px solid #f0f0f0
}
.VS-interface.ui-autocomplete .ui-menu-item a:active{
outline:none
}
.VS-interface.ui-autocomplete .ui-menu-item .ui-state-hover,
.VS-interface.ui-autocomplete .ui-menu-item .ui-state-focus{
background-color:#6483f7;
background-image:-moz-linear-gradient(top,#648bf5,#2465f3);
background-image:-webkit-gradient(linear,left top,left bottom,from(#648bf5),to(#2465f3));
background-image:linear-gradient(top,#648bf5,#2465f3);
border-top:1px solid #5b83ec;
border-bottom:1px solid #1459e9;
border-left:none;
border-right:none;
color:white;margin:0
}
.VS-interface.ui-autocomplete .ui-corner-all{
border-radius:0
}
.VS-interface.ui-autocomplete li{
list-style:none;
width:auto
}
////////////datauri.css////////////////////
.VS-search .VS-search-box.VS-focus{
border-color:#acf;
-webkit-box-shadow:inset 0 0 3px #acf;
-moz-box-shadow:inset 0 0 3px #acf;
box-shadow:inset 0 0 3px #acf
}
.VS-search .VS-placeholder{
position:absolute;
top:7px;
left:4px;
margin:0 20px 0 22px;
color:#808080;
font-size:14px
}
...@@ -18,6 +18,10 @@ configure = ($routeProvider) -> ...@@ -18,6 +18,10 @@ configure = ($routeProvider) ->
controller: 'ViewerController' controller: 'ViewerController'
reloadOnSearch: false reloadOnSearch: false
templateUrl: 'viewer.html' templateUrl: 'viewer.html'
$routeProvider.when '/page_search',
controller: 'SearchController'
reloadOnSearch: false
templateUrl: 'page_search.html'
$routeProvider.otherwise $routeProvider.otherwise
redirectTo: '/viewer' redirectTo: '/viewer'
configure.$inject = ['$routeProvider'] configure.$inject = ['$routeProvider']
......
...@@ -93,7 +93,7 @@ class App ...@@ -93,7 +93,7 @@ class App
else else
return unless drafts.discard() return unless drafts.discard()
dynamicBucket = false dynamicBucket = false
$location.search('id', null) $location.search({'id' : null })
annotator.showViewer heatmap.buckets[bucket] annotator.showViewer heatmap.buckets[bucket]
$scope.$digest() $scope.$digest()
...@@ -420,8 +420,41 @@ class Viewer ...@@ -420,8 +420,41 @@ class Viewer
return new Date() return new Date()
class Search
this.$inject = ['$location', '$routeParams', '$scope', 'annotator']
constructor: ($location, $routeParams, $scope, annotator) ->
console.log 'SearchController'
refresh = =>
$scope.search_filter = $routeParams.matched
$scope.thread = null
heatmap = annotator.plugins.Heatmap
threads = []
for bucket in heatmap.buckets
for annotation in bucket
thread = annotator.threading.getContainer annotation.id
#Cut out annotation branches which has no search results
children = thread.flattenChildren()
hit_in_children = false
if children?
for child in children
if child.id in $scope.search_filter
hit_in_children = true
break
unless annotation.id in $scope.search_filter or hit_in_children
continue
if $routeParams.whole_document or annotation in $scope.annotations
threads.push thread
$scope.threads = threads
#Replace this with threading call
refresh()
angular.module('h.controllers', ['bootstrap']) angular.module('h.controllers', ['bootstrap'])
.controller('AppController', App) .controller('AppController', App)
.controller('AnnotationController', Annotation) .controller('AnnotationController', Annotation)
.controller('EditorController', Editor) .controller('EditorController', Editor)
.controller('ViewerController', Viewer) .controller('ViewerController', Viewer)
.controller('SearchController', Search)
...@@ -50,8 +50,8 @@ class Hypothesis extends Annotator ...@@ -50,8 +50,8 @@ class Hypothesis extends Annotator
viewer: viewer:
addField: (-> ) addField: (-> )
this.$inject = ['$document', '$location', '$rootScope', '$route', 'drafts'] this.$inject = ['$document', '$location', '$rootScope', '$route', 'drafts', '$filter']
constructor: ($document, $location, $rootScope, $route, drafts) -> constructor: ($document, $location, $rootScope, $route, drafts, $filter) ->
Gettext.prototype.parse_locale_data annotator_locale_data Gettext.prototype.parse_locale_data annotator_locale_data
super ($document.find 'body') super ($document.find 'body')
...@@ -120,6 +120,90 @@ class Hypothesis extends Annotator ...@@ -120,6 +120,90 @@ class Hypothesis extends Annotator
# Reload the route after annotations are loaded # Reload the route after annotations are loaded
this.subscribe 'annotationsLoaded', -> $route.reload() this.subscribe 'annotationsLoaded', -> $route.reload()
@user_filter = $filter('userName')
@visualSearch = VS.init
container: $('.visual_search')
query: ''
callbacks:
search: (query, searchCollection) =>
matched = []
whole_document = true
for searchItem in searchCollection.models
if searchItem.attributes.category is 'area' and
searchItem.attributes.value is 'sidebar'
whole_document = false
if whole_document
annotations = @plugins.Store.annotations
else
annotations = $rootScope.annotations
for annotation in annotations
matches = true
for searchItem in searchCollection.models
category = searchItem.attributes.category
value = searchItem.attributes.value
switch category
when 'user'
userName = @user_filter annotation.user
unless userName.toLowerCase() is value.toLowerCase()
matches = false
break
when 'text'
unless annotation.text.toLowerCase().indexOf(value.toLowerCase()) > -1
matches = false
break
when 'time'
delta = Math.round((+new Date - new Date(annotation.updated)) / 1000)
switch value
when '5 minutes'
unless delta <= 60*5
matches = false
when '1 hour'
unless delta <= 60*60
matches = false
when '1 day'
unless delta <= 60*60*24
matches = false
when '1 week'
unless delta <= 60*60*24*7
matches = false
when '1 month'
unless delta <= 60*60*24*31
matches = false
when '1 year'
unless delta <= 60*60*24*366
matches = false
when 'group'
priv_public = 'group:__world__' in (annotation.permissions.read or [])
switch value
when 'Public'
unless priv_public
matches = false
when 'Private'
if priv_public
matches = false
if matches
matched.push annotation.id
#$rootScope.search_filter = matched
# Set the path
search =
whole_document : whole_document
matched : matched
$location.path('/page_search').search(search)
$rootScope.$digest()
facetMatches: (callback) ->
callback ['text','area', 'group', 'tag','time','user'], {preserveOrder: true}
valueMatches: (facet, searchTerm, callback) ->
switch facet
when 'group' then callback ['Public', 'Private']
when 'area' then callback ['sidebar', 'document']
when 'time'
callback ['5 minutes', '1 hour', '1 day', '1 week', '1 month', '1 year']
_setupXDM: -> _setupXDM: ->
$location = @element.injector().get '$location' $location = @element.injector().get '$location'
$rootScope = @element.injector().get '$rootScope' $rootScope = @element.injector().get '$rootScope'
...@@ -416,7 +500,68 @@ class DraftProvider ...@@ -416,7 +500,68 @@ class DraftProvider
false false
angular.module('h.services', ['ngResource']) class FlashProvider
queues:
'': []
info: []
error: []
success: []
notice: null
timeout: null
this.$inject = ['$httpProvider']
constructor: ($httpProvider) ->
# Configure notification classes
angular.extend Annotator.Notification,
INFO: 'info'
ERROR: 'error'
SUCCESS: 'success'
# Configure the response interceptor
$httpProvider.responseInterceptors.push ['$q', ($q) =>
(promise) =>
promise.then (response) =>
data = response.data
format = response.headers 'content-type'
if format?.match /^application\/json/
if data.flash?
this._flash q, msgs for q, msgs of data.flash
if data.status is 'failure'
this._flash 'error', data.reason
$q.reject(data.reason)
else if data.status is 'okay'
response.data = data.model
response
else
response
]
_process: ->
@timeout = null
for q, msgs of @queues
if msgs.length
msg = msgs.shift()
unless q then [q, msg] = msg
notice = Annotator.showNotification msg, q
@timeout = this._wait =>
# work around Annotator.Notification not removing classes
for _, klass of notice.options.classes
notice.element.removeClass klass
this._process()
break
$get: ['$timeout', 'annotator', ($timeout, annotator) ->
this._wait = (cb) -> $timeout cb, 5000
angular.bind this, this._flash
]
_flash: (queue, messages) ->
if @queues[queue]?
@queues[queue] = @queues[queue]?.concat messages
this._process() unless @timeout?
angular.module('h.services', ['ngResource','h.filters'])
.provider('authentication', AuthenticationProvider) .provider('authentication', AuthenticationProvider)
.provider('drafts', DraftProvider) .provider('drafts', DraftProvider)
.service('annotator', Hypothesis) .service('annotator', Hypothesis)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment