Commit 51c56980 authored by Randall Leeds's avatar Randall Leeds

Merge branch '719-stream-and-search-2' into develop

parents 00c8b946 9cc0e7a7
......@@ -642,6 +642,15 @@ blockquote {
display: inline-block;
font-family: $sansFontFamily;
}
&.search-upper {
padding: 0em;
padding-top: .15em;
}
}
.right-border {
padding-right: 3.7em;
}
}
......
imports = [
'bootstrap'
'ngRoute'
'h.controllers'
'h.directives'
'h.app_directives'
......
......@@ -384,6 +384,26 @@ fuzzytime = ['$filter', '$window', ($filter, $window) ->
template: '<span class="small">{{ftime | date:mediumDate}}</span>'
]
streamviewer = [ ->
link: (scope, elem, attr, ctrl) ->
return unless ctrl?
require: '?ngModel'
restrict: 'E'
templateUrl: 'streamviewer.html'
]
whenscrolled = ['$window', ($window) ->
link: (scope, elem, attr) ->
$window = angular.element($window)
$window.on 'scroll', ->
windowBottom = $window.height() + $window.scrollTop()
elementBottom = elem.offset().top + elem.height()
remaining = elementBottom - windowBottom
shouldScroll = remaining <= $window.height() * 0
if shouldScroll
scope.$apply attr.whenscrolled
]
angular.module('h.directives', ['ngSanitize'])
.directive('authentication', authentication)
......@@ -401,4 +421,5 @@ angular.module('h.directives', ['ngSanitize'])
.directive('ngBlur', ngBlur)
.directive('repeatAnim', repeatAnim)
.directive('notification', notification)
.directive('streamviewer', streamviewer)
.directive('whenscrolled', whenscrolled)
......@@ -39,25 +39,28 @@ class FlashProvider
]
flashInterceptor = ['$q', 'flash', ($q, flash) ->
response: (response) ->
data = response.data
format = response.headers 'content-type'
if format?.match /^application\/json/
if data.flash?
flash q, msgs for q, msgs of data.flash
if data.status is 'failure'
flash 'error', data.reason
$q.reject(data.reason)
else if data.status is 'okay'
response.data = data.model
response
else
response
]
angular.module('h.flash', ['ngResource'])
.provider('flash', FlashProvider)
.factory('flashInterceptor', flashInterceptor)
.config(['$httpProvider', ($httpProvider) ->
$httpProvider.responseInterceptors.push ['$q', 'flash', ($q, flash) ->
(promise) ->
promise.then (response) ->
data = response.data
format = response.headers 'content-type'
if format?.match /^application\/json/
if data.flash?
flash q, msgs for q, msgs of data.flash
if data.status is 'failure'
flash 'error', data.reason
$q.reject(data.reason)
else if data.status is 'okay'
response.data = data.model
response
else
response
]
$httpProvider.interceptors.push 'flashInterceptor'
])
\ No newline at end of file
get_quote = (annotation) ->
if annotation.quote? then return annotation.quote
if not 'target' in annotation then return ''
quote = '(Reply annotation)'
for target in annotation['target']
for selector in target['selector']
if selector['type'] is 'TextQuoteSelector'
quote = selector['exact'] + ' '
quote
class Stream
path: window.location.protocol + '//' + window.location.hostname + ':' +
window.location.port + '/__streamer__'
this.$inject = ['$location','$scope','$timeout','streamfilter']
constructor: ($location, $scope, $timeout, streamfilter) ->
$scope.annotations = []
urlParts = $location.absUrl().split('/')
$scope.filterValue = urlParts.pop()
filterType = urlParts.pop()
if filterType == "t"
$scope.filterDescription = "Annotations with tag '#{ $scope.filterValue }'"
filterClause = 'tags:i#' + $scope.filterValue
else
$scope.filterDescription = "Annotations by user '#{ $scope.filterValue }'"
filterClause = 'user:i=' + $scope.filterValue
# Generate client ID
buffer = new Array(16)
uuid.v4 null, buffer, 0
@clientID = uuid.unparse buffer
$scope.filter =
streamfilter
.setPastDataHits(150)
.setMatchPolicyIncludeAny()
.setClausesParse(filterClause)
.getFilter()
$scope.manage_new_data = (data, action) =>
for annotation in data
annotation.action = action
annotation.quote = get_quote annotation
annotation._share_link = window.location.protocol +
'//' + window.location.hostname + ':' + window.location.port + "/a/" + annotation.id
annotation._anim = 'fade'
switch action
when 'create', 'past'
unless annotation in $scope.annotations
$scope.annotations.unshift annotation
when 'update'
index = 0
for ann in $scope.annotations
if ann.id is annotation.id
# Remove the original
$scope.annotations.splice index,1
# Put back the edited
$scope.annotations.unshift annotation
break
index +=1
when 'delete'
for ann in $scope.annotations
if ann.id is annotation.id
$scope.annotations.splice index,1
break
index +=1
$scope.open = =>
$scope.sock = new SockJS(@path)
$scope.sock.onopen = =>
sockmsg =
filter: $scope.filter
clientID: @clientID
$scope.sock.send JSON.stringify sockmsg
$scope.sock.onclose = =>
$timeout $scope.open, 5000
$scope.sock.onmessage = (msg) =>
console.log 'Got something'
console.log msg
unless msg.data.type? and msg.data.type is 'annotation-notification'
return
data = msg.data.payload
action = msg.data.options.action
unless data instanceof Array then data = [data]
$scope.$apply =>
$scope.manage_new_data data, action
$scope.open()
angular.module('h.stream',['h.streamfilter', 'h.filters','h.directives','bootstrap'])
.controller('StreamCtrl', Stream)
get_quote = (annotation) ->
if not 'target' in annotation then return ''
quote = '(This is a reply annotation)'
for target in annotation['target']
for selector in target['selector']
if selector['type'] is 'TextQuoteSelector'
quote = selector['exact'] + ' '
quote
syntaxHighlight = (json) ->
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) ->
cls = 'number'
if /^"/.test(match)
if /:$/.test(match) then cls = 'key'
else cls = 'string'
else
if /true|false/.test(match) then cls = 'boolean'
else
if /null/.test(match) then cls = 'null'
return '<span class="' + cls + '">' + match + '</span>'
)
class Streamer
path: window.location.protocol + '//' + window.location.hostname + ':' +
window.location.port + '/__streamer__'
strategies: ['include_any', 'include_all', 'exclude_any', 'exclude_all']
past_modes: ['none','hits','time']
this.$inject = ['$location', '$scope', 'streamfilter', 'clauseparser']
constructor: ($location, $scope, streamfilter, clauseparser) ->
$scope.streaming = false
$scope.annotations = []
$scope.bads = []
$scope.time = 5
$scope.hits = 100
@sfilter = streamfilter
@sfilter.setPastDataHits(100)
$scope.filter = @sfilter.filter
#parse for route params
params = $location.search()
if params.match_policy in @strategies
$scope.filter.match_policy = params.match_policy
if params.action_create
if (typeof params.action_create) is 'boolean'
@sfilter.setActionCreate(params.action_create)
else
@sfilter.setActionCreate(params.action_create is 'true')
if params.action_update
if (typeof params.action_update) is 'boolean'
@sfilter.setActionUpdate(params.action_update)
else
@sfilter.setActionUpdate(params.action_update is 'true')
if params.action_delete
if (typeof params.action_delete) is 'boolean'
@sfilter.setActionDelete(params.action_delete)
else
@sfilter.setActionDelete(params.action_delete is 'true')
if params.load_past in @past_modes
if params.hits? and parseInt(params.hits) is not NaN
@sfilter.setPastDataHits(parseInt(params.hits))
if params.go_back? and parseInt(params.go_back) is not NaN
@sfilter.setPastDataTime(parseInt(params.go_back))
if params.clauses
test_clauses = params.clauses.replace ",", " "
@sfilter.setClausesParse(test_clauses)
$scope.clauses = test_clauses
else
$scope.clauses = ''
console.log $scope.filter
$scope.toggle_past = ->
switch $scope.filter.past_data.load_past
when 'none' then @sfilter.setPastDataTime($scope.time)
when 'time' then @sfilter.setPastDataHits($scope.hits)
when 'hits' then @sfilter.setPastDataNone()
$scope.$watch 'filter', (newValue, oldValue) =>
json = JSON.stringify $scope.filter, undefined, 2
$scope.json_content = syntaxHighlight json
,true
$scope.clause_change = =>
if $scope.clauses.slice(-1) is ' ' or $scope.clauses.length is 0
res = clauseparser.parse_clauses($scope.clauses)
if res?
$scope.filter.clauses = res[0]
$scope.bads = res[1]
else
$scope.filter.clauses = []
$scope.bads = []
$scope.start_streaming = =>
if $scope.streaming
$scope.sock.close()
$scope.streaming = false
res = clauseparser.parse_clauses($scope.clauses)
if res
$scope.filter.clauses = res[0]
$scope.bads = res[1]
unless $scope.bads.length is 0
return
$scope.open()
$scope.open = =>
$scope.sock = new SockJS @path
$scope.sock.onopen = =>
$scope.sock.send JSON.stringify $scope.filter
$scope.streaming = true
$scope.sock.onclose = =>
$scope.streaming = false
$scope.sock.onmessage = (msg) =>
console.log 'Got something'
console.log msg
data = msg.data[0]
action = msg.data[1]
unless data instanceof Array then data = [data]
$scope.$apply =>
$scope.manage_new_data data, action
$scope.manage_new_data = (data, action) =>
for annotation in data
annotation.action = action
annotation.quote = get_quote annotation
$scope.annotations.splice 0,0,annotation
#Update the parameters
$location.search
'match_policy': $scope.filter.match_policy
'action_create': $scope.filter.actions.create
'action_update': $scope.filter.actions.update
'action_delete': $scope.filter.actions.delete
'load_past': $scope.filter.past_data.load_past
'go_back': $scope.filter.past_data.go_back
'hits': $scope.filter.past_data.hits
'clauses' : $scope.clauses.replace " ", ","
$scope.stop_streaming = ->
$scope.sock.close()
$scope.streaming = false
angular.module('h.streamer',['h.streamfilter','h.filters','bootstrap'])
.controller('StreamerCtrl', Streamer)
class ClauseParser
filter_fields : ['references', 'text', 'user','uri', 'id', 'tags']
operators: ['=', '>', '<', '=>', '>=', '<=', '=<', '[', '#', '^']
filter_fields : ['references', 'text', 'user', 'uri', 'id', 'tags', 'created', 'updated']
operators: ['=','=>', '>=', '<=', '=<', '>', '<', '[', '#', '^', '{']
operator_mapping:
'=': 'equals'
'>': 'gt'
'<': 'lt'
'=>' : 'ge'
'>=' : 'ge'
'=>': 'ge'
'>=': 'ge'
'=<': 'le'
'<=' : 'le'
'<=': 'le'
'[' : 'one_of'
'#' : 'matches'
'^' : 'first_of'
'{' : 'match_of' # one_of but not exact search
insensitive_operator : 'i'
parse_clauses: (clauses) ->
......@@ -155,12 +156,13 @@ class StreamFilter
@filter.clauses.push clause
this
addClause: (field, operator, value, case_sensitive = false) ->
addClause: (field, operator, value, case_sensitive = false, es_query_string = false) ->
@filter.clauses.push
field: field
operator: operator
value: value
case_sensitive: case_sensitive
es_query_string: es_query_string
this
setClausesParse: (clauses_to_parse, error_checking = false) ->
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/**
* @license AngularJS v1.1.4
* @license AngularJS v1.2.0-rc.2
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {
'use strict';
(function(window, angular, undefined) {'use strict';
var $sanitizeMinErr = angular.$$minErr('$sanitize');
/**
* @ngdoc overview
* @name ngSanitize
* @description
*
* # ngSanitize
*
* The `ngSanitize` module provides functionality to sanitize HTML.
*
* {@installModule sanitize}
*
* See {@link ngSanitize.$sanitize `$sanitize`} for usage.
*/
/*
......@@ -48,68 +57,71 @@
<doc:example module="ngSanitize">
<doc:source>
<script>
function Ctrl($scope) {
function Ctrl($scope, $sce) {
$scope.snippet =
'<p style="color:blue">an html\n' +
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
'snippet</p>';
$scope.deliberatelyTrustDangerousSnippet = function() {
return $sce.trustAsHtml($scope.snippet);
};
}
</script>
<div ng-controller="Ctrl">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table>
<tr>
<td>Filter</td>
<td>Directive</td>
<td>How</td>
<td>Source</td>
<td>Rendered</td>
</tr>
<tr id="html-filter">
<td>html filter</td>
<td>
<pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippet"></div>
</td>
<tr id="bind-html-with-sanitize">
<td>ng-bind-html</td>
<td>Automatically uses $sanitize</td>
<td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind-html="snippet"></div></td>
</tr>
<tr id="bind-html-with-trust">
<td>ng-bind-html</td>
<td>Bypass $sanitize by explicitly trusting the dangerous value</td>
<td><pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
</tr>
<tr id="escaped-html">
<td>no filter</td>
<tr id="bind-default">
<td>ng-bind</td>
<td>Automatically escapes</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind="snippet"></div></td>
</tr>
<tr id="html-unsafe-filter">
<td>unsafe html filter</td>
<td><pre>&lt;div ng-bind-html-unsafe="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind-html-unsafe="snippet"></div></td>
</tr>
</table>
</div>
</doc:source>
<doc:scenario>
it('should sanitize the html snippet ', function() {
expect(using('#html-filter').element('div').html()).
it('should sanitize the html snippet by default', function() {
expect(using('#bind-html-with-sanitize').element('div').html()).
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
});
it('should inline raw snippet if bound to a trusted value', function() {
expect(using('#bind-html-with-trust').element("div").html()).
toBe("<p style=\"color:blue\">an html\n" +
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
"snippet</p>");
});
it('should escape snippet without any filter', function() {
expect(using('#escaped-html').element('div').html()).
expect(using('#bind-default').element('div').html()).
toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
"&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
"snippet&lt;/p&gt;");
});
it('should inline raw snippet if filtered as unsafe', function() {
expect(using('#html-unsafe-filter').element("div").html()).
toBe("<p style=\"color:blue\">an html\n" +
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
"snippet</p>");
});
it('should update', function() {
input('snippet').enter('new <b>text</b>');
expect(using('#html-filter').binding('snippet')).toBe('new <b>text</b>');
expect(using('#escaped-html').element('div').html()).toBe("new &lt;b&gt;text&lt;/b&gt;");
expect(using('#html-unsafe-filter').binding("snippet")).toBe('new <b>text</b>');
input('snippet').enter('new <b onclick="alert(1)">text</b>');
expect(using('#bind-html-with-sanitize').element('div').html()).toBe('new <b>text</b>');
expect(using('#bind-html-with-trust').element('div').html()).toBe('new <b onclick="alert(1)">text</b>');
expect(using('#bind-default').element('div').html()).toBe("new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
});
</doc:scenario>
</doc:example>
......@@ -129,7 +141,7 @@ var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:
BEGING_END_TAGE_REGEXP = /^<\s*\//,
COMMENT_REGEXP = /<!--(.*?)-->/g,
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/,
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/i,
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
......@@ -256,7 +268,7 @@ function htmlParser( html, handler ) {
}
if ( html == last ) {
throw "Parse Error: " + html;
throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block of html: {0}", html);
}
last = html;
}
......@@ -283,10 +295,10 @@ function htmlParser( html, handler ) {
var attrs = {};
rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) {
rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
var value = doubleQuotedValue
|| singleQoutedValue
|| unqoutedValue
|| singleQuotedValue
|| unquotedValue
|| '';
attrs[name] = decodeEntities(value);
......@@ -400,37 +412,16 @@ function htmlSanitizeWriter(buf){
// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).value('$sanitize', $sanitize);
/**
* @ngdoc directive
* @name ngSanitize.directive:ngBindHtml
*
* @description
* Creates a binding that will sanitize the result of evaluating the `expression` with the
* {@link ngSanitize.$sanitize $sanitize} service and innerHTML the result into the current element.
*
* See {@link ngSanitize.$sanitize $sanitize} docs for examples.
*
* @element ANY
* @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
*/
angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($sanitize) {
return function(scope, element, attr) {
element.addClass('ng-binding').data('$binding', attr.ngBindHtml);
scope.$watch(attr.ngBindHtml, function ngBindHtmlWatchAction(value) {
value = $sanitize(value);
element.html(value || '');
});
};
}]);
/**
* @ngdoc filter
* @name ngSanitize.filter:linky
* @function
*
* @description
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
* plain email address links.
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
* plain email address links.
*
* Requires the {@link ngSanitize `ngSanitize`} module to be installed.
*
* @param {string} text Input text.
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
......
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