This commit is contained in:
Kate Hudson 2014-03-21 14:34:18 -04:00
Родитель 918857c587
Коммит 1f01b97c53
5 изменённых файлов: 5213 добавлений и 0 удалений

3511
dist/web-literacy-client.with-langs.js поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

150
example/index.html Normal file
Просмотреть файл

@ -0,0 +1,150 @@
<!doctype html>
<html lang="en_US" dir="ltr">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Web literacy tags</title>
<link href="https://s3-us-west-2.amazonaws.com/makerstrap/makerstrap.complete.min.css" rel="stylesheet">
<link href="make-gallery.css" rel="stylesheet">
<style>
.autocomplete-suggestions { border: 1px solid #999; background: #FFF; overflow: auto; }
.autocomplete-suggestion { padding: 2px 5px; white-space: nowrap; overflow: hidden; }
.autocomplete-selected { background: #F0F0F0; }
.autocomplete-suggestions strong { font-weight: normal; color: #3399FF; }
.tag-input-wrapper {
float: left;
width: 250px;
margin-right: 5px;
margin-bottom: 5px;
}
.auto-tag {
float: left;
margin-right: 5px;
margin-bottom: 5px;
}
#tag .btn {
transition: all 0.1s ease-in;
position: relative;
}
#tag .btn:hover {
color: rgba(255, 255, 255, 0.3);
}
#tag .btn:hover .fa-times {
opacity: 1;
}
.fa-times {
color: #FFF;
opacity: 0;
transition: opacity 0.1s ease-in;
position: absolute;
top: 3px;
right: 3px;
font-size: 10px;
}
</style>
</head>
<body>
<div class="container">
<h2 class="text-center page-header">Web Literacy Standard (1.0.0) Localized tags demo</h2>
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<p>
<select id="language" class="form-control">
</select>
</p>
<div class="tags-wrapper">
<span id="tag"></span>
<div class="tag-input-wrapper">
<div class="input-group">
<input class="form-control" id="tagger">
</div>
</div>
</div>
</div>
</div>
<div id="gallery"></div>
</div>
<script src="https://code.jquery.com/jquery-2.1.0.min.js"></script>
<script src="autocomplete.js"></script>
<script src="../dist/web-literacy-client.with-langs.js"></script>
<script src="make-api.js"></script>
<script src="make-gallery.js"></script>
<script>
var wlc = new WebLiteracyClient();
// Suggestions
var suggestions = [];
function setSuggestions(lang) {
wlc.lang(lang);
suggestions = wlc.all().map(function(item) {
return {
value: item.term,
data: item
};
});
$('#tag .btn').each(function(i, el) {
$(el).html(wlc.term(el.getAttribute('data-tag')));
});
$('#tagger').autocomplete().setOptions({
lookup: suggestions
});
}
$('#tagger').autocomplete({
lookup: suggestions,
onSelect: function(suggestion) {
$('#tag')
.append('<a class="btn btn-primary auto-tag" data-tag="' + suggestion.data.tag + '">' + suggestion.value + '<span class="fa fa-times"></span></a>');
$('#tagger').val('');
selectedTags.push(suggestion.data.tag);
makeGallery();
}
});
//Selects
// wlc.supportedLangs()
['en', 'fr', 'zh_CN'].forEach(function(lang) {
$('#language').append('<option value="' + lang +'">' + lang +'</option>');
});
$('#language').change(function() {
setSuggestions(this.value);
});
$('#language').val('en');
setSuggestions('en');
var selectedTags = [];
function makeGallery() {
new MakeGallery({
limit: 20,
sortByField: 'likes',
tags: [
{
tags: selectedTags
}
]
}, {
apiURL: 'https://makeapi.webmaker.org',
elementSelector: '#gallery'
});
}
$('#tag').on('click', '.btn', function() {
var tag = this.getAttribute('data-tag');
selectedTags = $.grep(selectedTags, function(item) {
return item !== tag;
});
$(this).remove();
makeGallery();
});
</script>
</body>
</html>

493
example/make-api.js Normal file
Просмотреть файл

@ -0,0 +1,493 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Shim module so we can safely check what environment this is being included in.
var module = module || undefined;
(function ( module ) {
var API_PREFIX = "/api/20130724/make/";
var Make,
xhrStrategy,
apiURL,
credentials,
auth,
user,
pass,
csrfToken,
request,
hawk;
function nodeStrategy( type, path, data, callback ) {
// Only use auth if provided
var authObj = ( user && pass ) ? {
username: user,
password: pass,
sendImmediately: true
} : undefined,
requestOptions = {
method: type,
uri: path,
json: data,
headers: {}
},
header;
if ( authObj ) {
requestOptions.auth = authObj;
} else if( credentials ) {
header = hawk.client.header( path, type, { credentials: credentials } );
requestOptions.headers.Authorization = header.field;
}
request( requestOptions, function( err, res, body ) {
if ( err ) {
return callback( err );
}
if ( credentials ) {
if ( !hawk.client.authenticate( res, credentials, header.artifacts, { payload: JSON.stringify( body ) } ) ) {
return callback( "Warning: The response does not authenticate - your traffic may be getting intercepted and modified" );
}
}
if ( res.statusCode === 200 ) {
callback( null, body );
} else {
// something went wrong, the body contains the details
callback( body );
}
});
}
function browserStrategy( type, path, data, callback ) {
var request = new XMLHttpRequest();
if ( auth ) {
request.open( type, path, true, user, pass );
} else {
request.open( type, path, true );
}
if ( csrfToken ) {
request.setRequestHeader( "X-CSRF-Token", csrfToken ); // express.js uses a non-standard name for csrf-token
}
request.setRequestHeader( "Content-Type", "application/json; charset=utf-8" );
request.onreadystatechange = function() {
var response,
error;
if ( this.readyState === 4 ) {
try {
response = JSON.parse( this.responseText ),
error = response.error;
}
catch ( exception ) {
error = exception;
}
if ( error ) {
callback( error );
} else {
callback( null, response );
}
}
};
request.send( JSON.stringify( data ) );
}
function doXHR( type, path, data, callback ) {
if ( typeof data === "function" ) {
callback = data;
data = {};
} else if ( typeof data === "string" ) {
path = data.length ? path + "?" + data : path;
data = {};
}
path = apiURL + path;
xhrStrategy( type, path, data, callback );
}
// Extend a make with some API sugar.
function wrap( make, options ) {
function getMakeInstance() {
if ( !getMakeInstance.instance ) {
getMakeInstance.instance = Make( options );
}
return getMakeInstance.instance;
}
// Lazily extract various tags types as needed, and memoize.
function lazyInitTags( o, name, regexp ) {
delete o[ name ];
var tags = [];
make.tags.forEach( function( tag ) {
if( regexp.test( tag ) ) {
tags.push( tag );
}
});
o[ name ] = tags;
return tags;
}
var wrapped = {
// Application Tags are "webmaker.org:foo", which means two
// strings, joined with a ':', and the first string does not
// contain an '@'
get appTags() {
return lazyInitTags( this, 'appTags', /^[^@]+\:[^:]+/ );
},
// User Tags are "some@something.com:foo", which means two
// strings, joined with a ':', and the first string contains
// an email address (i.e., an '@').
get userTags() {
return lazyInitTags( this, 'userTags', /^[^@]+@[^@]+\:[^:]+/ );
},
// Raw Tags are "foo" or "#fooBar", which means one string
// which does not include a colon.
get rawTags() {
return lazyInitTags( this, 'rawTags', /^[^:]+$/ );
},
// Determine whether this make is tagged with any of the tags
// passed into `tags`. This can be a String or [ String ],
// and the logic is OR vs. AND for multiple.
taggedWithAny: function( tags ) {
var any = false,
all = make.tags;
tags = Array.isArray( tags ) ? tags : [ tags ];
for( var i = 0; i < tags.length; i++ ) {
if ( all.indexOf( tags[ i ] ) > -1 ) {
return true;
}
}
return false;
},
// Get a list of other makes that were remixed from this make.
// The current make's URL is used as a key.
remixes: function( callback ) {
callback = callback || function(){};
getMakeInstance()
.find({ remixedFrom: wrapped._id })
.then( callback );
},
// Similar to remixes(), but filter out only those remixes that
// have a different locale (i.e., are localized versions of this
// make).
locales: function( callback ) {
callback = callback || function(){};
this.remixes( function( err, results ) {
if( err ) {
callback( err );
return;
}
var locales = [];
results.forEach( function( one ) {
if ( one.locale !== wrapped.locale ) {
locales.push( one );
}
});
callback( null, locales );
});
},
// Get the original make used to create this remix. Null is sent
// back in the callback if there was no original (not a remix)
original: function( callback ) {
callback = callback || function(){};
if ( !wrapped.remixedFrom ) {
callback( null, null );
return;
}
getMakeInstance()
.find({ _id: wrapped._id })
.then( callback );
},
update: function( email, callback ) {
callback = callback || function(){};
getMakeInstance()
.update( wrapped._id, wrapped, callback );
}
};
// Extend wrapped with contents of make
[ "url", "contentType", "locale", "title",
"description", "author", "published", "tags", "thumbnail",
"username", "remixedFrom", "_id", "emailHash", "createdAt",
"updatedAt", "likes", "reports", "remixurl", "editurl" ].forEach( function( prop ) {
wrapped[ prop ] = make[ prop ];
});
// Virtuals will only be exposed while still on the server end
// forcing us to still manually expose it for client side users.
wrapped.id = wrapped._id;
return wrapped;
}
// Shorthand for creating a Make Object
Make = function Make( options ) {
// default search path - changed if Hawk credentials are provided
var searchPath = "search";
apiURL = options.apiURL;
if ( options.hawk ) {
credentials = options.hawk;
searchPath = "protectedSearch";
} else if ( options.auth ) {
auth = options.auth.split( ":" );
user = auth[ 0 ];
pass = auth[ 1 ];
}
if ( options.csrf ) {
csrfToken = options.csrf;
}
function addPair( queryPairs, key, val, not ) {
val = val ? val.toString() : "";
if ( !val.length ) {
return this;
}
val = not ? "{!}" + val : val;
queryPairs.push( encodeURIComponent( key ) + "=" + encodeURIComponent( val ) );
}
function mapAndJoinTerms( terms ) {
return terms.map(function( val ) {
return val.trim();
}).join( "," );
}
function addArrayPair( queryPairs, options, field, not ){
if ( options ) {
var terms = options[ field ] || options,
execution = options.execution || "and";
if ( Array.isArray( terms ) ) {
terms = mapAndJoinTerms( terms );
} else {
terms = mapAndJoinTerms( terms.split( "," ) );
}
terms = execution + "," + terms;
addPair( queryPairs, field, terms, not );
}
}
return {
queryPairs: [],
find: function( options ) {
options = options || {};
for ( var key in options ) {
if ( options.hasOwnProperty( key ) && this[ key ] ) {
if ( Array.isArray( options[ key ] ) ) {
this[ key ].apply( this, options[ key ] );
} else {
this[ key ]( options[ key ] );
}
}
}
return this;
},
author: function( name, not ) {
addPair( this.queryPairs, "author", name, not );
return this;
},
user: function( id, not ) {
addPair( this.queryPairs, "user", id, not );
return this;
},
tags: function( options, not ) {
addArrayPair( this.queryPairs, options, "tags", not );
return this;
},
tagPrefix: function( prefix, not ) {
addPair( this.queryPairs, "tagPrefix", prefix, not );
return this;
},
url: function( url, not ) {
addPair( this.queryPairs, "url", url, not );
return this;
},
contentType: function( contentType, not ) {
addPair( this.queryPairs, "contentType", contentType, not );
return this;
},
remixedFrom: function( id, not ) {
addPair( this.queryPairs, "remixedFrom", id, not );
return this;
},
id: function( ids, not ) {
if ( typeof ids === "string" ) {
addPair( this.queryPairs, "id", ids, not );
} else {
// override execution to be "or"
if ( Array.isArray( ids ) ) {
ids = {
id: ids,
execution: "or"
};
} else {
ids.execution = "or";
}
addArrayPair( this.queryPairs, ids, "id", not );
}
return this;
},
title: function( title, not ) {
addPair( this.queryPairs, "title", title, not );
return this;
},
description: function( desc, not ) {
addPair( this.queryPairs, "description", desc, not );
return this;
},
limit: function( num ) {
addPair( this.queryPairs, "limit", num );
return this;
},
page: function( num ) {
addPair( this.queryPairs, "page", num );
return this;
},
sortByField: function( field, direction ) {
var sortOpts;
if ( typeof field === "string" ) {
sortOpts = field;
sortOpts += "," + ( direction ? direction : "desc" );
addPair( this.queryPairs, "sortByField", sortOpts );
return this;
}
return this;
},
or: function() {
addPair( this.queryPairs, "or", "1" );
return this;
},
then: function( callback ) {
var querystring = this.queryPairs.join( "&" );
this.queryPairs = [];
doXHR( "GET", API_PREFIX + searchPath,
querystring,
function( err, data ) {
if ( err ) {
return callback( err );
}
if ( !data ) {
return callback( null, [], 0);
}
// Wrap resulting makes with some extra API.
var hits = data.makes;
for ( var i = 0; i < hits.length; i++ ) {
hits[ i ] = wrap( hits[ i ], options );
}
callback( null, hits, data.total );
}
);
},
create: function create( options, callback ) {
doXHR( "POST", API_PREFIX, options, callback );
return this;
},
update: function update( id, options, callback ) {
doXHR( "PUT", API_PREFIX + id, options, callback );
return this;
},
like: function like( id, maker, callback ) {
doXHR( "PUT", API_PREFIX + "like/" + id, { maker: maker }, callback );
return this;
},
unlike: function update( id, maker, callback ) {
doXHR( "PUT", API_PREFIX + "unlike/" + id, { maker: maker }, callback );
return this;
},
remove: function remove( id, callback ) {
doXHR( "DELETE", API_PREFIX + id, callback );
return this;
},
autocompleteTags: function autocompleteTags( term, size, callback ) {
if ( !callback && typeof size === "function" ) {
callback = size;
size = 10;
}
var query = "t=" + term + "&s=" + size;
doXHR( "GET", API_PREFIX + "tags", query, callback );
return this;
},
report: function report( id, maker, callback ) {
doXHR( "PUT", API_PREFIX + "report/" + id, { maker: maker }, callback );
return this;
},
cancelReport: function cancelReport( id, maker, callback ) {
doXHR( "PUT", API_PREFIX + "cancelReport/" + id, { maker: maker }, callback );
return this;
},
remixCount: function remixCount( id, options, callback ) {
options = options || {};
var from = options.from || "",
to = options.to || "",
qs = "id=" + id + "&from=" + from + "&to=" + to;
doXHR( "GET", API_PREFIX + "remixCount", qs, callback );
return this;
}
};
};
// Depending on the environment we need to export our "Make" object differently.
if ( typeof module !== 'undefined' && module.exports ) {
request = require( "request" );
hawk = require( "hawk" );
// npm install makeapi support
xhrStrategy = nodeStrategy;
module.exports = Make;
} else {
xhrStrategy = browserStrategy;
if ( typeof define === "function" && define.amd ) {
// Support for requirejs
define(function() {
return Make;
});
} else {
// Support for include on individual pages.
window.Make = Make;
}
}
}( module ));

233
example/make-gallery.css Normal file
Просмотреть файл

@ -0,0 +1,233 @@
@import url(http://fonts.googleapis.com/css?family=Open+Sans);
.make-gallery * {
margin: 0;
padding: 0;
}
.make-gallery {
background: #EEE;
overflow: auto;
padding: 0 1em 1em 0;
font-family: "Open Sans", "Helvetica Neue", Arial, sans-serif;
}
.make-gallery .make-node {
padding: 1em 0 0 1em;
font-weight: 300;
float: left;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.make-gallery .make-node-inner {
background: white;
position: relative;
-webkit-box-shadow: 0 3px 3px -2px rgba(0,0,0,0.2);
-moz-box-shadow: 0 3px 3px -2px rgba(0,0,0,0.2);
box-shadow: 0 3px 3px -2px rgba(0,0,0,0.2);
}
.make-gallery .make-link {
overflow: hidden;
display: block;
border-bottom: solid 1px #EEE;
}
.make-gallery .make-thumbnail {
height: 200px;
background-position: center;
background-size: cover;
background-color: white;
border-bottom: solid 1px #EEE;
-webkit-transition: all linear .1s;
-moz-transition: all linear .1s;
-o-transition: all linear .1s;
transition: all linear .1s;
}
.make-gallery .default-thumbnail {
background-image: url("../../images/webmaker-logo-large.png");
}
.make-gallery .make-thumbnail:hover {
opacity: .8;
}
.make-gallery h1 {
font-size: 20px;
font-weight: normal;
margin: 0 0 13px 0;
padding: 0;
line-height: 28px;
}
.make-gallery .make-details-link {
color: #555;
text-decoration: none;
}
.make-gallery .make-details-user {
color: #333;
text-decoration: none;
font-weight: 500;
font-family: "Open Sans", "Helvetica Neue", Arial, sans-serif;
}
.make-gallery .make-details-user:hover {
text-decoration: underline;
color: #27aae1;
}
.make-gallery .make-meta {
font-size: .9em;
padding: 0;
margin: 0 0 15px 0;
overflow: auto;
}
.make-gallery .make-description {
color: #999;
font-size: 1em;
padding: 0;
margin: 0 0 10px 0;
}
.make-gallery .make-details {
background: white;
padding: 1em 1em 1.5em 1em;
color: #999;
}
.make-gallery .make-user-avatar {
float: left;
margin-right: 6px;
-webkit-border-radius: 44px;
-moz-border-radius: 44px;
border-radius: 44px;
height: 44px;
width: 44px;
}
.make-gallery .make-details-link:hover {
color: #27aae1;
text-decoration: underline;
}
.make-gallery .make-tags {
font-size: .9em;
}
.make-gallery .make-tags a {
color: #27aae1;
text-decoration: none;
font-family: "Open Sans", "Helvetica Neue", sans-serif;
display: inline-block;
font-weight: normal;
}
.make-gallery .make-tags a:hover {
text-decoration: underline;
}
.make-gallery .make-tags a:after {
content: ",";
}
.make-gallery .make-tags a:before {
content: "#";
}
.make-gallery .make-tags a:last-child:after {
content: "";
}
.make-gallery .make-tags a:only-child:after {
content: "";
}
.make-gallery .button-container {
padding: 0 1em 1em 1em;
}
.make-gallery .button-container a {
display: inline-block;
position: relative;
font-size: .9em;
padding: 0.6em 0.9em 0.8em 2.4em;
text-decoration: none;
font-family: "Open Sans", "Helvetica Neue", sans-serif;
}
.make-gallery .make-remix {
background: #3fb58e;
border-bottom: solid 2px #228363;
color: white;
border-radius: 2px;
font-weight: normal;
}
.make-gallery .make-remix .icon-remix {
left: .6em;
top: 8px;
display: inline-block;
width: 20px;
height: 20px;
background-size: 12px;
background-repeat: no-repeat;
background-position: center;
background-image: url(/images/remix-icon.png);
position: absolute;
}
.make-gallery .make-remix:hover {
background: #4bc79e;
}
.make-gallery .make-remix:active {
border-bottom: none;
background: #2f9b77;
border-top: solid 2px #208161;
}
.make-gallery .make-remix:active .icon-remix {
top: 9px;
}
.make-gallery .make-like {
display: none !important;
}
.make-gallery .make-node {
width: 20%;
}
.make-gallery[data-cols="1"] .make-node {
width: 100%;
}
.make-gallery[data-cols="2"] .make-node {
width: 50%;
}
.make-gallery[data-cols="3"] .make-node {
width: 33.3%;
}
.make-gallery[data-cols="4"] .make-node {
width: 25%;
}
.make-gallery[data-cols="5"] .make-node {
width: 20%;
}
.make-gallery .make-no-results {
padding: 20px;
text-align: center;
display: none;
}

826
example/make-gallery.js Normal file
Просмотреть файл

@ -0,0 +1,826 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Shim module so we can safely check what environment this is being included in.
var module = module || undefined;
(function ( module ) {
var API_PREFIX = "/api/20130724/make/";
var Make,
xhrStrategy,
apiURL,
credentials,
auth,
user,
pass,
csrfToken,
request,
hawk;
function nodeStrategy( type, path, data, callback ) {
// Only use auth if provided
var authObj = ( user && pass ) ? {
username: user,
password: pass,
sendImmediately: true
} : undefined,
requestOptions = {
method: type,
uri: path,
json: data,
headers: {}
},
header;
if ( authObj ) {
requestOptions.auth = authObj;
} else if( credentials ) {
header = hawk.client.header( path, type, { credentials: credentials } );
requestOptions.headers.Authorization = header.field;
}
request( requestOptions, function( err, res, body ) {
if ( err ) {
return callback( err );
}
if ( credentials ) {
if ( !hawk.client.authenticate( res, credentials, header.artifacts, { payload: JSON.stringify( body ) } ) ) {
return callback( "Warning: The response does not authenticate - your traffic may be getting intercepted and modified" );
}
}
if ( res.statusCode === 200 ) {
callback( null, body );
} else {
// something went wrong, the body contains the details
callback( body );
}
});
}
function browserStrategy( type, path, data, callback ) {
var request = new XMLHttpRequest();
if ( auth ) {
request.open( type, path, true, user, pass );
} else {
request.open( type, path, true );
}
if ( csrfToken ) {
request.setRequestHeader( "X-CSRF-Token", csrfToken ); // express.js uses a non-standard name for csrf-token
}
request.setRequestHeader( "Content-Type", "application/json; charset=utf-8" );
request.onreadystatechange = function() {
var response,
error;
if ( this.readyState === 4 ) {
try {
response = JSON.parse( this.responseText ),
error = response.error;
}
catch ( exception ) {
error = exception;
}
if ( error ) {
callback( error );
} else {
callback( null, response );
}
}
};
request.send( JSON.stringify( data ) );
}
function doXHR( type, path, data, callback ) {
if ( typeof data === "function" ) {
callback = data;
data = {};
} else if ( typeof data === "string" ) {
path = data.length ? path + "?" + data : path;
data = {};
}
path = apiURL + path;
xhrStrategy( type, path, data, callback );
}
// Extend a make with some API sugar.
function wrap( make, options ) {
function getMakeInstance() {
if ( !getMakeInstance.instance ) {
getMakeInstance.instance = Make( options );
}
return getMakeInstance.instance;
}
// Lazily extract various tags types as needed, and memoize.
function lazyInitTags( o, name, regexp ) {
delete o[ name ];
var tags = [];
make.tags.forEach( function( tag ) {
if( regexp.test( tag ) ) {
tags.push( tag );
}
});
o[ name ] = tags;
return tags;
}
var wrapped = {
// Application Tags are "webmaker.org:foo", which means two
// strings, joined with a ':', and the first string does not
// contain an '@'
get appTags() {
return lazyInitTags( this, 'appTags', /^[^@]+\:[^:]+/ );
},
// User Tags are "some@something.com:foo", which means two
// strings, joined with a ':', and the first string contains
// an email address (i.e., an '@').
get userTags() {
return lazyInitTags( this, 'userTags', /^[^@]+@[^@]+\:[^:]+/ );
},
// Raw Tags are "foo" or "#fooBar", which means one string
// which does not include a colon.
get rawTags() {
return lazyInitTags( this, 'rawTags', /^[^:]+$/ );
},
// Determine whether this make is tagged with any of the tags
// passed into `tags`. This can be a String or [ String ],
// and the logic is OR vs. AND for multiple.
taggedWithAny: function( tags ) {
var any = false,
all = make.tags;
tags = Array.isArray( tags ) ? tags : [ tags ];
for( var i = 0; i < tags.length; i++ ) {
if ( all.indexOf( tags[ i ] ) > -1 ) {
return true;
}
}
return false;
},
// Get a list of other makes that were remixed from this make.
// The current make's URL is used as a key.
remixes: function( callback ) {
callback = callback || function(){};
getMakeInstance()
.find({ remixedFrom: wrapped._id })
.then( callback );
},
// Similar to remixes(), but filter out only those remixes that
// have a different locale (i.e., are localized versions of this
// make).
locales: function( callback ) {
callback = callback || function(){};
this.remixes( function( err, results ) {
if( err ) {
callback( err );
return;
}
var locales = [];
results.forEach( function( one ) {
if ( one.locale !== wrapped.locale ) {
locales.push( one );
}
});
callback( null, locales );
});
},
// Get the original make used to create this remix. Null is sent
// back in the callback if there was no original (not a remix)
original: function( callback ) {
callback = callback || function(){};
if ( !wrapped.remixedFrom ) {
callback( null, null );
return;
}
getMakeInstance()
.find({ _id: wrapped._id })
.then( callback );
},
update: function( email, callback ) {
callback = callback || function(){};
getMakeInstance()
.update( wrapped._id, wrapped, callback );
}
};
// Extend wrapped with contents of make
[ "url", "contentType", "locale", "title",
"description", "author", "published", "tags", "thumbnail",
"username", "remixedFrom", "_id", "emailHash", "createdAt",
"updatedAt", "likes", "reports", "remixurl", "editurl" ].forEach( function( prop ) {
wrapped[ prop ] = make[ prop ];
});
// Virtuals will only be exposed while still on the server end
// forcing us to still manually expose it for client side users.
wrapped.id = wrapped._id;
return wrapped;
}
// Shorthand for creating a Make Object
Make = function Make( options ) {
// default search path - changed if Hawk credentials are provided
var searchPath = "search";
apiURL = options.apiURL;
if ( options.hawk ) {
credentials = options.hawk;
searchPath = "protectedSearch";
} else if ( options.auth ) {
auth = options.auth.split( ":" );
user = auth[ 0 ];
pass = auth[ 1 ];
}
if ( options.csrf ) {
csrfToken = options.csrf;
}
function addPair( queryPairs, key, val, not ) {
val = val ? val.toString() : "";
if ( !val.length ) {
return this;
}
val = not ? "{!}" + val : val;
queryPairs.push( encodeURIComponent( key ) + "=" + encodeURIComponent( val ) );
}
function mapAndJoinTerms( terms ) {
return terms.map(function( val ) {
return val.trim();
}).join( "," );
}
function addArrayPair( queryPairs, options, field, not ){
if ( options ) {
var terms = options[ field ] || options,
execution = options.execution || "and";
if ( Array.isArray( terms ) ) {
terms = mapAndJoinTerms( terms );
} else {
terms = mapAndJoinTerms( terms.split( "," ) );
}
terms = execution + "," + terms;
addPair( queryPairs, field, terms, not );
}
}
return {
queryPairs: [],
find: function( options ) {
options = options || {};
for ( var key in options ) {
if ( options.hasOwnProperty( key ) && this[ key ] ) {
if ( Array.isArray( options[ key ] ) ) {
this[ key ].apply( this, options[ key ] );
} else {
this[ key ]( options[ key ] );
}
}
}
return this;
},
author: function( name, not ) {
addPair( this.queryPairs, "author", name, not );
return this;
},
user: function( id, not ) {
addPair( this.queryPairs, "user", id, not );
return this;
},
tags: function( options, not ) {
addArrayPair( this.queryPairs, options, "tags", not );
return this;
},
tagPrefix: function( prefix, not ) {
addPair( this.queryPairs, "tagPrefix", prefix, not );
return this;
},
url: function( url, not ) {
addPair( this.queryPairs, "url", url, not );
return this;
},
contentType: function( contentType, not ) {
addPair( this.queryPairs, "contentType", contentType, not );
return this;
},
remixedFrom: function( id, not ) {
addPair( this.queryPairs, "remixedFrom", id, not );
return this;
},
id: function( ids, not ) {
if ( typeof ids === "string" ) {
addPair( this.queryPairs, "id", ids, not );
} else {
// override execution to be "or"
if ( Array.isArray( ids ) ) {
ids = {
id: ids,
execution: "or"
};
} else {
ids.execution = "or";
}
addArrayPair( this.queryPairs, ids, "id", not );
}
return this;
},
title: function( title, not ) {
addPair( this.queryPairs, "title", title, not );
return this;
},
description: function( desc, not ) {
addPair( this.queryPairs, "description", desc, not );
return this;
},
limit: function( num ) {
addPair( this.queryPairs, "limit", num );
return this;
},
page: function( num ) {
addPair( this.queryPairs, "page", num );
return this;
},
sortByField: function( field, direction ) {
var sortOpts;
if ( typeof field === "string" ) {
sortOpts = field;
sortOpts += "," + ( direction ? direction : "desc" );
addPair( this.queryPairs, "sortByField", sortOpts );
return this;
}
return this;
},
or: function() {
addPair( this.queryPairs, "or", "1" );
return this;
},
then: function( callback ) {
var querystring = this.queryPairs.join( "&" );
this.queryPairs = [];
doXHR( "GET", API_PREFIX + searchPath,
querystring,
function( err, data ) {
if ( err ) {
return callback( err );
}
if ( !data ) {
return callback( null, [], 0);
}
// Wrap resulting makes with some extra API.
var hits = data.makes;
for ( var i = 0; i < hits.length; i++ ) {
hits[ i ] = wrap( hits[ i ], options );
}
callback( null, hits, data.total );
}
);
},
create: function create( options, callback ) {
doXHR( "POST", API_PREFIX, options, callback );
return this;
},
update: function update( id, options, callback ) {
doXHR( "PUT", API_PREFIX + id, options, callback );
return this;
},
like: function like( id, maker, callback ) {
doXHR( "PUT", API_PREFIX + "like/" + id, { maker: maker }, callback );
return this;
},
unlike: function update( id, maker, callback ) {
doXHR( "PUT", API_PREFIX + "unlike/" + id, { maker: maker }, callback );
return this;
},
remove: function remove( id, callback ) {
doXHR( "DELETE", API_PREFIX + id, callback );
return this;
},
autocompleteTags: function autocompleteTags( term, size, callback ) {
if ( !callback && typeof size === "function" ) {
callback = size;
size = 10;
}
var query = "t=" + term + "&s=" + size;
doXHR( "GET", API_PREFIX + "tags", query, callback );
return this;
},
report: function report( id, maker, callback ) {
doXHR( "PUT", API_PREFIX + "report/" + id, { maker: maker }, callback );
return this;
},
cancelReport: function cancelReport( id, maker, callback ) {
doXHR( "PUT", API_PREFIX + "cancelReport/" + id, { maker: maker }, callback );
return this;
},
remixCount: function remixCount( id, options, callback ) {
options = options || {};
var from = options.from || "",
to = options.to || "",
qs = "id=" + id + "&from=" + from + "&to=" + to;
doXHR( "GET", API_PREFIX + "remixCount", qs, callback );
return this;
}
};
};
// Depending on the environment we need to export our "Make" object differently.
if ( typeof module !== 'undefined' && module.exports ) {
request = require( "request" );
hawk = require( "hawk" );
// npm install makeapi support
xhrStrategy = nodeStrategy;
module.exports = Make;
} else {
xhrStrategy = browserStrategy;
if ( typeof define === "function" && define.amd ) {
// Support for requirejs
define('../bower_components/makeapi-client/src/make-api',[],function() {
return Make;
});
} else {
// Support for include on individual pages.
window.Make = Make;
}
}
}( module ));
(function() {
var noMakesHTML = '<p class="make-no-results"><strong>Sorry!</strong> We couldnt find any makes that match your search.</p>';
var makeHTML = '' +
' <div class="make-node">' +
' <div class="make-node-inner">' +
' <a href="#" class="make-link">' +
' <div class="make-thumbnail"></div>' +
' </a>' +
' <div class="make-details">' +
' <h1>' +
' <a href="#" class="make-details-link"></a>' +
' </h1>' +
' <p class="make-meta">' +
' <img class="make-user-avatar">' +
' <span class="make-meta-author">Created by <a href="#" class="make-details-user">@flukeout</a></span>' +
' <span class="make-meta-timestamp"><span class="make-details-timestamp"></span> ago</span>' +
' <span class="make-likes">,'+
' <span class="make-likes-count"></span> like</span>' +
' </span>'+
' </p>' +
' <p class="make-description"></p>' +
' <div class="make-tags"></div>' +
' </div>' +
' <div class="button-container">' +
' <a href="#" class="make-remix">' +
' <span class="icon-remix"></span>' +
' Remix' +
' </a>' +
' </div>' +
' </div>';
var galleryElement;
var minWidth = 250, //Minimum width of a Gallery item
DEFAULT_LIMIT = 10; //Default result limit
var MakeAPIURL = "https://makeapi.webmaker.org",
MakeAPI = window.Make;
var default_profileBaseURL = "https://webmaker.org/u/",
fallbackAvatar = "https://i1.wp.com/stuff.webmaker.org/avatars/webmaker-avatar-44x44.png";
var delayer,
resizeDelay = 25;
window.onresize = function resizeTracker() {
if(delayer) {
window.clearTimeout(delayer);
}
delayer = window.setTimeout(fixHeights,resizeDelay);
};
function generateNode(make,clientConfig) {
// var stuffToKeep = {
// "thumbnail" : function(blam){
//
// }, etc...
// }
// Later iterate over the array of things to keep and run the above.!
var node = document.createElement("div"),
hidden = clientConfig.hidden || [],
avatar,
avatarSrc,
createdAt,
createdTime,
currentTime,
dateString,
days,
day,
description,
like,
likeCount,
likesWrapper,
link,
makeTags,
months,
remix,
thumb,
thumbLink,
timeDelta,
user,
userURL,
userWrapper,
years;
node.innerHTML = makeHTML;
node = node.querySelector(".make-node");
//Thumbnail Image
thumb = node.querySelector(".make-thumbnail");
if(hidden.indexOf("thumbnail") < 0) {
thumbLink = node.querySelector(".make-link");
thumbLink.setAttribute("href", make.url);
if(make.thumbnail) {
thumb.style.backgroundImage = "url(" + make.thumbnail + ")";
} else {
if(clientConfig.fallbackThumbnail) {
thumb.style.backgroundImage = "url("+clientConfig.fallbackThumbnail+")";
} else {
thumb.classList.add("default-thumbnail");
}
}
} else {
thumb.parentNode.removeChild(thumb);
}
//Title link
link = node.querySelector("h1 a");
if(hidden.indexOf("title") <0 ) {
link.setAttribute("href",make.url);
link.innerHTML = make.title;
} else {
link.parentNode.removeChild(link);
}
if(hidden.indexOf("created-by") < 0){
user = node.querySelector(".make-details-user");
user.innerHTML = make.username;
userURL = clientConfig.profileBaseURL || default_profileBaseURL;
user.setAttribute("href",userURL + make.username);
} else {
userWrapper = node.querySelector(".make-meta-author");
userWrapper.parentNode.removeChild(userWrapper);
}
//Created At
if(hidden.indexOf("created-at") < 0){
createdAt = node.querySelector(".make-details-timestamp");
createdTime = new Date(make.createdAt);
currentTime = new Date().getTime();
timeDelta = currentTime - createdTime;
day = 1000* 60 * 60 * 24;
days = Math.floor(timeDelta/day);
months = Math.floor(days/31);
years = Math.floor(months/12);
if(days > 50) {
if(months > 12) {
dateString = years + " years";
} else {
dateString = months + " months";
}
} else {
dateString = days + " days";
}
createdAt.innerHTML = dateString;
} else {
createdAt = node.querySelector(".make-meta-timestamp");
createdAt.parentNode.removeChild(createdAt);
}
//Like count
likesWrapper = node.querySelector(".make-likes");
if(hidden.indexOf("likes-count") < 0) {
likeCount = node.querySelector(".make-likes-count");
likeCount.innerHTML = make.likes.length;
if(make.likes.length == 0) {
likesWrapper.style.display = "none";
}
if(make.likes.length > 2) {
likesWrapper.innerHTML = likesWrapper.innerHTML + "s";
}
} else {
likesWrapper.parentNode.removeChild(likesWrapper);
}
//Descripiton
description = node.querySelector(".make-description");
if(hidden.indexOf("description") < 0){
description.innerHTML = make.description;
} else {
description.parentNode.removeChild(description);
}
//Remix Button
remix = node.querySelector(".make-remix");
if(hidden.indexOf("remix-button") < 0){
remix.setAttribute("href",make.remixurl);
} else {
remix.parentNode.removeChild(remix);
}
//Remix Button
like = node.querySelector(".make-like");
if(hidden.indexOf("like-button") >= 0){
like.parentNode.removeChild(like);
}
avatar = node.querySelector(".make-user-avatar");
if(hidden.indexOf("author-picture") < 0){
if(!clientConfig.fallbackAvatar) {
clientConfig.fallbackAvatar = fallbackAvatar;
}
avatarSrc = "http://www.gravatar.com/avatar/" + make.emailHash + "?s=44&d=" + encodeURIComponent(clientConfig.fallbackAvatar);
avatar.setAttribute("src", avatarSrc);
} else {
avatar.parentNode.removeChild(avatar);
}
//Generate tags
makeTags = node.querySelector(".make-tags");
if(hidden.indexOf("tags") < 0) {
for(var i = 0; i < make.tags.length; i++){
var tag = document.createElement("a");
tag.classList.add("make-tag");
tag.innerHTML = make.tags[i];
tag.setAttribute("href","https://webmaker.org/t/" + make.tags[i]);
makeTags.appendChild(tag);
makeTags.innerHTML = makeTags.innerHTML + " ";
}
} else {
makeTags.parentNode.removeChild(makeTags);
}
return node;
}
function build( rootElement, makes,clientConfig) {
makes.forEach(function(make) {
rootElement.appendChild(generateNode(make,clientConfig));
});
fixHeights();
}
function countRowItems(){
var galleryWidth = galleryElement.offsetWidth;
return Math.floor(galleryWidth/minWidth);
}
function fixHeights(){
var columnCount = countRowItems();
galleryElement.setAttribute("data-cols",columnCount);
var makeEls = galleryElement.querySelectorAll(".make-node"),
lastTop,
tallest = 0,
itemsPerRow = countRowItems(),
rowItem = 0,
currentRow = 1,
rowCount = Math.ceil(makeEls.length / itemsPerRow);
for(var i = 0; i < rowCount; i++){
tallest = 0;
//Figure out tallest Make in each row
for(var j = 0; j < itemsPerRow; j++){
var makeIndex = j + (i*itemsPerRow);
var makeEl = makeEls[makeIndex];
if(makeEl){
makeDetails = makeEl.querySelector(".make-details");
makeDetails.style.height = "";
var height = makeDetails.offsetHeight;
if (height > tallest){
tallest = height;
}
}
}
//Set each make to the tallest in that row
tallest = tallest - 50;
for(var j = 0; j < itemsPerRow; j++){
var makeIndex = j + (i*itemsPerRow);
var makeEl = makeEls[makeIndex];
if(makeEl){
makeEl.querySelector(".make-details").style.height = tallest + "px";
}
}
}
}
function MakeGallery(query, clientConfig) {
clientConfig.apiURL = MakeAPIURL;
var element = clientConfig.elementSelector;
galleryElement = document.querySelector(element);
galleryElement.innerHTML = noMakesHTML;
if(!galleryElement.classList.contains("make-gallery")) {
galleryElement.classList.add("make-gallery");
}
var self = this;
this.element = typeof element === "string" ? document.querySelector( element ) : element;
if ( !element ) {
throw new Error( "you must provide an element or selector for the gallery" );
}
if ( !clientConfig ) {
throw new Error( "you must provide a MakeAPI client Configuration object" );
}
var makeClient = new MakeAPI(clientConfig);
query.limit = query.limit ? query.limit : DEFAULT_LIMIT;
makeClient.find(query).then(function(err,makes,count) {
if (err) { throw err;}
if ( count ) {
build( self.element, makes, clientConfig );
} else {
// Add a 0 makes found thingy
}
});
return this;
}
// Depending on the environment we need to export our "Make" object differently.
if ( typeof define === "function" && define.amd ) {
// Support for requirejs
define('make-gallery',[],function() {
return MakeGallery;
});
} else {
window.MakeGallery = MakeGallery;
}
})();