mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-09-18 21:50:56 +00:00
First draft of brand new dual-mode classifiers. Both tumbs up and thumbs down in the same classifier. Hover behavior works, not much else, so far.
This commit is contained in:
parent
fea4a5ce47
commit
9d51e999ba
4 changed files with 302 additions and 91 deletions
|
@ -1922,7 +1922,7 @@ a.NB-splash-link:hover {
|
|||
.NB-classifier .NB-classifier-tag label {
|
||||
}
|
||||
|
||||
.NB-classifier .NB-classifier-tag-count {
|
||||
.NB-classifier .NB-classifier-count {
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
margin: 0 6px 0 2px;
|
||||
|
@ -2263,9 +2263,11 @@ background: transparent;
|
|||
display: block;
|
||||
margin: 2px 6px 6px 0;
|
||||
cursor: pointer;
|
||||
padding: 0 9px 0 4px;
|
||||
padding: 0 34px 0 26px;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
background-color: #F5CD09;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier input[type=checkbox] {
|
||||
|
@ -2283,43 +2285,81 @@ background: transparent;
|
|||
}
|
||||
|
||||
.NB-classifiers .NB-classifier label b {
|
||||
color: #303030;
|
||||
color: #957D09;
|
||||
text-shadow: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier.NB-classifier-tag {
|
||||
background-color: #FFA200;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-tag label {
|
||||
text-shadow: 1px 1px 0 #cF7200;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier.NB-classifier-title {
|
||||
background-color: #FF7940;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-title label {
|
||||
text-shadow: 1px 1px 0 #cF4910;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier.NB-classifier-author {
|
||||
background-color: #60D6A7;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-author label {
|
||||
text-shadow: 1px 1px 0 #30a677;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier.NB-classifier-publisher {
|
||||
background-color: #6a93d4;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-publisher label {
|
||||
text-shadow: 1px 1px 0 #3A63A4;
|
||||
.NB-classifiers .NB-classifier label span {
|
||||
text-shadow: 1px 1px 0 #F4E576;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier.NB-classifier-facet-disabled {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier .feed_favicon {
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier .NB-classifier-icon-like {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: transparent url('../img/reader/thumbs-up.png') no-repeat 0 0;
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier .NB-classifier-icon-dislike {
|
||||
width: 26px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: transparent url('../img/reader/thumbs-down.png') no-repeat 6px -2px;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier .NB-classifier-icon-dislike-inner {
|
||||
margin: 4px 4px 0 0;
|
||||
width: 18px;
|
||||
height: 13px;
|
||||
border-left: 1px solid #F5E529;
|
||||
}
|
||||
|
||||
.NB-classifiers .NB-classifier.NB-classifier-hover-like {
|
||||
background-color: #54B54E;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-hover-like label b {
|
||||
color: white;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-hover-like label span {
|
||||
color: white;
|
||||
text-shadow: 1px 1px 0 #254E18;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-hover-like .NB-classifier-icon-dislike {
|
||||
opacity: .1;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-hover-dislike {
|
||||
background-color: #C92123;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-hover-dislike label b {
|
||||
color: white;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-hover-dislike label span {
|
||||
color: white;
|
||||
text-shadow: 1px 1px 0 #7F1012;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-hover-dislike .NB-classifier-icon-dislike {
|
||||
opacity: 1;
|
||||
}
|
||||
.NB-classifiers .NB-classifier.NB-classifier-hover-dislike .NB-classifier-icon-like {
|
||||
opacity: .1;
|
||||
}
|
||||
/* =================== */
|
||||
/* = Mouse Indicator = */
|
||||
/* =================== */
|
||||
|
|
158
media/js/inflector.js
Normal file
158
media/js/inflector.js
Normal file
|
@ -0,0 +1,158 @@
|
|||
// Naive English transformations on words.
|
||||
window.Inflector = {
|
||||
|
||||
small : "(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v[.]?|via|vs[.]?)",
|
||||
punct : "([!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]*)",
|
||||
|
||||
// Titleize function by John Resig after John Gruber. MIT Licensed.
|
||||
titleize : function(s) {
|
||||
s = s.replace(/[-.\/_]/g, ' ').replace(/\s+/gm, ' ');
|
||||
var cap = this.capitalize;
|
||||
var parts = [], split = /[:.;?!] |(?: |^)["Ò]/g, index = 0;
|
||||
while (true) {
|
||||
var m = split.exec(s);
|
||||
parts.push( s.substring(index, m ? m.index : s.length)
|
||||
.replace(/\b([A-Za-z][a-z.'Õ]*)\b/g, function(all){
|
||||
return (/[A-Za-z]\.[A-Za-z]/).test(all) ? all : cap(all);
|
||||
})
|
||||
.replace(RegExp("\\b" + this.small + "\\b", "ig"), this.lowercase)
|
||||
.replace(RegExp("^" + this.punct + this.small + "\\b", "ig"), function(all, punct, word) {
|
||||
return punct + cap(word);
|
||||
})
|
||||
.replace(RegExp("\\b" + this.small + this.punct + "$", "ig"), cap));
|
||||
index = split.lastIndex;
|
||||
if ( m ) parts.push( m[0] );
|
||||
else break;
|
||||
}
|
||||
return parts.join("").replace(/ V(s?)\. /ig, " v$1. ")
|
||||
.replace(/(['Õ])S\b/ig, "$1s")
|
||||
.replace(/\b(AT&T|Q&A)\b/ig, function(all){
|
||||
return all.toUpperCase();
|
||||
});
|
||||
},
|
||||
|
||||
// Delegate to the ECMA5 String.prototype.trim function, if available.
|
||||
trim : function(s) {
|
||||
return s.trim ? s.trim() : s.replace(/^\s+|\s+$/g, '');
|
||||
},
|
||||
|
||||
// Trim leading and trailing non-whitespace characters, and add ellipses.
|
||||
// Try to find natural breaks in the sentence, and avoid breaking HTML fragments.
|
||||
trimExcerpt : function(s) {
|
||||
s = s.replace(/^([^<>]{0,100}?[.,!]|[^<>\s]+)/g, '');
|
||||
s = s.replace(/(([.,!]\s?)[^<>]{0,100}?|[^<>\s]+)$/g, '$2');
|
||||
return '…' + s + '…';
|
||||
},
|
||||
|
||||
camelize : function(s) {
|
||||
var parts = s.split('-'), len = parts.length;
|
||||
if (len == 1) return parts[0];
|
||||
|
||||
var camelized = s.charAt(0) == '-'
|
||||
? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
|
||||
: parts[0];
|
||||
|
||||
for (var i = 1; i < len; i++)
|
||||
camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
|
||||
|
||||
return camelized;
|
||||
},
|
||||
|
||||
lowercase : function(s) {
|
||||
return s.toLowerCase();
|
||||
},
|
||||
|
||||
capitalize : function(s) {
|
||||
return s.charAt(0).toUpperCase() + s.substring(1).toLowerCase();
|
||||
},
|
||||
|
||||
underscore : function(s) {
|
||||
return s.replace(/::/g, '/').replace(/([A-Z]+)([A-Z][a-z])/g,'$1_$2').replace(/([a-z\d])([A-Z])/g,'$1_$2').replace(/-/g,'_').toLowerCase();
|
||||
},
|
||||
|
||||
spacify : function(s) {
|
||||
return s.replace(/_/g, ' ');
|
||||
},
|
||||
|
||||
dasherize : function(s) {
|
||||
return s.replace(/_/g,'-');
|
||||
},
|
||||
|
||||
singularize : function(s) {
|
||||
return s.replace(/s$/, '');
|
||||
},
|
||||
|
||||
// Only works for words that pluralize by adding an 's', end in a 'y', or
|
||||
// that we've special-cased. Not comprehensive.
|
||||
pluralize : function(s, count) {
|
||||
if (count == 1) return s;
|
||||
if (s == 'person') return 'people';
|
||||
if (s.match(/y$/i)) return s.replace(/y$/i, 'ies');
|
||||
return s + 's';
|
||||
},
|
||||
|
||||
classify : function(s) {
|
||||
return this.camelize(this.capitalize(this.dasherize(this.singularize(s))));
|
||||
},
|
||||
|
||||
possessivize : function(s) {
|
||||
var endsInS = s.charAt(s.length - 1) == 's';
|
||||
return s + (endsInS ? "'" : "'s");
|
||||
},
|
||||
|
||||
truncate : function(s, length, truncation) {
|
||||
length = length || 30;
|
||||
truncation = _.isUndefined(truncation) ? '...' : truncation;
|
||||
return s.length > length ? s.slice(0, length - truncation.length) + truncation : s;
|
||||
},
|
||||
|
||||
// Convert a string (usually a title), to something appropriate for use in a URL.
|
||||
// Apostrophes and quotes are removed, non-word-chars become spaces, whitespace
|
||||
// is trimmed, lowercased, and spaces become dashes.
|
||||
sluggify : function(s) {
|
||||
return $.trim(s.replace(/['"]+/g, '').replace(/\W+/g, ' ')).toLowerCase().replace(/\s+/g, '-');
|
||||
},
|
||||
|
||||
commify : function(list, options) {
|
||||
var words = [];
|
||||
_.each(list, function(word, i) {
|
||||
if (options.quote) word = '"' + word + '"';
|
||||
words.push(word);
|
||||
var end = i == list.length - 1 ? '' :
|
||||
(i == list.length - 2) && options.conjunction ? ', ' + options.conjunction + ' ' :
|
||||
', ';
|
||||
words.push(end);
|
||||
});
|
||||
return words.join('');
|
||||
},
|
||||
|
||||
// Convert bytes into KB or MB
|
||||
bytesToMB : function(bytes) {
|
||||
var byteSize = Math.round(bytes / 1024 * 100) * 0.01;
|
||||
var suffix = 'KB';
|
||||
if (byteSize > 1000) {
|
||||
byteSize = Math.round(byteSize * 0.001 * 100) * 0.01;
|
||||
suffix = 'MB';
|
||||
}
|
||||
var sizeParts = byteSize.toString().split('.');
|
||||
byteSize = sizeParts[0] + (sizeParts.length > 1 ? '.' + sizeParts[1].substr(0,1) : '');
|
||||
return byteSize + ' ' + suffix;
|
||||
},
|
||||
|
||||
// Normalize an entered-by-hand url, trimming and adding the protocol, if missing.
|
||||
normalizeUrl : function(s) {
|
||||
s = Inflector.trim(s);
|
||||
if (!s) return null;
|
||||
return (/^https?:\/\//).test(s) ? s : 'http://' + s;
|
||||
},
|
||||
|
||||
// From Prototype.js. Strip out HTML tags.
|
||||
stripTags : function(s) {
|
||||
return s.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
|
||||
},
|
||||
|
||||
escapeRegExp : function(s) {
|
||||
return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
|
||||
}
|
||||
|
||||
};
|
|
@ -64,7 +64,7 @@ NEWSBLUR.ReaderClassifierStory = function(story_id, feed_id, options) {
|
|||
this.runner_story();
|
||||
};
|
||||
|
||||
var classifier = {
|
||||
var classifier_prototype = {
|
||||
|
||||
runner_trainer: function() {
|
||||
this.user_classifiers = {};
|
||||
|
@ -512,21 +512,8 @@ var classifier = {
|
|||
&& this.user_classifiers.authors[author] == this.score) {
|
||||
input_attrs['checked'] = 'checked';
|
||||
}
|
||||
|
||||
var $author = $.make('span', { className: 'NB-classifier-container NB-classifier-author-container' }, [
|
||||
$.make('span', { className: 'NB-classifier NB-classifier-author' }, [
|
||||
$.make('input', input_attrs),
|
||||
$.make('label', { 'for': 'classifier_author_'+a }, [
|
||||
$.make('b', 'Author: '),
|
||||
$.make('span', author)
|
||||
])
|
||||
]),
|
||||
(author_count && $.make('span', { className: 'NB-classifier-tag-count' }, [
|
||||
'× ',
|
||||
author_count
|
||||
]))
|
||||
]);
|
||||
$authors.push($author);
|
||||
|
||||
var $author = this.make_classifier(author, author, 'author', author_count); $authors.push($author);
|
||||
}
|
||||
return $authors;
|
||||
},
|
||||
|
@ -546,58 +533,83 @@ var classifier = {
|
|||
|
||||
if (!tag) continue;
|
||||
|
||||
var input_attrs = {
|
||||
type: 'checkbox',
|
||||
name: opinion+'tag',
|
||||
value: tag,
|
||||
id: 'classifier_tag_'+t
|
||||
};
|
||||
|
||||
if (tag in this.user_classifiers.tags && this.user_classifiers.tags[tag] == this.score) {
|
||||
input_attrs['checked'] = 'checked';
|
||||
}
|
||||
|
||||
var $tag = $.make('span', { className: 'NB-classifier-container NB-classifier-tag-container' }, [
|
||||
$.make('span', { className: 'NB-classifier NB-classifier-tag' }, [
|
||||
$.make('input', input_attrs),
|
||||
$.make('label', { 'for': 'classifier_tag_'+t }, [
|
||||
$.make('b', 'Tag: '),
|
||||
$.make('span', tag)
|
||||
])
|
||||
]),
|
||||
(tag_count && $.make('span', { className: 'NB-classifier-tag-count' }, [
|
||||
'× ',
|
||||
tag_count
|
||||
]))
|
||||
]);
|
||||
var $tag = this.make_classifier(tag, tag, 'tag', tag_count);
|
||||
$tags.push($tag);
|
||||
}
|
||||
|
||||
return $tags;
|
||||
},
|
||||
|
||||
make_publisher: function(publisher, opinion) {
|
||||
var input_attrs = {
|
||||
type: 'checkbox',
|
||||
name: opinion+'publisher',
|
||||
value: this.feed_id,
|
||||
id: 'classifier_publisher',
|
||||
checked: false
|
||||
};
|
||||
if (this.user_classifiers.feeds[this.feed_id] == this.score) {
|
||||
input_attrs['checked'] = true;
|
||||
|
||||
make_classifier: function(classifier_title, classifier_value, classifier_type, classifier_count) {
|
||||
var score = 0;
|
||||
NEWSBLUR.log(['make_classifier', this.user_classifiers, classifier_title, classifier_value, classifier_type, classifier_count]);
|
||||
if (classifier_value in this.user_classifiers[classifier_type+'s']) {
|
||||
score = this.user_classifiers[classifier_type+'s'][classifier_value];
|
||||
}
|
||||
|
||||
var $publisher = $.make('div', { className: 'NB-classifier NB-classifier-publisher' }, [
|
||||
$.make('input', input_attrs),
|
||||
$.make('label', { 'for': 'classifier_publisher' }, [
|
||||
$.make('img', { className: 'feed_favicon', src: this.google_favicon_url + publisher.feed_link }),
|
||||
$.make('span', { className: 'feed_title' }, [
|
||||
$.make('b', 'Publisher: '),
|
||||
$.make('span', publisher.feed_title)
|
||||
var classifier_type_title = Inflector.capitalize(classifier_type=='feed' ?
|
||||
'publisher' :
|
||||
classifier_type);
|
||||
|
||||
var $classifier = $.make('span', { className: 'NB-classifier-container' }, [
|
||||
$.make('span', { className: 'NB-classifier NB-classifier-'+classifier_type }, [
|
||||
$.make('input', {
|
||||
type: 'checkbox',
|
||||
className: 'NB-classifier-input-like',
|
||||
name: 'like_'+classifier_type,
|
||||
value: classifier_value
|
||||
}),
|
||||
$.make('input', {
|
||||
type: 'checkbox',
|
||||
className: 'NB-classifier-input-dislike',
|
||||
name: 'dislike_'+classifier_type,
|
||||
value: classifier_value
|
||||
}),
|
||||
$.make('div', { className: 'NB-classifier-icon-like' }),
|
||||
$.make('div', { className: 'NB-classifier-icon-dislike' }, [
|
||||
$.make('div', { className: 'NB-classifier-icon-dislike-inner' })
|
||||
]),
|
||||
$.make('label', [
|
||||
(classifier_type == 'feed' &&
|
||||
$.make('img', {
|
||||
className: 'feed_favicon',
|
||||
src: this.google_favicon_url + this.feed.feed_link
|
||||
})),
|
||||
$.make('b', classifier_type_title+': '),
|
||||
$.make('span', classifier_title)
|
||||
])
|
||||
])
|
||||
]),
|
||||
(classifier_count && $.make('span', { className: 'NB-classifier-count' }, [
|
||||
'× ',
|
||||
classifier_count
|
||||
]))
|
||||
]);
|
||||
|
||||
if (score > 0) {
|
||||
$classifier.addClass('NB-classifier-like');
|
||||
$('.NB-classifier-input-like', $classifier).attr('checked', true);
|
||||
} else if (score < 0) {
|
||||
$classifier.addClass('NB-classifier-dislike');
|
||||
$('.NB-classifier-input-dislike', $classifier).attr('checked', true);
|
||||
}
|
||||
|
||||
$('.NB-classifier', $classifier).bind('mouseover', function(e) {
|
||||
$(e.currentTarget).addClass('NB-classifier-hover-like');
|
||||
}).bind('mouseout', function(e) {
|
||||
$(e.currentTarget).removeClass('NB-classifier-hover-like');
|
||||
});
|
||||
|
||||
$('.NB-classifier-icon-dislike', $classifier).bind('mouseover', function(e) {
|
||||
$('.NB-classifier', $classifier).addClass('NB-classifier-hover-dislike');
|
||||
}).bind('mouseout', function(e) {
|
||||
$('.NB-classifier', $classifier).removeClass('NB-classifier-hover-dislike');
|
||||
});
|
||||
|
||||
return $classifier;
|
||||
},
|
||||
|
||||
make_publisher: function(publisher, opinion) {
|
||||
var $publisher = this.make_classifier(publisher.feed_title, this.feed_id, 'feed');
|
||||
return $publisher;
|
||||
},
|
||||
|
||||
|
@ -800,6 +812,6 @@ var classifier = {
|
|||
|
||||
};
|
||||
|
||||
NEWSBLUR.ReaderClassifierStory.prototype = classifier;
|
||||
NEWSBLUR.ReaderClassifierFeed.prototype = classifier;
|
||||
NEWSBLUR.ReaderClassifierTrainer.prototype = classifier;
|
||||
NEWSBLUR.ReaderClassifierStory.prototype = classifier_prototype;
|
||||
NEWSBLUR.ReaderClassifierFeed.prototype = classifier_prototype;
|
||||
NEWSBLUR.ReaderClassifierTrainer.prototype = classifier_prototype;
|
||||
|
|
|
@ -85,6 +85,7 @@ COMPRESS_JS = {
|
|||
'all': {
|
||||
'source_filenames': (
|
||||
'js/jquery-1.4.2.js',
|
||||
'js/inflector.js',
|
||||
'js/jquery.json.js',
|
||||
'js/jquery.easing.js',
|
||||
'js/jquery.newsblur.js',
|
||||
|
|
Loading…
Add table
Reference in a new issue