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:
Samuel Clay 2010-10-19 20:57:25 -04:00
parent fea4a5ce47
commit 9d51e999ba
4 changed files with 302 additions and 91 deletions

View file

@ -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
View 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 '&hellip;' + s + '&hellip;';
},
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');
}
};

View file

@ -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' }, [
'&times;&nbsp;',
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' }, [
'&times;&nbsp;',
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' }, [
'&times;&nbsp;',
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;

View file

@ -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',