Introducing Pillar Framework

Refactor of pillar-server and pillar-web into a single python package. This
simplifies the overall architecture of pillar applications.

Special thanks @sybren and @venomgfx
This commit is contained in:
2016-08-19 09:19:06 +02:00
parent a5e92e1d87
commit 2c5dc34ea2
232 changed files with 79508 additions and 2232 deletions

View File

@@ -0,0 +1,359 @@
$(document).ready(function() {
/********************
* INITIALIZATION
* *******************/
var HITS_PER_PAGE = 25;
var MAX_VALUES_PER_FACET = 30;
// DOM binding
var $inputField = $('#q');
var $hits = $('#hits');
var $stats = $('#stats');
var $facets = $('#facets');
var $pagination = $('#pagination');
// Templates binding
var hitTemplate = Hogan.compile($('#hit-template').text());
var statsTemplate = Hogan.compile($('#stats-template').text());
var facetTemplate = Hogan.compile($('#facet-template').text());
var sliderTemplate = Hogan.compile($('#slider-template').text());
var paginationTemplate = Hogan.compile($('#pagination-template').text());
// Client initialization
var algolia = algoliasearch(APPLICATION_ID, SEARCH_ONLY_API_KEY);
// Helper initialization
var params = {
hitsPerPage: HITS_PER_PAGE,
maxValuesPerFacet: MAX_VALUES_PER_FACET,
facets: $.map(FACET_CONFIG, function(facet) { return !facet.disjunctive ? facet.name : null; }),
disjunctiveFacets: $.map(FACET_CONFIG, function(facet) { return facet.disjunctive ? facet.name : null; })
};
// Setup the search helper
var helper = algoliasearchHelper(algolia, INDEX_NAME, params);
// Check if we passed hidden facets in the FACET_CONFIG
var result = $.grep(FACET_CONFIG, function(e){ return e.hidden && e.hidden == true; });
for (var i = 0; i < result.length; i++) {
var f = result[i];
helper.addFacetRefinement(f.name, f.value);
}
// Input binding
$inputField.on('keyup change', function() {
var query = $inputField.val();
toggleIconEmptyInput(!query.trim());
helper.setQuery(query).search();
}).focus();
// AlgoliaHelper events
helper.on('change', function(state) {
setURLParams(state);
});
helper.on('error', function(error) {
console.log(error);
});
helper.on('result', function(content, state) {
renderStats(content);
renderHits(content);
renderFacets(content, state);
renderPagination(content);
bindSearchObjects();
renderFirstHit($(hits).children('.search-hit:first'));
});
/************
* SEARCH
* ***********/
function renderFirstHit(firstHit) {
firstHit.addClass('active');
firstHit.find('#search-loading').addClass('active');
var getNode = setTimeout(function(){
$.get('/nodes/' + firstHit.attr('data-hit-id') + '/view', function(dataHtml){
$('#search-hit-container').html(dataHtml);
})
.done(function(){
$('.search-loading').removeClass('active');
$('#search-error').hide();
$('#search-hit-container').show();
clearTimeout(getNode);
})
.fail(function(data){
$('.search-loading').removeClass('active');
$('#search-hit-container').hide();
$('#search-error').show().html('Houston!\n\n' + data.status + ' ' + data.statusText);
});
}, 1000);
};
// Initial search
initWithUrlParams();
helper.search();
function convertTimestamp(timestamp) {
var d = new Date(timestamp * 1000), // Convert the passed timestamp to milliseconds
yyyy = d.getFullYear(),
mm = ('0' + (d.getMonth() + 1)).slice(-2), // Months are zero based. Add leading 0.
dd = ('0' + d.getDate()).slice(-2), // Add leading 0.
time;
time = dd + '/' + mm + '/' + yyyy;
return time;
}
function renderStats(content) {
var stats = {
nbHits: numberWithDelimiter(content.nbHits),
processingTimeMS: content.processingTimeMS,
nbHits_plural: content.nbHits !== 1
};
$stats.html(statsTemplate.render(stats));
}
function renderHits(content) {
var hitsHtml = '';
for (var i = 0; i < content.hits.length; ++i) {
// console.log(content.hits[i]);
var created = content.hits[i]['created'];
if (created) {
content.hits[i]['created'] = convertTimestamp(created);
}
var updated = content.hits[i]['updated'];
if (updated) {
content.hits[i]['updated'] = convertTimestamp(updated);
}
hitsHtml += hitTemplate.render(content.hits[i]);
}
if (content.hits.length === 0) hitsHtml = '<p id="no-hits">We didn\'t find any items. Try searching something else.</p>';
$hits.html(hitsHtml);
}
function renderFacets(content, state) {
// If no results
if (content.hits.length === 0) {
$facets.empty();
return;
}
// Process facets
var facets = [];
for (var facetIndex = 0; facetIndex < FACET_CONFIG.length; ++facetIndex) {
var facetParams = FACET_CONFIG[facetIndex];
if (facetParams.hidden) {
continue
}
var facetResult = content.getFacetByName(facetParams.name);
if (facetResult) {
var facetContent = {};
facetContent.facet = facetParams.name;
facetContent.title = facetParams.title;
facetContent.type = facetParams.type;
if (facetParams.type === 'slider') {
// if the facet is a slider
facetContent.min = facetResult.stats.min;
facetContent.max = facetResult.stats.max;
var valueMin = state.getNumericRefinement(facetParams.name, '>=') || facetResult.stats.min;
var valueMax = state.getNumericRefinement(facetParams.name, '<=') || facetResult.stats.max;
valueMin = Math.min(facetContent.max, Math.max(facetContent.min, valueMin));
valueMax = Math.min(facetContent.max, Math.max(facetContent.min, valueMax));
facetContent.values = [valueMin, valueMax];
} else {
// format and sort the facet values
var values = [];
for (var v in facetResult.data) {
var label = '';
if (v === 'true') { label = 'Yes'; }
else if (v === 'false') { label = 'No'; }
// Remove any underscore from the value
else { label = v.replace(/_/g," "); }
values.push({ label: label, value: v, count: facetResult.data[v], refined: helper.isRefined(facetParams.name, v) });
}
var sortFunction = facetParams.sortFunction || sortByCountDesc;
if (facetParams.topListIfRefined) sortFunction = sortByRefined(sortFunction);
values.sort(sortFunction);
facetContent.values = values.slice(0, 10);
facetContent.has_other_values = values.length > 10;
facetContent.other_values = values.slice(10);
facetContent.disjunctive = facetParams.disjunctive;
}
facets.push(facetContent);
}
}
// Display facets
var facetsHtml = '';
for (var indexFacet = 0; indexFacet < facets.length; ++indexFacet) {
var facet = facets[indexFacet];
if (facet.type && facet.type === 'slider') facetsHtml += sliderTemplate.render(facet);
else facetsHtml += facetTemplate.render(facet);
}
$facets.html(facetsHtml);
}
function renderPagination(content) {
// If no results
if (content.hits.length === 0) {
$pagination.empty();
return;
}
var maxPages = 2;
// Process pagination
var pages = [];
if (content.page > maxPages) {
pages.push({ current: false, number: 1 });
// They don't really add much...
// pages.push({ current: false, number: '...', disabled: true });
}
for (var p = content.page - maxPages; p < content.page + maxPages; ++p) {
if (p < 0 || p >= content.nbPages) {
continue;
}
pages.push({ current: content.page === p, number: (p + 1) });
}
if (content.page + maxPages < content.nbPages) {
// They don't really add much...
// pages.push({ current: false, number: '...', disabled: true });
pages.push({ current: false, number: content.nbPages });
}
var pagination = {
pages: pages,
prev_page: (content.page > 0 ? content.page : false),
next_page: (content.page + 1 < content.nbPages ? content.page + 2 : false)
};
// Display pagination
$pagination.html(paginationTemplate.render(pagination));
}
// Event bindings
function bindSearchObjects() {
// Slider binding
// $('#customerReviewCount-slider').slider().on('slideStop', function(ev) {
// helper.addNumericRefinement('customerReviewCount', '>=', ev.value[0]).search();
// helper.addNumericRefinement('customerReviewCount', '<=', ev.value[1]).search();
// });
// Pimp checkboxes
// $('input[type="checkbox"]').checkbox();
}
// Click binding
$(document).on('click','.show-more, .show-less',function(e) {
e.preventDefault();
$(this).closest('ul').find('.show-more').toggle();
$(this).closest('ul').find('.show-less').toggle();
return false;
});
$(document).on('click','.toggleRefine',function() {
helper.toggleRefine($(this).data('facet'), $(this).data('value')).search();
return false;
});
$(document).on('click','.gotoPage',function() {
helper.setCurrentPage(+$(this).data('page') - 1).search();
$("html, body").animate({scrollTop:0}, '500', 'swing');
return false;
});
$(document).on('click','.sortBy',function() {
$(this).closest('.btn-group').find('.sort-by').text($(this).text());
helper.setIndex(INDEX_NAME + $(this).data('index-suffix')).search();
return false;
});
$(document).on('click','#input-loop',function() {
$inputField.val('').change();
});
// Dynamic styles
$('#facets').on("mouseenter mouseleave", ".button-checkbox", function(e){
$(this).parent().find('.facet_link').toggleClass("hover");
});
$('#facets').on("mouseenter mouseleave", ".facet_link", function(e){
$(this).parent().find('.button-checkbox button.btn').toggleClass("hover");
});
/************
* HELPERS
* ***********/
function toggleIconEmptyInput(isEmpty) {
if(isEmpty) {
$('#input-loop').addClass('glyphicon-loop');
$('#input-loop').removeClass('glyphicon-remove');
}
else {
$('#input-loop').removeClass('glyphicon-loop');
$('#input-loop').addClass('glyphicon-remove');
}
}
function numberWithDelimiter(number, delimiter) {
number = number + '';
delimiter = delimiter || ',';
var split = number.split('.');
split[0] = split[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + delimiter);
return split.join('.');
}
var sortByCountDesc = function sortByCountDesc (a, b) { return b.count - a.count; };
var sortByName = function sortByName (a, b) {
return a.value.localeCompare(b.value);
};
var sortByRefined = function sortByRefined (sortFunction) {
return function (a, b) {
if (a.refined !== b.refined) {
if (a.refined) return -1;
if (b.refined) return 1;
}
return sortFunction(a, b);
};
};
function initWithUrlParams() {
var sPageURL = location.hash;
if (!sPageURL || sPageURL.length === 0) { return true; }
var sURLVariables = sPageURL.split('&');
if (!sURLVariables || sURLVariables.length === 0) { return true; }
var query = decodeURIComponent(sURLVariables[0].split('=')[1]);
$inputField.val(query);
helper.setQuery(query);
for (var i = 2; i < sURLVariables.length; i++) {
var sParameterName = sURLVariables[i].split('=');
var facet = decodeURIComponent(sParameterName[0]);
var value = decodeURIComponent(sParameterName[1]);
helper.toggleRefine(facet, value, false);
}
// Page has to be set in the end to avoid being overwritten
var page = decodeURIComponent(sURLVariables[1].split('=')[1])-1;
helper.setCurrentPage(page);
}
function setURLParams(state) {
var urlParams = '#';
var currentQuery = state.query;
urlParams += 'q=' + encodeURIComponent(currentQuery);
var currentPage = state.page+1;
urlParams += '&page=' + currentPage;
for (var facetRefine in state.facetsRefinements) {
urlParams += '&' + encodeURIComponent(facetRefine) + '=' + encodeURIComponent(state.facetsRefinements[facetRefine]);
}
for (var disjunctiveFacetrefine in state.disjunctiveFacetsRefinements) {
for (var value in state.disjunctiveFacetsRefinements[disjunctiveFacetrefine]) {
urlParams += '&' + encodeURIComponent(disjunctiveFacetrefine) + '=' + encodeURIComponent(state.disjunctiveFacetsRefinements[disjunctiveFacetrefine][value]);
}
}
location.replace(urlParams);
}
});

162
src/scripts/file_upload.js Normal file
View File

@@ -0,0 +1,162 @@
function deleteFile(fileField, newFileId) {
if (newFileId) {
fileField.val(newFileId);
} else {
fileField.val('');
}
}
var current_file_uploads = 0;
function on_file_upload_activated() {
if (current_file_uploads == 0) {
// Disable the save buttons.
$('.button-save')
.addClass('disabled')
.find('a').html('<i class="pi-spin spin"></i> Uploading...');
}
current_file_uploads++;
}
function on_file_upload_finished() {
current_file_uploads = Math.max(0, current_file_uploads-1);
if (current_file_uploads == 0) {
// Restore the save buttons.
$('.button-save')
.removeClass('disabled')
.find('a').html('<i class="pi-check"></i> Save Changes');
}
}
function setup_file_uploader(index, upload_element) {
var $upload_element = $(upload_element);
var container = $upload_element.parent().parent();
var progress_bar = container.find('div.form-upload-progress-bar');
function set_progress_bar(progress, html_class) {
progress_bar.css({
'width': progress + '%',
'display': progress == 0 ? 'none' : 'block'});
progress_bar.removeClass('progress-error progress-uploading progress-processing');
if (!!html_class) progress_bar.addClass(html_class);
}
$upload_element.fileupload({
dataType: 'json',
replaceFileInput: false,
dropZone: container,
formData: {},
beforeSend: function (xhr, data) {
var token = this.fileInput.attr('data-token');
xhr.setRequestHeader('Authorization', 'basic ' + btoa(token + ':'));
statusBarSet('info', 'Uploading File...', 'pi-upload-cloud');
// console.log('Uploading from', upload_element, upload_element.value);
// Clear thumbnail & progress bar.
container.find('.preview-thumbnail').hide();
set_progress_bar(0);
$('body').trigger('file-upload:activated');
},
add: function (e, data) {
var uploadErrors = [];
// Load regex if available (like /^image\/(gif|jpe?g|png)$/i;)
var acceptFileTypes = new RegExp($(this).data('file-format'));
if (data.originalFiles[0]['type'].length && !acceptFileTypes.test(data.originalFiles[0]['type'])) {
uploadErrors.push('Not an accepted file type');
}
// Limit upload size to 1GB
if (data.originalFiles[0]['size'] && data.originalFiles[0]['size'] > 1262485504) {
uploadErrors.push('Filesize is too big');
}
if (uploadErrors.length > 0) {
$(this).parent().parent().addClass('error');
$(this).after(uploadErrors.join("\n"));
} else {
$(this).parent().parent().removeClass('error');
data.submit();
}
},
progressall: function (e, data) {
// Update progressbar during upload
var progress = parseInt(data.loaded / data.total * 100, 10);
// console.log('Uploading', upload_element.value, ': ', progress, '%');
set_progress_bar(Math.max(progress, 2),
progress > 99.9 ? 'progress-processing' : 'progress-uploading'
);
},
done: function (e, data) {
if (data.result.status !== 'ok') {
if (console)
console.log('FIXME, do error handling for non-ok status', data.result);
return;
}
// Ensure the form refers to the correct Pillar file ID.
var pillar_file_id = data.result.file_id;
var $file_id_field = $('#' + $(this).attr('data-field-name'));
if ($file_id_field.val()) {
deleteFile($file_id_field, pillar_file_id);
}
$file_id_field.val(pillar_file_id);
// Ugly workaround: If the asset has the default name, name it as the file
if ($('.form-group.name .form-control').val() == 'New asset') {
var filename = data.files[0].name;
$('.form-group.name .form-control').val(filename);
$('.node-edit-title').html(filename);
}
statusBarSet('success', 'File Uploaded Successfully', 'pi-check');
set_progress_bar(100);
$('body').trigger('file-upload:finished');
},
fail: function (jqXHR, textStatus, errorThrown) {
if (console) {
console.log(textStatus, 'Upload error: ' + errorThrown);
}
statusBarSet(textStatus, 'Upload error: ' + errorThrown, 'pi-attention', 8000);
set_progress_bar(100, 'progress-error');
$('body').trigger('file-upload:finished');
}
});
}
$(function () {
// $('.file_delete').click(function(e){
$('body').unbind('click')
.on('click', '.file_delete', function(e) {
e.preventDefault();
var field_name = '#' + $(this).data('field-name');
var file_field = $(field_name);
deleteFile(file_field);
$(this).parent().parent().hide();
$(this).parent().parent().prev().hide();
})
.on('file-upload:activated', on_file_upload_activated)
.on('file-upload:finished', on_file_upload_finished)
;
function inject_project_id_into_url(index, element) {
// console.log('Injecting ', ProjectUtils.projectId(), ' into ', element);
var url = element.getAttribute('data-url');
url = url.replace('{project_id}', ProjectUtils.projectId());
element.setAttribute('data-url', url);
// console.log('The new element is', element);
}
$('.fileupload')
.each(inject_project_id_into_url)
.each(setup_file_uploader)
;
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,114 @@
(function () {
var output, Converter;
if (typeof exports === "object" && typeof require === "function") { // we're in a CommonJS (e.g. Node.js) module
output = exports;
Converter = require("./Markdown.Converter").Converter;
} else {
output = window.Markdown;
Converter = output.Converter;
}
output.getSanitizingConverter = function () {
var converter = new Converter();
converter.hooks.chain("postConversion", sanitizeHtml);
converter.hooks.chain("postConversion", balanceTags);
return converter;
}
function sanitizeHtml(html) {
return html.replace(/<[^>]*>?/gi, sanitizeTag);
}
// (tags that can be opened/closed) | (tags that stand alone)
var basic_tag_whitelist = /^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|iframe|kbd|li|ol(?: start="\d+")?|p|pre|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i;
// <a href="url..." optional title>|</a>
var a_white = /^(<a\shref="((https?|ftp):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)*[\]$]+"(\stitle="[^"<>]+")?(\sclass="[^"<>]+")?\s?>|<\/a>)$/i;
// Cloud custom: Allow iframe embed from YouTube, Vimeo and SoundCloud
var iframe_youtube = /^(<iframe(\swidth="\d{1,3}")?(\sheight="\d{1,3}")\ssrc="((https?):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)*[\]$]+"(\sframeborder="\d{1,3}")?(\sallowfullscreen)\s?>|<\/iframe>)$/i;
var iframe_vimeo = /^(<iframe(\ssrc="((https?):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)*[\]$]+"?\swidth="\d{1,3}")?(\sheight="\d{1,3}")?(\sframeborder="\d{1,3}")?(\swebkitallowfullscreen)\s?(\smozallowfullscreen)\s?(\sallowfullscreen)\s?>|<\/iframe>)$/i;
var iframe_soundcloud = /^(<iframe(\swidth="\d{1,3}\%")?(\sheight="\d{1,3}")?(\sscrolling="(?:yes|no)")?(\sframeborder="(?:yes|no)")\ssrc="((https?):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)*[\]$]+"\s?>|<\/iframe>)$/i;
// <img src="url..." optional width optional height optional alt optional title
var img_white = /^(<img\ssrc="(https?:\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)*[\]$]+"(\swidth="\d{1,3}")?(\sheight="\d{1,3}")?(\salt="[^"<>]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i;
function sanitizeTag(tag) {
if (tag.match(basic_tag_whitelist) || tag.match(a_white) || tag.match(img_white) || tag.match(iframe_youtube) || tag.match(iframe_vimeo) || tag.match(iframe_soundcloud)) {
return tag;
} else {
return "";
}
}
/// <summary>
/// attempt to balance HTML tags in the html string
/// by removing any unmatched opening or closing tags
/// IMPORTANT: we *assume* HTML has *already* been
/// sanitized and is safe/sane before balancing!
///
/// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593
/// </summary>
function balanceTags(html) {
if (html == "")
return "";
var re = /<\/?\w+[^>]*(\s|$|>)/g;
// convert everything to lower case; this makes
// our case insensitive comparisons easier
var tags = html.toLowerCase().match(re);
// no HTML tags present? nothing to do; exit now
var tagcount = (tags || []).length;
if (tagcount == 0)
return html;
var tagname, tag;
var ignoredtags = "<p><img><br><li><hr>";
var match;
var tagpaired = [];
var tagremove = [];
var needsRemoval = false;
// loop through matched tags in forward order
for (var ctag = 0; ctag < tagcount; ctag++) {
tagname = tags[ctag].replace(/<\/?(\w+).*/, "$1");
// skip any already paired tags
// and skip tags in our ignore list; assume they're self-closed
if (tagpaired[ctag] || ignoredtags.search("<" + tagname + ">") > -1)
continue;
tag = tags[ctag];
match = -1;
if (!/^<\//.test(tag)) {
// this is an opening tag
// search forwards (next tags), look for closing tags
for (var ntag = ctag + 1; ntag < tagcount; ntag++) {
if (!tagpaired[ntag] && tags[ntag] == "</" + tagname + ">") {
match = ntag;
break;
}
}
}
if (match == -1)
needsRemoval = tagremove[ctag] = true; // mark for removal
else
tagpaired[match] = true; // mark paired
}
if (!needsRemoval)
return html;
// delete all orphaned tags from the string
var ctag = 0;
html = html.replace(re, function (match) {
var res = tagremove[ctag] ? "" : match;
ctag++;
return res;
});
return html;
}
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,874 @@
(function () {
// A quick way to make sure we're only keeping span-level tags when we need to.
// This isn't supposed to be foolproof. It's just a quick way to make sure we
// keep all span-level tags returned by a pagedown converter. It should allow
// all span-level tags through, with or without attributes.
var inlineTags = new RegExp(['^(<\\/?(a|abbr|acronym|applet|area|b|basefont|',
'bdo|big|button|cite|code|del|dfn|em|figcaption|',
'font|i|iframe|img|input|ins|kbd|label|map|',
'mark|meter|object|param|progress|q|ruby|rp|rt|s|',
'samp|script|select|small|span|strike|strong|',
'sub|sup|textarea|time|tt|u|var|wbr)[^>]*>|',
'<(br)\\s?\\/?>)$'].join(''), 'i');
/******************************************************************
* Utility Functions *
*****************************************************************/
// patch for ie7
if (!Array.indexOf) {
Array.prototype.indexOf = function(obj) {
for (var i = 0; i < this.length; i++) {
if (this[i] == obj) {
return i;
}
}
return -1;
};
}
function trim(str) {
return str.replace(/^\s+|\s+$/g, '');
}
function rtrim(str) {
return str.replace(/\s+$/g, '');
}
// Remove one level of indentation from text. Indent is 4 spaces.
function outdent(text) {
return text.replace(new RegExp('^(\\t|[ ]{1,4})', 'gm'), '');
}
function contains(str, substr) {
return str.indexOf(substr) != -1;
}
// Sanitize html, removing tags that aren't in the whitelist
function sanitizeHtml(html, whitelist) {
return html.replace(/<[^>]*>?/gi, function(tag) {
return tag.match(whitelist) ? tag : '';
});
}
// Merge two arrays, keeping only unique elements.
function union(x, y) {
var obj = {};
for (var i = 0; i < x.length; i++)
obj[x[i]] = x[i];
for (i = 0; i < y.length; i++)
obj[y[i]] = y[i];
var res = [];
for (var k in obj) {
if (obj.hasOwnProperty(k))
res.push(obj[k]);
}
return res;
}
// JS regexes don't support \A or \Z, so we add sentinels, as Pagedown
// does. In this case, we add the ascii codes for start of text (STX) and
// end of text (ETX), an idea borrowed from:
// https://github.com/tanakahisateru/js-markdown-extra
function addAnchors(text) {
if(text.charAt(0) != '\x02')
text = '\x02' + text;
if(text.charAt(text.length - 1) != '\x03')
text = text + '\x03';
return text;
}
// Remove STX and ETX sentinels.
function removeAnchors(text) {
if(text.charAt(0) == '\x02')
text = text.substr(1);
if(text.charAt(text.length - 1) == '\x03')
text = text.substr(0, text.length - 1);
return text;
}
// Convert markdown within an element, retaining only span-level tags
function convertSpans(text, extra) {
return sanitizeHtml(convertAll(text, extra), inlineTags);
}
// Convert internal markdown using the stock pagedown converter
function convertAll(text, extra) {
var result = extra.blockGamutHookCallback(text);
// We need to perform these operations since we skip the steps in the converter
result = unescapeSpecialChars(result);
result = result.replace(/~D/g, "$$").replace(/~T/g, "~");
result = extra.previousPostConversion(result);
return result;
}
// Convert escaped special characters
function processEscapesStep1(text) {
// Markdown extra adds two escapable characters, `:` and `|`
return text.replace(/\\\|/g, '~I').replace(/\\:/g, '~i');
}
function processEscapesStep2(text) {
return text.replace(/~I/g, '|').replace(/~i/g, ':');
}
// Duplicated from PageDown converter
function unescapeSpecialChars(text) {
// Swap back in all the special characters we've hidden.
text = text.replace(/~E(\d+)E/g, function(wholeMatch, m1) {
var charCodeToReplace = parseInt(m1);
return String.fromCharCode(charCodeToReplace);
});
return text;
}
function slugify(text) {
return text.toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
}
/*****************************************************************************
* Markdown.Extra *
****************************************************************************/
Markdown.Extra = function() {
// For converting internal markdown (in tables for instance).
// This is necessary since these methods are meant to be called as
// preConversion hooks, and the Markdown converter passed to init()
// won't convert any markdown contained in the html tags we return.
this.converter = null;
// Stores html blocks we generate in hooks so that
// they're not destroyed if the user is using a sanitizing converter
this.hashBlocks = [];
// Stores footnotes
this.footnotes = {};
this.usedFootnotes = [];
// Special attribute blocks for fenced code blocks and headers enabled.
this.attributeBlocks = false;
// Fenced code block options
this.googleCodePrettify = false;
this.highlightJs = false;
// Table options
this.tableClass = '';
this.tabWidth = 4;
};
Markdown.Extra.init = function(converter, options) {
// Each call to init creates a new instance of Markdown.Extra so it's
// safe to have multiple converters, with different options, on a single page
var extra = new Markdown.Extra();
var postNormalizationTransformations = [];
var preBlockGamutTransformations = [];
var postSpanGamutTransformations = [];
var postConversionTransformations = ["unHashExtraBlocks"];
options = options || {};
options.extensions = options.extensions || ["all"];
if (contains(options.extensions, "all")) {
options.extensions = ["tables", "fenced_code_gfm", "def_list", "attr_list", "footnotes", "smartypants", "strikethrough", "newlines"];
}
preBlockGamutTransformations.push("wrapHeaders");
if (contains(options.extensions, "attr_list")) {
postNormalizationTransformations.push("hashFcbAttributeBlocks");
preBlockGamutTransformations.push("hashHeaderAttributeBlocks");
postConversionTransformations.push("applyAttributeBlocks");
extra.attributeBlocks = true;
}
if (contains(options.extensions, "fenced_code_gfm")) {
// This step will convert fcb inside list items and blockquotes
preBlockGamutTransformations.push("fencedCodeBlocks");
// This extra step is to prevent html blocks hashing and link definition/footnotes stripping inside fcb
postNormalizationTransformations.push("fencedCodeBlocks");
}
if (contains(options.extensions, "tables")) {
preBlockGamutTransformations.push("tables");
}
if (contains(options.extensions, "def_list")) {
preBlockGamutTransformations.push("definitionLists");
}
if (contains(options.extensions, "footnotes")) {
postNormalizationTransformations.push("stripFootnoteDefinitions");
preBlockGamutTransformations.push("doFootnotes");
postConversionTransformations.push("printFootnotes");
}
if (contains(options.extensions, "smartypants")) {
postConversionTransformations.push("runSmartyPants");
}
if (contains(options.extensions, "strikethrough")) {
postSpanGamutTransformations.push("strikethrough");
}
if (contains(options.extensions, "newlines")) {
postSpanGamutTransformations.push("newlines");
}
converter.hooks.chain("postNormalization", function(text) {
return extra.doTransform(postNormalizationTransformations, text) + '\n';
});
converter.hooks.chain("preBlockGamut", function(text, blockGamutHookCallback) {
// Keep a reference to the block gamut callback to run recursively
extra.blockGamutHookCallback = blockGamutHookCallback;
text = processEscapesStep1(text);
text = extra.doTransform(preBlockGamutTransformations, text) + '\n';
text = processEscapesStep2(text);
return text;
});
converter.hooks.chain("postSpanGamut", function(text) {
return extra.doTransform(postSpanGamutTransformations, text);
});
// Keep a reference to the hook chain running before doPostConversion to apply on hashed extra blocks
extra.previousPostConversion = converter.hooks.postConversion;
converter.hooks.chain("postConversion", function(text) {
text = extra.doTransform(postConversionTransformations, text);
// Clear state vars that may use unnecessary memory
extra.hashBlocks = [];
extra.footnotes = {};
extra.usedFootnotes = [];
return text;
});
if ("highlighter" in options) {
extra.googleCodePrettify = options.highlighter === 'prettify';
extra.highlightJs = options.highlighter === 'highlight';
}
if ("table_class" in options) {
extra.tableClass = options.table_class;
}
extra.converter = converter;
// Caller usually won't need this, but it's handy for testing.
return extra;
};
// Do transformations
Markdown.Extra.prototype.doTransform = function(transformations, text) {
for(var i = 0; i < transformations.length; i++)
text = this[transformations[i]](text);
return text;
};
// Return a placeholder containing a key, which is the block's index in the
// hashBlocks array. We wrap our output in a <p> tag here so Pagedown won't.
Markdown.Extra.prototype.hashExtraBlock = function(block) {
return '\n<p>~X' + (this.hashBlocks.push(block) - 1) + 'X</p>\n';
};
Markdown.Extra.prototype.hashExtraInline = function(block) {
return '~X' + (this.hashBlocks.push(block) - 1) + 'X';
};
// Replace placeholder blocks in `text` with their corresponding
// html blocks in the hashBlocks array.
Markdown.Extra.prototype.unHashExtraBlocks = function(text) {
var self = this;
function recursiveUnHash() {
var hasHash = false;
text = text.replace(/(?:<p>)?~X(\d+)X(?:<\/p>)?/g, function(wholeMatch, m1) {
hasHash = true;
var key = parseInt(m1, 10);
return self.hashBlocks[key];
});
if(hasHash === true) {
recursiveUnHash();
}
}
recursiveUnHash();
return text;
};
// Wrap headers to make sure they won't be in def lists
Markdown.Extra.prototype.wrapHeaders = function(text) {
function wrap(text) {
return '\n' + text + '\n';
}
text = text.replace(/^.+[ \t]*\n=+[ \t]*\n+/gm, wrap);
text = text.replace(/^.+[ \t]*\n-+[ \t]*\n+/gm, wrap);
text = text.replace(/^\#{1,6}[ \t]*.+?[ \t]*\#*\n+/gm, wrap);
return text;
};
/******************************************************************
* Attribute Blocks *
*****************************************************************/
// TODO: use sentinels. Should we just add/remove them in doConversion?
// TODO: better matches for id / class attributes
var attrBlock = "\\{[ \\t]*((?:[#.][-_:a-zA-Z0-9]+[ \\t]*)+)\\}";
var hdrAttributesA = new RegExp("^(#{1,6}.*#{0,6})[ \\t]+" + attrBlock + "[ \\t]*(?:\\n|0x03)", "gm");
var hdrAttributesB = new RegExp("^(.*)[ \\t]+" + attrBlock + "[ \\t]*\\n" +
"(?=[\\-|=]+\\s*(?:\\n|0x03))", "gm"); // underline lookahead
var fcbAttributes = new RegExp("^(```[^`\\n]*)[ \\t]+" + attrBlock + "[ \\t]*\\n" +
"(?=([\\s\\S]*?)\\n```[ \\t]*(\\n|0x03))", "gm");
// Extract headers attribute blocks, move them above the element they will be
// applied to, and hash them for later.
Markdown.Extra.prototype.hashHeaderAttributeBlocks = function(text) {
var self = this;
function attributeCallback(wholeMatch, pre, attr) {
return '<p>~XX' + (self.hashBlocks.push(attr) - 1) + 'XX</p>\n' + pre + "\n";
}
text = text.replace(hdrAttributesA, attributeCallback); // ## headers
text = text.replace(hdrAttributesB, attributeCallback); // underline headers
return text;
};
// Extract FCB attribute blocks, move them above the element they will be
// applied to, and hash them for later.
Markdown.Extra.prototype.hashFcbAttributeBlocks = function(text) {
// TODO: use sentinels. Should we just add/remove them in doConversion?
// TODO: better matches for id / class attributes
var self = this;
function attributeCallback(wholeMatch, pre, attr) {
return '<p>~XX' + (self.hashBlocks.push(attr) - 1) + 'XX</p>\n' + pre + "\n";
}
return text.replace(fcbAttributes, attributeCallback);
};
Markdown.Extra.prototype.applyAttributeBlocks = function(text) {
var self = this;
var blockRe = new RegExp('<p>~XX(\\d+)XX</p>[\\s]*' +
'(?:<(h[1-6]|pre)(?: +class="(\\S+)")?(>[\\s\\S]*?</\\2>))', "gm");
text = text.replace(blockRe, function(wholeMatch, k, tag, cls, rest) {
if (!tag) // no following header or fenced code block.
return '';
// get attributes list from hash
var key = parseInt(k, 10);
var attributes = self.hashBlocks[key];
// get id
var id = attributes.match(/#[^\s#.]+/g) || [];
var idStr = id[0] ? ' id="' + id[0].substr(1, id[0].length - 1) + '"' : '';
// get classes and merge with existing classes
var classes = attributes.match(/\.[^\s#.]+/g) || [];
for (var i = 0; i < classes.length; i++) // Remove leading dot
classes[i] = classes[i].substr(1, classes[i].length - 1);
var classStr = '';
if (cls)
classes = union(classes, [cls]);
if (classes.length > 0)
classStr = ' class="' + classes.join(' ') + '"';
return "<" + tag + idStr + classStr + rest;
});
return text;
};
/******************************************************************
* Tables *
*****************************************************************/
// Find and convert Markdown Extra tables into html.
Markdown.Extra.prototype.tables = function(text) {
var self = this;
var leadingPipe = new RegExp(
['^' ,
'[ ]{0,3}' , // Allowed whitespace
'[|]' , // Initial pipe
'(.+)\\n' , // $1: Header Row
'[ ]{0,3}' , // Allowed whitespace
'[|]([ ]*[-:]+[-| :]*)\\n' , // $2: Separator
'(' , // $3: Table Body
'(?:[ ]*[|].*\\n?)*' , // Table rows
')',
'(?:\\n|$)' // Stop at final newline
].join(''),
'gm'
);
var noLeadingPipe = new RegExp(
['^' ,
'[ ]{0,3}' , // Allowed whitespace
'(\\S.*[|].*)\\n' , // $1: Header Row
'[ ]{0,3}' , // Allowed whitespace
'([-:]+[ ]*[|][-| :]*)\\n' , // $2: Separator
'(' , // $3: Table Body
'(?:.*[|].*\\n?)*' , // Table rows
')' ,
'(?:\\n|$)' // Stop at final newline
].join(''),
'gm'
);
text = text.replace(leadingPipe, doTable);
text = text.replace(noLeadingPipe, doTable);
// $1 = header, $2 = separator, $3 = body
function doTable(match, header, separator, body, offset, string) {
// remove any leading pipes and whitespace
header = header.replace(/^ *[|]/m, '');
separator = separator.replace(/^ *[|]/m, '');
body = body.replace(/^ *[|]/gm, '');
// remove trailing pipes and whitespace
header = header.replace(/[|] *$/m, '');
separator = separator.replace(/[|] *$/m, '');
body = body.replace(/[|] *$/gm, '');
// determine column alignments
var alignspecs = separator.split(/ *[|] */);
var align = [];
for (var i = 0; i < alignspecs.length; i++) {
var spec = alignspecs[i];
if (spec.match(/^ *-+: *$/m))
align[i] = ' align="right"';
else if (spec.match(/^ *:-+: *$/m))
align[i] = ' align="center"';
else if (spec.match(/^ *:-+ *$/m))
align[i] = ' align="left"';
else align[i] = '';
}
// TODO: parse spans in header and rows before splitting, so that pipes
// inside of tags are not interpreted as separators
var headers = header.split(/ *[|] */);
var colCount = headers.length;
// build html
var cls = self.tableClass ? ' class="' + self.tableClass + '"' : '';
var html = ['<table', cls, '>\n', '<thead>\n', '<tr>\n'].join('');
// build column headers.
for (i = 0; i < colCount; i++) {
var headerHtml = convertSpans(trim(headers[i]), self);
html += [" <th", align[i], ">", headerHtml, "</th>\n"].join('');
}
html += "</tr>\n</thead>\n";
// build rows
var rows = body.split('\n');
for (i = 0; i < rows.length; i++) {
if (rows[i].match(/^\s*$/)) // can apply to final row
continue;
// ensure number of rowCells matches colCount
var rowCells = rows[i].split(/ *[|] */);
var lenDiff = colCount - rowCells.length;
for (var j = 0; j < lenDiff; j++)
rowCells.push('');
html += "<tr>\n";
for (j = 0; j < colCount; j++) {
var colHtml = convertSpans(trim(rowCells[j]), self);
html += [" <td", align[j], ">", colHtml, "</td>\n"].join('');
}
html += "</tr>\n";
}
html += "</table>\n";
// replace html with placeholder until postConversion step
return self.hashExtraBlock(html);
}
return text;
};
/******************************************************************
* Footnotes *
*****************************************************************/
// Strip footnote, store in hashes.
Markdown.Extra.prototype.stripFootnoteDefinitions = function(text) {
var self = this;
text = text.replace(
/\n[ ]{0,3}\[\^(.+?)\]\:[ \t]*\n?([\s\S]*?)\n{1,2}((?=\n[ ]{0,3}\S)|$)/g,
function(wholeMatch, m1, m2) {
m1 = slugify(m1);
m2 += "\n";
m2 = m2.replace(/^[ ]{0,3}/g, "");
self.footnotes[m1] = m2;
return "\n";
});
return text;
};
// Find and convert footnotes references.
Markdown.Extra.prototype.doFootnotes = function(text) {
var self = this;
if(self.isConvertingFootnote === true) {
return text;
}
var footnoteCounter = 0;
text = text.replace(/\[\^(.+?)\]/g, function(wholeMatch, m1) {
var id = slugify(m1);
var footnote = self.footnotes[id];
if (footnote === undefined) {
return wholeMatch;
}
footnoteCounter++;
self.usedFootnotes.push(id);
var html = '<a href="#fn:' + id + '" id="fnref:' + id
+ '" title="See footnote" class="footnote">' + footnoteCounter
+ '</a>';
return self.hashExtraInline(html);
});
return text;
};
// Print footnotes at the end of the document
Markdown.Extra.prototype.printFootnotes = function(text) {
var self = this;
if (self.usedFootnotes.length === 0) {
return text;
}
text += '\n\n<div class="footnotes">\n<hr>\n<ol>\n\n';
for(var i=0; i<self.usedFootnotes.length; i++) {
var id = self.usedFootnotes[i];
var footnote = self.footnotes[id];
self.isConvertingFootnote = true;
var formattedfootnote = convertSpans(footnote, self);
delete self.isConvertingFootnote;
text += '<li id="fn:'
+ id
+ '">'
+ formattedfootnote
+ ' <a href="#fnref:'
+ id
+ '" title="Return to article" class="reversefootnote">&#8617;</a></li>\n\n';
}
text += '</ol>\n</div>';
return text;
};
/******************************************************************
* Fenced Code Blocks (gfm) *
******************************************************************/
// Find and convert gfm-inspired fenced code blocks into html.
Markdown.Extra.prototype.fencedCodeBlocks = function(text) {
function encodeCode(code) {
code = code.replace(/&/g, "&amp;");
code = code.replace(/</g, "&lt;");
code = code.replace(/>/g, "&gt;");
// These were escaped by PageDown before postNormalization
code = code.replace(/~D/g, "$$");
code = code.replace(/~T/g, "~");
return code;
}
var self = this;
text = text.replace(/(?:^|\n)```([^`\n]*)\n([\s\S]*?)\n```[ \t]*(?=\n)/g, function(match, m1, m2) {
var language = trim(m1), codeblock = m2;
// adhere to specified options
var preclass = self.googleCodePrettify ? ' class="prettyprint"' : '';
var codeclass = '';
if (language) {
if (self.googleCodePrettify || self.highlightJs) {
// use html5 language- class names. supported by both prettify and highlight.js
codeclass = ' class="language-' + language + '"';
} else {
codeclass = ' class="' + language + '"';
}
}
var html = ['<pre', preclass, '><code', codeclass, '>',
encodeCode(codeblock), '</code></pre>'].join('');
// replace codeblock with placeholder until postConversion step
return self.hashExtraBlock(html);
});
return text;
};
/******************************************************************
* SmartyPants *
******************************************************************/
Markdown.Extra.prototype.educatePants = function(text) {
var self = this;
var result = '';
var blockOffset = 0;
// Here we parse HTML in a very bad manner
text.replace(/(?:<!--[\s\S]*?-->)|(<)([a-zA-Z1-6]+)([^\n]*?>)([\s\S]*?)(<\/\2>)/g, function(wholeMatch, m1, m2, m3, m4, m5, offset) {
var token = text.substring(blockOffset, offset);
result += self.applyPants(token);
self.smartyPantsLastChar = result.substring(result.length - 1);
blockOffset = offset + wholeMatch.length;
if(!m1) {
// Skip commentary
result += wholeMatch;
return;
}
// Skip special tags
if(!/code|kbd|pre|script|noscript|iframe|math|ins|del|pre/i.test(m2)) {
m4 = self.educatePants(m4);
}
else {
self.smartyPantsLastChar = m4.substring(m4.length - 1);
}
result += m1 + m2 + m3 + m4 + m5;
});
var lastToken = text.substring(blockOffset);
result += self.applyPants(lastToken);
self.smartyPantsLastChar = result.substring(result.length - 1);
return result;
};
function revertPants(wholeMatch, m1) {
var blockText = m1;
blockText = blockText.replace(/&\#8220;/g, "\"");
blockText = blockText.replace(/&\#8221;/g, "\"");
blockText = blockText.replace(/&\#8216;/g, "'");
blockText = blockText.replace(/&\#8217;/g, "'");
blockText = blockText.replace(/&\#8212;/g, "---");
blockText = blockText.replace(/&\#8211;/g, "--");
blockText = blockText.replace(/&\#8230;/g, "...");
return blockText;
}
Markdown.Extra.prototype.applyPants = function(text) {
// Dashes
text = text.replace(/---/g, "&#8212;").replace(/--/g, "&#8211;");
// Ellipses
text = text.replace(/\.\.\./g, "&#8230;").replace(/\.\s\.\s\./g, "&#8230;");
// Backticks
text = text.replace(/``/g, "&#8220;").replace (/''/g, "&#8221;");
if(/^'$/.test(text)) {
// Special case: single-character ' token
if(/\S/.test(this.smartyPantsLastChar)) {
return "&#8217;";
}
return "&#8216;";
}
if(/^"$/.test(text)) {
// Special case: single-character " token
if(/\S/.test(this.smartyPantsLastChar)) {
return "&#8221;";
}
return "&#8220;";
}
// Special case if the very first character is a quote
// followed by punctuation at a non-word-break. Close the quotes by brute force:
text = text.replace (/^'(?=[!"#\$\%'()*+,\-.\/:;<=>?\@\[\\]\^_`{|}~]\B)/, "&#8217;");
text = text.replace (/^"(?=[!"#\$\%'()*+,\-.\/:;<=>?\@\[\\]\^_`{|}~]\B)/, "&#8221;");
// Special case for double sets of quotes, e.g.:
// <p>He said, "'Quoted' words in a larger quote."</p>
text = text.replace(/"'(?=\w)/g, "&#8220;&#8216;");
text = text.replace(/'"(?=\w)/g, "&#8216;&#8220;");
// Special case for decade abbreviations (the '80s):
text = text.replace(/'(?=\d{2}s)/g, "&#8217;");
// Get most opening single quotes:
text = text.replace(/(\s|&nbsp;|--|&[mn]dash;|&\#8211;|&\#8212;|&\#x201[34];)'(?=\w)/g, "$1&#8216;");
// Single closing quotes:
text = text.replace(/([^\s\[\{\(\-])'/g, "$1&#8217;");
text = text.replace(/'(?=\s|s\b)/g, "&#8217;");
// Any remaining single quotes should be opening ones:
text = text.replace(/'/g, "&#8216;");
// Get most opening double quotes:
text = text.replace(/(\s|&nbsp;|--|&[mn]dash;|&\#8211;|&\#8212;|&\#x201[34];)"(?=\w)/g, "$1&#8220;");
// Double closing quotes:
text = text.replace(/([^\s\[\{\(\-])"/g, "$1&#8221;");
text = text.replace(/"(?=\s)/g, "&#8221;");
// Any remaining quotes should be opening ones.
text = text.replace(/"/ig, "&#8220;");
return text;
};
// Find and convert markdown extra definition lists into html.
Markdown.Extra.prototype.runSmartyPants = function(text) {
this.smartyPantsLastChar = '';
text = this.educatePants(text);
// Clean everything inside html tags (some of them may have been converted due to our rough html parsing)
text = text.replace(/(<([a-zA-Z1-6]+)\b([^\n>]*?)(\/)?>)/g, revertPants);
return text;
};
/******************************************************************
* Definition Lists *
******************************************************************/
// Find and convert markdown extra definition lists into html.
Markdown.Extra.prototype.definitionLists = function(text) {
var wholeList = new RegExp(
['(\\x02\\n?|\\n\\n)' ,
'(?:' ,
'(' , // $1 = whole list
'(' , // $2
'[ ]{0,3}' ,
'((?:[ \\t]*\\S.*\\n)+)', // $3 = defined term
'\\n?' ,
'[ ]{0,3}:[ ]+' , // colon starting definition
')' ,
'([\\s\\S]+?)' ,
'(' , // $4
'(?=\\0x03)' , // \z
'|' ,
'(?=' ,
'\\n{2,}' ,
'(?=\\S)' ,
'(?!' , // Negative lookahead for another term
'[ ]{0,3}' ,
'(?:\\S.*\\n)+?' , // defined term
'\\n?' ,
'[ ]{0,3}:[ ]+' , // colon starting definition
')' ,
'(?!' , // Negative lookahead for another definition
'[ ]{0,3}:[ ]+' , // colon starting definition
')' ,
')' ,
')' ,
')' ,
')'
].join(''),
'gm'
);
var self = this;
text = addAnchors(text);
text = text.replace(wholeList, function(match, pre, list) {
var result = trim(self.processDefListItems(list));
result = "<dl>\n" + result + "\n</dl>";
return pre + self.hashExtraBlock(result) + "\n\n";
});
return removeAnchors(text);
};
// Process the contents of a single definition list, splitting it
// into individual term and definition list items.
Markdown.Extra.prototype.processDefListItems = function(listStr) {
var self = this;
var dt = new RegExp(
['(\\x02\\n?|\\n\\n+)' , // leading line
'(' , // definition terms = $1
'[ ]{0,3}' , // leading whitespace
'(?![:][ ]|[ ])' , // negative lookahead for a definition
// mark (colon) or more whitespace
'(?:\\S.*\\n)+?' , // actual term (not whitespace)
')' ,
'(?=\\n?[ ]{0,3}:[ ])' // lookahead for following line feed
].join(''), // with a definition mark
'gm'
);
var dd = new RegExp(
['\\n(\\n+)?' , // leading line = $1
'(' , // marker space = $2
'[ ]{0,3}' , // whitespace before colon
'[:][ ]+' , // definition mark (colon)
')' ,
'([\\s\\S]+?)' , // definition text = $3
'(?=\\n*' , // stop at next definition mark,
'(?:' , // next term or end of text
'\\n[ ]{0,3}[:][ ]|' ,
'<dt>|\\x03' , // \z
')' ,
')'
].join(''),
'gm'
);
listStr = addAnchors(listStr);
// trim trailing blank lines:
listStr = listStr.replace(/\n{2,}(?=\\x03)/, "\n");
// Process definition terms.
listStr = listStr.replace(dt, function(match, pre, termsStr) {
var terms = trim(termsStr).split("\n");
var text = '';
for (var i = 0; i < terms.length; i++) {
var term = terms[i];
// process spans inside dt
term = convertSpans(trim(term), self);
text += "\n<dt>" + term + "</dt>";
}
return text + "\n";
});
// Process actual definitions.
listStr = listStr.replace(dd, function(match, leadingLine, markerSpace, def) {
if (leadingLine || def.match(/\n{2,}/)) {
// replace marker with the appropriate whitespace indentation
def = Array(markerSpace.length + 1).join(' ') + def;
// process markdown inside definition
// TODO?: currently doesn't apply extensions
def = outdent(def) + "\n\n";
def = "\n" + convertAll(def, self) + "\n";
} else {
// convert span-level markdown inside definition
def = rtrim(def);
def = convertSpans(outdent(def), self);
}
return "\n<dd>" + def + "</dd>\n";
});
return removeAnchors(listStr);
};
/***********************************************************
* Strikethrough *
************************************************************/
Markdown.Extra.prototype.strikethrough = function(text) {
// Pretty much duplicated from _DoItalicsAndBold
return text.replace(/([\W_]|^)~T~T(?=\S)([^\r]*?\S[\*_]*)~T~T([\W_]|$)/g,
"$1<del>$2</del>$3");
};
/***********************************************************
* New lines *
************************************************************/
Markdown.Extra.prototype.newlines = function(text) {
// We have to ignore already converted newlines and line breaks in sub-list items
return text.replace(/(<(?:br|\/li)>)?\n/g, function(wholeMatch, previousTag) {
return previousTag ? wholeMatch : " <br>\n";
});
};
})();

238
src/scripts/project-edit.js Normal file
View File

@@ -0,0 +1,238 @@
/* Edit Node */
/* Move Node */
var movingMode = Cookies.getJSON('bcloud_moving_node');
function editNode(nodeId) {
// Remove the 'n_' suffix from the id
if (nodeId.substring(0, 2) == 'n_') {
nodeId = nodeId.substr(2);
}
var url = '/nodes/' + nodeId + '/edit?embed=1';
$.get(url, function(dataHtml) {
// Update the DOM injecting the generate HTML into the page
$('#project_context').html(dataHtml);
updateUi(nodeId, 'edit');
})
.fail(function(dataResponse) {
$('#project_context').html($('<iframe id="server_error"/>'));
$('#server_error').attr('src', url);
})
.always(function(){
$('.button-edit-icon').addClass('pi-edit').removeClass('pi-spin spin');
});
}
/* Add Node */
function addNode(nodeTypeName, parentId) {
var url = '/nodes/create';
var node_props = {node_type_name: nodeTypeName, project_id: ProjectUtils.projectId()};
if (typeof(parentId) != 'undefined') {node_props.parent_id = parentId};
$.post(url, node_props)
.done(function(data) {
editNode(data.data.asset_id);
})
.always(function(){
$('.button-add-group-icon').addClass('pi-collection-plus').removeClass('pi-spin spin');
})
.fail(function(data){
statusBarSet('error', 'Error creating node (' + data.status + ' - ' + data.statusText + ')', 'pi-warning', 5000);
});
}
/* Edit Button */
$('#item_edit').click(function(e){
$('.button-edit-icon').addClass('pi-spin spin').removeClass('pi-edit');
// When clicking on the edit icon, embed the edit
e.preventDefault();
if (ProjectUtils.isProject()) {
window.location.replace(urlProjectEdit);
} else {
editNode(ProjectUtils.nodeId());
}
});
function moveModeEnter() {
$('#overlay-mode-move-container').addClass('visible');
$('.button-move').addClass('disabled');
// Scroll to top so we can see the instructions/buttons
$("#project_context-container").scrollTop(0);
}
function moveModeExit() {
/* Remove cookie, display current node, remove UI */
if (ProjectUtils.isProject()) {
displayProject(ProjectUtils.projectId());
} else {
displayNode(ProjectUtils.nodeId());
}
$('#overlay-mode-move-container').removeClass('visible');
$('.button-move').removeClass('disabled');
$('#item_move_accept').html('<i class="pi-check"></i> Move Here');
Cookies.remove('bcloud_moving_node');
}
$( document ).ready(function() {
if (movingMode) {
moveModeEnter();
} else {
$('#overlay-mode-move-container').removeClass('visible');
$('.button-move').removeClass('disabled');
}
/* Add Node Type Button */
$('.item_add_node').click(function(e){
e.preventDefault();
var nodeTypeName = $(this).data('node-type-name');
if (ProjectUtils.isProject()) {
addNode(nodeTypeName);
} else {
addNode(nodeTypeName, ProjectUtils.nodeId());
}
});
$('#item_move').click(function(e){
e.preventDefault();
moveModeEnter();
// Set the nodeId in the cookie
Cookies.set('bcloud_moving_node', { node_id: ProjectUtils.nodeId(), node_type: ProjectUtils.nodeType()});
});
$("#item_move_accept").click(function(e) {
e.preventDefault();
var movingNodeId = Cookies.getJSON('bcloud_moving_node').node_id;
var moveNodeParams = {node_id: movingNodeId};
// If we are not at the root of the project, add the parent node id to the
// request params
if (!ProjectUtils.isProject()) {
moveNodeParams.dest_parent_node_id = ProjectUtils.nodeId();
}
$(this).html('<i class="pi-spin spin"></i> Moving...');
$.post(urlNodeMove, moveNodeParams,
function(data){
}).done(function() {
statusBarSet('success', 'Moved just fine');
Cookies.remove('bcloud_moving_node');
moveModeExit();
$('#project_tree').jstree("refresh");
})
.fail(function(data){
statusBarSet('error', 'Error moving node (' + data.status + ' - ' + data.statusText + ')', 'pi-warning', 6000);
$(this).html('<i class="pi-check"></i> Move Here');
});
});
$("#item_move_cancel").click(function(e) {
e.preventDefault();
$('.button-edit-icon').addClass('pi-spin spin').removeClass('pi-cancel');
moveModeExit();
});
/* Featured Toggle */
$('#item_featured').click(function(e){
e.preventDefault();
$.post(urlNodeFeature, {node_id : ProjectUtils.nodeId()},
function(data){
// Feedback logic
})
.done(function(){
statusBarSet('success', 'Featured status toggled successfully', 'pi-star-filled');
})
.fail(function(data){
statusBarSet('error', 'Error toggling feature (' + data.status + ' - ' + data.statusText + ')', 'pi-warning', 6000);
});
});
/* Project Header toggle */
$('#item_toggle_projheader').click(function (e) {
e.preventDefault();
$.post(urlNodeToggleProjHeader, {node_id: ProjectUtils.nodeId()})
.done(function (data) {
statusBarSet('success', 'Project Header ' + data.action + ' successfully', 'pi-star-filled');
})
.fail(function (jsxhr) {
var content_type = jsxhr.getResponseHeader('Content-Type');
if(content_type.startsWith('application/json')) {
var data = jsxhr.responseJSON;
statusBarSet('error', 'Error toggling (' + data.messsage + ')', 'pi-warning', 6000);
} else {
statusBarSet('error', 'Error toggling (' + jsxhr.responseText + ')', 'pi-warning', 6000);
}
});
});
/* Delete */
$('#item_delete').click(function(e){
e.preventDefault();
if (ProjectUtils.isProject()) {
// url = window.location.href.split('#')[0] + 'delete';
// window.location.replace(url);
$.post(urlProjectDelete, {project_id: ProjectUtils.projectId()},
function (data) {
// Feedback logic
}).done(function () {
window.location.replace('/p/');
});
} else {
$.post(urlNodeDelete, {node_id: ProjectUtils.nodeId()},
function (data) {
// Feedback logic
})
.done(function () {
statusBarSet('success', 'Deleted successfully', 'pi-trash');
if (ProjectUtils.parentNodeId() != '') {
displayNode(ProjectUtils.parentNodeId());
} else {
// Display the project when the group is at the root of the tree
displayProject(ProjectUtils.projectId());
}
setTimeout(function(){
$('#project_tree').jstree('refresh');
}, 1000);
})
.fail(function (data) {
statusBarSet('error', 'Error deleting (' + data.status + ' - ' + data.statusText + ')', 'pi-warning', 6000);
});
}
});
/* Toggle public */
$('#item_toggle_public').click(function(e){
e.preventDefault();
var currentNodeId = ProjectUtils.nodeId();
$.post(urlNodeTogglePublic, {node_id : currentNodeId},
function(data){
// Feedback logic
})
.done(function(data){
statusBarSet('success', data.data.message);
displayNode(currentNodeId);
})
.fail(function(data){
statusBarSet('error', 'Error toggling status (' + data.status + ' - ' + data.statusText + ')', 'pi-warning', 6000);
});
});
$('ul.project-edit-tools').removeClass('disabled');
});

View File

@@ -0,0 +1,70 @@
$(function () {
$('[data-toggle="tooltip"]').tooltip({'delay' : {'show': 1250, 'hide': 250}});
$('[data-toggle="popover"]').popover();
})
function NavbarTransparent() {
var startingpoint = 50;
$(window).on("load scroll", function () {
if ($(this).scrollTop() > startingpoint) {
$('.navbar-overlay, .navbar-transparent').addClass('is-active');
if(document.getElementById("project_context-header") !== null) {
$('#project_context-header').addClass('is-offset');
}
} else {
$('.navbar-overlay, .navbar-transparent').removeClass('is-active');
if(document.getElementById("project_context-header") !== null) {
$('#project_context-header').removeClass('is-offset');
}
};
});
};
NavbarTransparent();
/* Status Bar */
function statusBarSet(classes, html, icon_name, time){
/* Utility to notify the user by temporarily flashing text on the project header
Usage:
'classes' can be: success, error, warning, info, default
'html': the text to display, can contain html tags
(in case of errors, it's better to use data.status + data.statusText instead )
'icon_name': optional, sets a custom icon (otherwise an icon based on the class will be used)
'time': optional, custom time in milliseconds for the text to be displayed
*/
var icon = '';
if (!time) { time = 3000 };
if (!icon_name) {
if (classes == 'error') {
icon_name = 'pi-attention';
} else if (classes == 'success') {
icon_name = 'pi-check';
} else if (classes == 'warning') {
icon_name = 'pi-warning';
} else if (classes == 'info') {
icon_name = 'pi-info';
} else {
icon = '<i class="' + icon_name + '"></i>';
};
} else {
icon = '<i class="' + icon_name + '"></i>';
};
var text = icon + html;
$("#project-statusbar").addClass('active ' + classes);
$("#project-statusbar").html(text);
/* Back to normal */
setTimeout(function(){
$("#project-statusbar").removeAttr('class');
$("#project-statusbar").html();
}, time);
};

View File

@@ -0,0 +1,182 @@
function projectNavCollapse() {
$("#project-side-container").addClass('collapsed');
$("ul.breadcrumb.context").addClass('active');
if (typeof Ps !== 'undefined'){
Ps.destroy(document.getElementById('project_tree'));
};
};
function projectNavExpand() {
$("#project-side-container").removeClass('collapsed');
$("ul.breadcrumb.context").removeAttr('class');
if (typeof Ps !== 'undefined'){
Ps.initialize(document.getElementById('project_tree'), {suppressScrollX: true});
}
};
function projectNavCheck(){
/* Only run if there is a tree */
if(document.getElementById("project_tree") !== null) {
var nav_status = Cookies.getJSON('bcloud_ui');
if (nav_status && nav_status.nav_collapsed) {
if (nav_status.nav_collapsed == 'expanded') {
projectNavExpand();
} else if ( nav_status.nav_collapsed == 'collapsed' ) {
projectNavCollapse();
}
} else {
projectNavExpand();
}
}
}
function projectNavToggle(){
var nav_status = Cookies.getJSON('bcloud_ui');
if (nav_status && nav_status.nav_collapsed) {
if (nav_status.nav_collapsed == 'expanded') {
projectNavCollapse();
setJSONCookie('bcloud_ui', 'nav_collapsed', 'collapsed');
} else if ( nav_status.nav_collapsed == 'collapsed' ) {
projectNavExpand();
setJSONCookie('bcloud_ui', 'nav_collapsed', 'expanded');
}
} else {
projectNavCollapse();
setJSONCookie('bcloud_ui', 'nav_collapsed', 'collapsed');
}
$('#project_context-header').width($('#project_context-container').width());
}
$(function () {
/* Check on first load */
projectNavCheck();
$('.project_split, .project_nav-toggle-btn').on('click', function (e) {
projectNavToggle();
});
/* Only run if there is a tree */
if(document.getElementById("project_tree") !== null) {
$(document).keypress(function(e) {
var tag = e.target.tagName.toLowerCase();
/* Toggle when pressing [T] key */
if(e.which == 116 && tag != 'input' && tag != 'textarea' && !e.ctrlKey && !e.metaKey && !e.altKey) {
projectNavToggle();
}
});
}
});
/* Small utility to enable specific node_types under the Add New dropdown */
/* It takes:
* empty: Enable every item
* false: Disable every item
* array: Disable every item except a list of node_types, e.g: ['asset', 'group']
*/
function addMenuEnable(node_types){
$("#item_add").parent().removeClass('disabled');
$("ul.add_new-menu li[class^='button-']").hide().addClass('disabled');
if (node_types === undefined) {
$("ul.add_new-menu li[class^='button-']").show().removeClass('disabled');
} else if (node_types == false) {
$("#item_add").parent().addClass('disabled');
} else {
$.each(node_types, function(index, value) {
$("ul.add_new-menu li[class*='button-" + value +"']").show().removeClass('disabled');
});
}
}
function addMenuDisable(node_types){
$.each(node_types, function(index, value) {
$("ul.add_new-menu li[class*='button-" + value +"']").addClass('disabled');
});
}
/* Completely hide specific items (like Texture when on project root) */
function addMenuHide(node_types){
$.each(node_types, function(index, value) {
$("ul.add_new-menu li[class*='button-" + value +"']").hide().addClass('disabled');
});
}
/* Jump to the top of the page! */
function hopToTop(limit){
if (limit == null) {
limit = 500;
}
document.getElementById("hop").onclick = function(e){ window.scrollTo(0, 0);}
$(window).scroll(function() {
if ($(window).scrollTop() >= limit) {$("#hop").addClass("active")} else {$("#hop").removeAttr("class")}
});
}
/* Utility to replace a single item on a JSON cookie */
function setJSONCookie(cookieToChange, cookieItem, cookieData){
/* Get cookie to change, and its list if it has any */
var cookieList = Cookies.getJSON(cookieToChange);
/* Create an empty list if there's no cookie */
if (!cookieList){ cookieList = {}; }
cookieList[cookieItem] = cookieData;
/* Set (or create) cookie */
Cookies.set(cookieToChange, cookieList);
}
function containerResizeY(window_height){
var container_offset = $('#project-container').offset();
var container_height = window_height - container_offset.top;
var container_height_wheader = window_height - container_offset.top - $('#project_nav-header').height();
var window_height_minus_nav = $('#project_nav').height() - $('#project_nav-header').height();
$('#project_context-header').width($('#project_context-container').width());
$('#project_nav-container, .project_split').css(
{'max-height': window_height_minus_nav + 'px',
'height': window_height_minus_nav + 'px'}
);
if ($(window).width() > 768) {
if (container_height > parseInt($('#project-container').css("min-height"))) {
if (projectTree){
$(projectTree).css(
{'max-height': container_height_wheader + 'px',
'height': container_height_wheader + 'px'}
);
}
};
};
if (projectTree){ Ps.update(projectTree) }
};

View File

@@ -0,0 +1,109 @@
/* Reply */
$(document).on('click','body .comment-action-reply',function(e){
e.preventDefault();
// container of the comment we are replying to
var parentDiv = $(this).parent().parent();
// container of the first-level comment in the thread
var parentDivFirst = $(this).parent().parent().prevAll('.is-first:first');
// Get the id of the comment
if (parentDiv.hasClass('is-reply')) {
parentNodeId = parentDivFirst.data('node_id');
} else {
parentNodeId = parentDiv.data('node_id');
}
// Get the textarea and set its parent_id data
var commentField = document.getElementById('comment_field');
commentField.setAttribute('data-parent_id', parentNodeId);
// Start the comment field with @authorname:
var replyAuthor = $(this).parent().parent().find('.comment-author:first').html();
$(commentField).val("**@" + replyAuthor + ":** ");
// Add class for styling
$('.comment-container').removeClass('is-replying');
parentDiv.addClass('is-replying');
// Rename Post Comment button to Reply
var commentSubmitButton = document.getElementById('comment_submit');
$(commentSubmitButton).text('Post Reply');
// Move comment-reply container field after the parent container
var commentForm = $('.comment-reply-container').detach();
parentDiv.after(commentForm);
// document.getElementById('comment_field').focus();
$(commentField).focus();
// Convert Markdown
var convert = new Markdown.getSanitizingConverter().makeHtml;
var preview = $('.comment-reply-preview');
preview.html(convert($(commentField).val()));
$('.comment-reply-form').addClass('filled');
});
/* Cancel Reply */
$(document).on('click','body .comment-action-cancel',function(e){
e.preventDefault();
$('.comment-reply-container').detach().prependTo('#comments-list');
var commentField = document.getElementById('comment_field');
$(commentField).val('');
// Convert Markdown
var convert = new Markdown.getSanitizingConverter().makeHtml;
var preview = $('.comment-reply-preview');
preview.html(convert($(commentField).val()));
var commentSubmitButton = document.getElementById('comment_submit');
$(commentSubmitButton).text('Post Comment');
$('.comment-reply-form').removeClass('filled');
$('.comment-container').removeClass('is-replying');
});
/* Rate */
$(document).on('click','body .comment-action-rating',function(e){
e.preventDefault();
var $this = $(this);
var nodeId = $this.parent().parent().parent().data('node_id');
var is_positive = !$this.hasClass('down');
var parentDiv = $this.parent();
var rated_positive = parentDiv.hasClass('positive');
var op;
if (parentDiv.hasClass('rated') && is_positive == rated_positive) {
op = 'revoke';
} else if (is_positive) {
op = 'upvote';
} else {
op = 'downvote';
}
$.post("/nodes/comments/" + nodeId + "/rate/" + op)
.done(function(data){
// Add/remove styles for rated statuses
switch(op) {
case 'revoke':
parentDiv.removeClass('rated');
break;
case 'upvote':
parentDiv.addClass('rated');
parentDiv.addClass('positive');
break;
case 'downvote':
parentDiv.addClass('rated');
parentDiv.removeClass('positive');
break;
}
var rating = data['data']['rating_positive'] - data['data']['rating_negative'];
$this.siblings('.comment-rating-value').text(rating);
});
});

View File

@@ -0,0 +1,15 @@
// Util to handle project, node and parent properties
ProjectUtils = {
nodeId: function() { return document.body.dataset.nodeId; },
parentNodeId: function() { return document.body.dataset.parentNodeId; },
projectId: function() { return document.body.dataset.projectId; },
isProject: function() { return document.body.dataset.isProject === 'true'; },
nodeType: function() { return document.body.dataset.nodeType; },
isModified: function() { return document.body.dataset.isModified === 'true'; },
setProjectAttributes: function(props) {
for (var key in props) {
if (!props.hasOwnProperty(key)) continue;
document.body.dataset[key] = props[key];
}
}
};

View File

@@ -0,0 +1,100 @@
/*
* == Search ==
* index and algolia settings are defined in layout.jade
*/
$(document).ready(function() {
var searchInput = $('#cloud-search');
var tu = searchInput.typeahead({hint: true}, {
source: index.ttAdapter(),
displayKey: 'name',
limit: 10,
minLength: 0,
templates: {
suggestion: function(hit) {
var hitMedia = (hit.media ? ' · <span class="media">'+hit.media+'</span>' : '');
var hitFree = (hit.is_free ? '<div class="search-hit-ribbon"><span>free</span></div>' : '');
var hitPicture;
if (hit.picture){
hitPicture = '<img src="' + hit.picture + '"/>';
} else {
hitPicture = '<div class="search-hit-thumbnail-icon">';
hitPicture += (hit.media ? '<i class="pi-' + hit.media + '"></i>' : '<i class="dark pi-'+ hit.node_type + '"></i>');
hitPicture += '</div>';
};
return '' +
'<a href="/nodes/'+ hit.objectID + '/redir" class="search-site-result" id="'+ hit.objectID + '">' +
'<div class="search-hit">' +
'<div class="search-hit-thumbnail">' +
hitPicture +
hitFree +
'</div>' +
'<div class="search-hit-name" title="' + hit.name + '">' +
hit._highlightResult.name.value + ' ' +
'</div>' +
'<div class="search-hit-meta">' +
'<span class="project">' + hit._highlightResult.project.name.value + '</span> · ' +
'<span class="node_type">' + hit.node_type + '</span>' +
hitMedia +
'</div>' +
'</div>'+
'</a>';
}
}
});
$('.search-site-result.advanced, .search-icon').on('click', function(e){
e.stopPropagation();
e.preventDefault();
window.location.href = '/search#q='+ $("#cloud-search").val() + '&page=1';
});
searchInput.bind('typeahead:select', function(ev, hit) {
$('.search-icon').removeClass('pi-search').addClass('pi-spin spin');
window.location.href = '/nodes/'+ hit.objectID + '/redir';
});
searchInput.bind('typeahead:active', function() {
$('#search-overlay').addClass('active');
$('.page-body').addClass('blur');
});
searchInput.bind('typeahead:close', function() {
$('#search-overlay').removeClass('active');
$('.page-body').removeClass('blur');
});
searchInput.keyup(function(e) {
if ( $('.tt-dataset').is(':empty') ){
if(e.keyCode == 13){
window.location.href = '/search#q='+ $("#cloud-search").val() + '&page=1';
};
};
});
searchInput.bind('typeahead:render', function(event, suggestions, async, dataset) {
if( suggestions != undefined && $('.tt-all-results').length <= 0){
$('.tt-dataset').append(
'<a id="search-advanced" href="/search#q='+ $("#cloud-search").val() + '&page=1" class="search-site-result advanced tt-suggestion">' +
'<div class="search-hit">' +
'<div class="search-hit-thumbnail">' +
'<div class="search-hit-thumbnail-icon">' +
'<i class="pi-search"></i>' +
'</div>' +
'</div>' +
'<div class="search-hit-name">' +
'Use Advanced Search' +
'</div>' +
'</div>'+
'</a>');
};
});
});

View File

@@ -0,0 +1,329 @@
// Store the title, to later append notifications count
var page_title = document.title;
var unread_on_load = 0;
var unread_new = 0;
var first_load = false;
// getNotifications by fetching json every X seconds
function getNotifications(){
$.getJSON( "/notifications/", function( data ) {
if (!first_load) {
unread_on_load = data['items'].length;
first_load = true;
}
var items = [];
unread_new = 0;
// Only if there's actual data
if (data['items'][0]){
// Loop through each item
$.each(data['items'], function(i, no){
// Increase the unread_new counter
if (!no['is_read']){ unread_new++ };
// Check if the current item has been read, to style it
var is_read = no['is_read'] ? 'is_read' : '';
var read_info = 'data-id="'+ no['_id'] + '" data-read="' + no['is_read'] + '"';
// Notification list item
var content = '<li class="nc-item ' + is_read +'" data-id="'+ no['_id'] + '">';
// User's avatar
content += '<div class="nc-avatar">';
content += '<img ' + read_info + ' src="' + no['username_avatar'] + '"/> ';
content += '</div>';
// Text of the notification
content += '<div class="nc-text">';
// Username and action
content += no['username'] + ' ' + no['action'] + ' ';
// Object
content += '<a '+read_info+'" href="'+no['object_url']+'" class="nc-a">';
content += no['context_object_name'] + ' ';
content += '</a> ';
// Date
content += '<span class="nc-date">';
content += '<a '+read_info+'" href="'+no['object_url']+'" class="nc-a">';
content += no['date'];
content += '</a>';
content += '</span>';
// Read Toggle
content += '<a id="'+no['_id']+'" href="/notifications/' + no['_id'] + '/read-toggle" class="nc-button nc-read_toggle">';
if (no['is_read']){
content += '<i title="Mark as Unread" class="pi pi-circle-dot"></i>';
} else {
content += '<i title="Mark as Read" class="pi pi-circle"></i>';
};
content += '</a>';
// Subscription Toggle
content += '<a href="/notifications/' + no['_id'] + '/subscription-toggle" class="nc-button nc-subscription_toggle">';
if (no['is_subscribed']){
content += '<i title="Turn Off Notifications" class="pi-toggle-on"></i>';
} else {
content += '<i title="Turn On Notifications" class="pi-toggle-off"></i>';
};
content += '</a>';
content += '</div>';
content += '</li>';
items.push(content);
}); // each
if (unread_new > 0) {
// Set page title, display notifications and set counter
document.title = '(' + unread_new + ') ' + page_title;
$('#notifications-count').addClass('bloom');
$('#notifications-count').html('<span>' + unread_new + '</span>');
$('#notifications-toggle i').removeClass('pi-notifications-none').addClass('pi-notifications-active');
} else {
document.title = page_title;
$('#notifications-count').removeAttr('class');
$('#notifications-toggle i').removeClass('pi-notifications-active').addClass('pi-notifications-none');
};
checkPopNotification(
data['items'][0]['_id'],
data['items'][0]['username'],
data['items'][0]['username_avatar'],
data['items'][0]['action'],
data['items'][0]['date'],
data['items'][0]['context_object_name'],
data['items'][0]['object_url']);
} else {
var content = '<li class="nc-item nc-item-empty">';
content += 'No notifications... yet.';
content += '</li>';
items.push(content);
}; // if items
// Populate the list
$('ul#notifications-list').html( items.join(''));
})
.done(function(){
// clear the counter
unread_on_load = unread_new;
});
};
// Used when we click somewhere in the page
function hideNotifications(){
$('#notifications').hide();
$('#notifications-toggle').removeClass('active');
};
function popNotification(){
// pop in!
$("#notification-pop").addClass('in');
// After 10s, add a class to make it pop out
setTimeout(function(){
$("#notification-pop").addClass('out');
// And a second later, remove all classes
setTimeout(function(){
$("#notification-pop").removeAttr('class');
}, 1000);
}, 10000);
// Set them the same so it doesn't pop up again
unread_on_load = unread_new;
};
function checkPopNotification(id,username,username_avatar,action,date,context_object_name,object_url)
{
// If there's new content
if (unread_new > unread_on_load){
// Fill in the urls for redirect on click, and mark-read
$("#notification-pop").attr('data-url', object_url);
$("#notification-pop").attr('data-read-toggle', '/notifications/' + id + '/read-toggle');
// The text in the pop
var text = '<span class="nc-author">' + username + '</span> ';
text += action + ' ';
text += context_object_name + ' ';
text += '<span class="nc-date">' + date + '</span>';
// Fill the html
$('#notification-pop .nc-text').html(text);
$('#notification-pop .nc-avatar img').attr('src', username_avatar);
// pop in!
popNotification();
};
};
// Function to set #notifications flyout height and resize if needed
function notificationsResize(){
var height = $(window).height() - 80;
if ($('#notifications').height() > height){
$('#notifications').css({
'max-height' : height / 2,
'overflow-y' : 'scroll'
}
);
} else {
$('#notifications').css({
'max-height' : '1000%',
'overflow-y' : 'initial'
}
);
};
};
$(function() {
// Click anywhere in the page to hide #notifications
$(document).click(function () {
hideNotifications();
});
// ...but clicking inside #notifications shouldn't hide itself
$('#notifications').on('click', function (e) {
e.stopPropagation();
});
// Toggle the #notifications flyout
$('#notifications-toggle').on('click', function (e) {
e.stopPropagation();
$('#notifications').toggle();
$(this).toggleClass("active");
notificationsResize();
// Hide other dropdowns
$('nav .dropdown').removeClass('open');
var navbarCollapse = $('nav.navbar-collapse');
if ($(navbarCollapse).hasClass('in')){
$(navbarCollapse).addClass('show-notifications').removeClass('in');
$('.nav-notifications-icon').removeClass('pi-notifications-none').addClass('pi-cancel');
} else {
$(navbarCollapse).removeClass('show-notifications');
$('.nav-notifications-icon').addClass('pi-notifications-none').removeClass('pi-cancel');
}
});
// Hide flyout when clicking other dropdowns
$('nav').on('click', '.dropdown', function (e) {
$('#notifications').hide();
$('#notifications-toggle').removeClass('active');
});
$('#notification-pop').on('click', function (e) {
e.preventDefault();
e.stopPropagation();
var link_url = $(this).data('url');
var read_url = $(this).data('read-toggle');
$.get(read_url)
.done(function () {
window.location.href = link_url;
});
});
// Read/Subscription Toggles
$('ul#notifications-list').on('click', '.nc-button', function (e) {
e.preventDefault();
var nc = $(this);
// Swap to spin icon while we wait for the response
$('i', nc).addClass('spin');
$.get($(nc).attr('href'))
.done(function (data) {
if ($(nc).hasClass('nc-read_toggle')) {
if (data.data.is_read) {
$('i', nc).removeClass('pi-circle').addClass('pi-circle-dot');
$(nc).closest('.nc-item').addClass('is_read');
} else {
$('i', nc).removeClass('pi-circle-dot').addClass('pi-circle');
$(nc).closest('.nc-item').removeClass('is_read');
}
}
;
if ($(nc).hasClass('nc-subscription_toggle')) {
if (data.data.is_subscribed) {
$('i', nc).removeClass('pi-toggle-on').addClass('pi-toggle-off');
} else {
$('i', nc).removeClass('pi-toggle-off').addClass('pi-toggle-on');
}
}
;
$('i', nc).removeClass('spin');
});
});
// When clicking on links, toggle as read
$('ul#notifications-list').on('click', '.nc-a', function (e) {
e.preventDefault();
var is_read = $(this).data('read');
var link_url = $(this).attr('href');
var read_url = '/notifications/' + $(this).data('id') + '/read-toggle';
if (is_read) {
window.location.href = link_url;
} else {
$.get(read_url)
.done(function () {
window.location.href = link_url;
});
}
});
// Mark All as Read
$('#notifications-markallread').on('click', function (e) {
e.preventDefault();
$.get("/notifications/read-all");
$('ul#notifications-list li.nc-item:not(.is_read)').each(function () {
$(this).addClass('is_read');
});
document.title = page_title;
$('#notifications-count').removeAttr('class');
$('#notifications-toggle i').removeClass('pi-notifications-active').addClass('pi-notifications-none');
unread_on_load = unread_new;
});
});
function getNotificationsLoop() {
getNotifications();
var getLoop = setTimeout(function () {
getNotificationsLoop();
}, 30000);
}

File diff suppressed because one or more lines are too long

1078
src/styles/_base.sass Normal file

File diff suppressed because it is too large Load Diff

599
src/styles/_comments.sass Normal file
View File

@@ -0,0 +1,599 @@
$comments-width-max: 710px
#comments-container
margin-top: 15px
padding: 5px 20px 20px 20px
position: relative
border-top: 1px solid $color-background
min-height: 170px
&.texture
border-top: none
#comments-reload
text-align: center
cursor: pointer
padding: 15px 0
display: block
#comments-list-header
#comments-list-title
padding: 15px 0 10px 0
margin: 0
font:
size: 1.5em
weight: 300
family: $font-body
color: $color-text-dark-primary
#comments-list-items-loading
font-size: 2em
color: $color-background
text-align: center
position: relative
top: 25px
margin-bottom: 10px
+spin
#comments-list-items,
.comment-reply-container
position: relative
+media-xs
max-width: 100%
+media-sm
max-width: 100%
+media-md
max-width: $comments-width-max
+media-lg
max-width: $comments-width-max
.nocomments
color: $color-text-dark-hint
text-align: center
cursor: default
padding: 8px 0
#comments-list
/* Each comment on the list*/
.comment-container,
.comment-reply-container
display: flex
flex-direction: column
position: relative
padding: 15px 0 25px 0
transition: background-color 150ms ease-in-out, padding 150ms ease-in-out, margin 150ms ease-in-out
&.comment-linked
background-color: $color-background-light !important
border-top: 3px solid $color-info !important
border-bottom: 2px solid $color-background-dark
border-bottom-left-radius: 3px
border-bottom-right-radius: 3px
&:before
content: 'Linked Comment'
position: absolute
right: 20px
color: $color-info
text-transform: uppercase
font-size: .8em
&.is-replying
margin-bottom: 15px !important
.comment-header .comment-avatar
padding-right: 5px
padding-left: 5px
&.comment-linked+.comment-container.is-first
border-top: none
/* Header containing author, time, and badges if any*/
.comment-header
display: flex
align-items: center
.comment-avatar
padding-right: 10px
img
border-radius: 50%
width: 20px
height: 20px
.comment-author
position: relative
color: $color-background-nav
font:
weight: 500
&.own
color: $color-success
&.op
color: $color-primary-dark
.username
padding-left: 5px
color: $color-text-dark-secondary
.comment-time
padding-left: 10px
margin-left: 10px
color: $color-text-dark-hint
&:before
content: '·'
position: relative
left: -10px
font-weight: 600
/* The actual comment body. */
/* Here we style both the preview comment and posted comments */
.comment-content,
.comment-reply-form
+node-details-description
padding: 10px 0 0 10px
color: darken($color-text-dark, 10%)
font:
size: 1em
weight: normal
transition: background-color 200ms ease-in-out, margin 200ms ease-in-out
margin: 0
border: thin solid transparent
+media-xs
padding:
left: 0
top: 15px
p
line-height: 1.5em
+media-xs
padding:
left: 0
right: 0
strong, b
font-weight: 500
color: $color-info
textarea
+node-details-description
background-color: transparent
padding: 0 0 0 5px
margin-left: 15px
width: 100%
color: $color-text-dark-primary
border: none
border-radius: 3px
font:
size: 1em
weight: normal
&:focus
outline: none
border: none
color: $color-text-dark
&.editing
background-color: $color-background-light
margin: 10px 0
border-color: $color-background-dark
border-radius: 3px
&.empty
border-color: $color-danger
.comment-content-preview
display: none
+node-details-description
padding: 10px 0 0 10px
font:
size: 1em
weight: normal
position: relative
margin-top: 15px
&:empty
margin: 0
padding: 0
&:before, &:after
display: none
&:before
content: 'Live Preview'
position: absolute
top: -20px
left: 30px
font-size: .9em
color: $color-text-dark-hint
transition: color 150ms ease-in-out
+media-md
visibility: visible
+media-sm
visibility: hidden
&:after
content: 'Markdown Supported'
position: absolute
top: -20px
right: 20px
font-size: .9em
color: $color-text-dark-hint
transition: color 150ms ease-in-out
/* Rating, and actions such as reply */
.comment-meta
display: flex
align-items: center
padding: 5px 0 0 30px
color: $color-text-dark-secondary
/* Small container for rating buttons and value */
.comment-rating
display: flex
align-items: center
&.rated
color: $color-text-dark-secondary
.down
color: $color-downvote
&.rated.positive
color: $color-upvote
.down
color: $color-text-dark-secondary
.comment-action-rating.up:before
content: '\e83f'
.comment-rating-value
padding-right: 15px
color: $color-text-dark-secondary
cursor: default
.comment-action-rating
font-family: 'pillar-font'
height: 25px
width: 16px
cursor: pointer
.comment-action-rating.up
&:hover
color: $color-upvote
&:before
content: '\e83e'
top: 2px
position: relative
.comment-action-rating.down
&:hover
color: $color-downvote
&:before
content: '\e838'
/* Reply button */
.comment-action-reply,
.comment-action-edit
padding-left: 10px
margin-left: 10px
color: $color-text-dark-secondary
&:before
content: '·'
position: relative
left: -10px
font-weight: 600
span
cursor: pointer
&:hover
color: $color-primary
span.edit_save,
color: $color-success
display: none
&:hover
color: lighten($color-success, 10%)
&.error
color: $color-danger
&.saving
user-select: none
pointer-events: none
cursor: default
i
font-size: .8em
margin-right: 5px
span.edit_cancel
display: none
margin-left: 15px
&.is-reply
padding:
top: 20px
left: 15px
margin-left: 30px
border-left: 3px solid $color-background-dark
+media-xs
padding-left: 15px
&.comment-linked
border-left: 3px solid $color-info
border-top: thin solid $color-background-dark !important
border-bottom: thin solid $color-background-dark !important
&.is-first
border-top: 1px solid lighten($color-text-dark-hint, 15%)
&.is-team
.comment-author
color: $color-success
&.is-replying
border-left: 3px solid $color-primary
padding-left: 10px
// &.is-replying.is-first+.comment-reply-container
&.is-replying+.comment-reply-container
border-left: 3px solid $color-primary
margin-left: 0
padding-left: 30px
.comment-badge
display: inline-block
border: 1px solid $color-text-dark-hint
color: $color-text-dark-hint
padding: 1px 4px
margin: 0 5px 0 10px
border-radius: 3px
font:
size: .7em
weight: 400
text-transform: uppercase
&.badge-team
border-color: $color-info
color: $color-info
&.badge-op
border-color: $color-primary
color: $color-primary
&.badge-own
border-color: $color-success
color: $color-success
.comment-reply-container
// It's flex, like the others, but different direction
flex-direction: row
/* Little gravatar icon on the left */
.comment-reply-avatar
img
border-radius: 50%
width: 25px
height: 25px
/* textarea field, submit button and reply details */
.comment-reply-form
width: 100%
padding:
top: 0
left: 10px
.comment-reply-field
position: relative
textarea
width: 100%
height: 45px
line-height: 1.5em
border: 1px solid $color-background-dark
border-radius: 3px
margin: 0 auto 5px auto
padding: 10px
color: $color-text-dark
resize: none
transition: all 300ms ease-in-out
&:focus
border: 1px solid $color-success
outline: none
&.field-error
border-color: $color-danger
&.sign-in
textarea
margin: 0
.sign-in
position: absolute
top: 0
left: 0
right: 0
bottom: 0
background-color: rgba(white, .5)
display: flex
align-items: center
padding-left: 15px
a
margin-right: 4px
.comment-reply-preview
position: relative
margin: 0 auto 5px auto
padding: 10px
color: $color-text-dark-primary
transition: all 150ms ease-in-out
&:before
content: 'Live Preview'
position: absolute
top: -28px
font-size: .9em
color: $color-text-dark-hint
transition: color 150ms ease-in-out
+media-md
visibility: visible
+media-sm
visibility: hidden
p
padding-left: 0
padding-right: 0
.comment-reply-preview:empty
color: transparent
margin: 0 auto
padding: 0 10px
&:before
content: ''
color: transparent
.comment-reply-meta
display: flex
align-items: center
.comment-details
opacity: 0
font-size: .9em
display: flex
align-items: center
justify-content: flex-end
width: 100%
transition: opacity 300ms ease-in-out
.comment-author
padding:
right: 15px
color: $color-text-dark
font:
weight: 300
.author-name
padding-left: 3px
font:
weight: 500
.comment-rules
padding-right: 8px
margin-right: 8px
&:after
content: "·"
position: relative
right: -8px
a
color: $color-text-dark-hint
&:hover
color: $color-primary
i
font-size: 1.5em
position: relative
top: 2px
button.comment-action-submit
margin-left: auto
min-width: 180px
min-height: 30px
transition: all 200ms ease-in-out
@include button-rounded($color-success, 6px)
position: relative
span.hint
position: absolute
top: 35px
left: 50%
transform: translateX(-50%)
font-size: .7em
text-transform: initial
display: block
color: $color-text-dark-hint
&.submitting
background: linear-gradient(to left, transparent 50%, #7AC29A 50%)
background-size: 200% 100%
color: white
animation:
name: background-fill-left-right
duration: .5s
delay: 0
fill-mode: forwards
iteration-count: 1
timing-function: ease-out
i
+spin
&.button-field-error
@include button-rounded($color-danger, 6px)
background: transparent
pointer-events: none
i
position: relative
right: 2px
button.comment-action-cancel
display: none
margin-left: auto
margin-right: 5px
padding:
left: 15px
right: 15px
@include button-rounded($color-text-dark-secondary, 6px)
border-color: transparent
i
margin-right: 0
&.filled
.comment-reply-field
textarea
height: 120px
.comment-reply-meta
.comment-details
opacity: 1
/* Style the comment container when we're replying */
.comment-container + .comment-reply-container
margin-left: 30px
padding-top: 0
.comment-reply-form
.comment-reply-meta
button.comment-action-cancel
display: inline-block
// @import plugins/_ckeditor

93
src/styles/_config.sass Normal file
View File

@@ -0,0 +1,93 @@
$color-background: #eaebec
$color-background-light: lighten($color-background, 5%)
$color-background-dark: darken($color-background, 5%)
$color-background-nav: hsl(hue($color-background), 20%, 25%)
$color-background-nav-light: hsl(hue($color-background), 20%, 35%)
$color-background-nav-dark: hsl(hue($color-background), 20%, 15%)
$font-body: 'Roboto'
$font-headings: 'Lato'
$font-size: 14px
$color-text: #4d4e53
$color-text-dark: $color-text
$color-text-dark-primary: #646469 // rgba($color-text, .87)
$color-text-dark-secondary: #9E9FA2 // rgba($color-text, .54)
$color-text-dark-hint: #BBBBBD // rgba($color-text, .38)
$color-text-light: white
$color-text-light-primary: rgba($color-text-light, .87)
$color-text-light-secondary: rgba($color-text-light, .54)
$color-text-light-hint: rgba($color-text-light, .38)
$color-primary: #68B3C8
$color-primary-light: hsl(hue($color-primary), 30%, 90%)
$color-primary-dark: hsl(hue($color-primary), 80%, 30%)
$color-primary-accent: hsl(hue($color-primary), 100%, 50%)
$color-secondary: #f42942
$color-secondary-light: hsl(hue($color-secondary), 30%, 90%)
$color-secondary-dark: hsl(hue($color-secondary), 80%, 40%)
$color-secondary-accent: hsl(hue($color-secondary), 100%, 50%)
$color-warning: #F3BB45 !default
$color-info: #68B3C8 !default
$color-success: #27AE60 !default
$color-danger: #EB5E28 !default
$color-open_projects: #7eb66f
$color-training: #71c5d3
/* Borrowed from dillo.space :) */
$color_upvote: #ff8b60
$color_downvote: #74a4ff
/* Label Status */
$color-status-todo: lightgray
$color-status-on_hold: #fff2cc
$color-status-in_progress: #fff2cc
$color-status-review: #e4f5f9
$color-status-final: #e7f5d3
$color-status-active: #E6F3FD
$color-status-updated: #e7f5d3
/* Mobile Stuff */
$screen-xs: 480px !default
$screen-xs-min: $screen-xs
$screen-phone: $screen-xs-min
$screen-sm: 768px !default
$screen-sm-min: $screen-sm
$screen-tablet: $screen-sm-min
$screen-md: 1100px !default // 992px
$screen-md-min: $screen-md
$screen-desktop: $screen-md-min
$screen-lg: 1270px !default // 1200px
$screen-lg-min: $screen-lg
$screen-lg-desktop: $screen-lg-min
$screen-xs-max: $screen-sm-min - 1
$screen-sm-max: $screen-md-min - 1
$screen-md-max: $screen-lg-min - 1
/* Project specifics */
$project_nav-width: 240px
$project-sidebar-width: 50px
$project_header-height: 50px
$project_footer-height: 30px
$list-node-children-item-width: 160px
$list-node-children-item-width_list: 48px
$z-index-base: 13
.container
@media (min-width: $screen-sm-min)
width: 750px
@media (min-width: $screen-md-min)
width: 1100px // 970px
@media (min-width: $screen-lg-min)
width: 1270px // 1170px

113
src/styles/_error.sass Normal file
View File

@@ -0,0 +1,113 @@
body.error
width: 100%
height: 100%
#error_container
display: flex
justify-content: center
align-items: center
position: absolute
top: 0
left: 0
right: 0
bottom: 0
background-color: darken($color-background-nav, 3%)
&.standalone
width: 100%
height: 100%
position: fixed
#error_box
.error-lead
padding: 10px 10px
p
display: block
&.extra
padding-top: 0
#error_box
min-width: 60%
max-width: 500px
text-align: center
display: flex
justify-content: center
align-items: center
flex-direction: column
+container-box
box-shadow: 0 0 15px rgba(black, .2)
padding: 0
background-color: $color-background-nav
.error-top-container
position: relative
width: 100%
background-color: $color-background-nav
background: repeating-linear-gradient(-45deg, lighten($color-background-nav, 2%), lighten($color-background-nav, 2%) 10px, $color-background-nav 10px, $color-background-nav 20px)
.error-title
padding: 25px 0 10px 0
color: white
font:
family: $font-headings
size: 2em
weight: 300
.error-lead
width: 100%
padding: 0 25px 25px 0
line-height: 1.6em
color: $color-text-light-primary
font:
family: $font-body
weight: 300
a
text-decoration: none
&:hover
text-decoration: underline
&.extra
padding: 25px 0
font-size: .9em
color: $color-text-light-secondary
hr
margin: 0 0
width: 100%
border: none
height: 3px
background-color: lighten($color-background-nav, 5%)
.buttons
margin: 0 15px 25px 0
a
font-size: 1.2em
margin: 0 15px
&.sign-up
@include button-rounded-filled($color-primary, 999em)
&.sign-in
@include button-rounded($color-background, 999em)
border-color: transparent
#node-overlay
#error_container
@include overlay(rgba($color-background-nav, .8), 0%, transparent, 60%)
#error_box
top: 50px
position: absolute
left: 50%
transform: translateX(-50%)
border-radius: 3px
box-shadow: 0 0 100px rgba(black, .85)
overflow: hidden

View File

@@ -0,0 +1,609 @@
@font-face
font-family: 'pillar-font'
src: url('../font/pillar-font.woff2?89968090') format('woff2')
src: url('../font/pillar-font.woff?89968090') format('woff')
font-weight: normal
font-style: normal
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it.
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be
/*
*@media screen and (-webkit-min-device-pixel-ratio:0) {
* @font-face {
* font-family: 'pillar-font';
* src: url('../font/pillar-font.svg?89968090#pillar-font') format('svg');
* }
*}
[class^="pi-"]:before, [class*=" pi-"]:before
font-family: "pillar-font"
font-style: normal
font-weight: normal
speak: none
display: inline-block
text-decoration: inherit
width: 1em
margin-right: .2em
text-align: center
/* opacity: .8;
/* For safety - reset parent styles, that can break glyph codes
font-variant: normal
text-transform: none
/* fix buttons height, for twitter bootstrap
line-height: 1em
/* Animation center compensation - margins should be symmetric
/* remove if not needed
margin-left: .2em
/* you can be more comfortable with increased icons size
/* font-size: 120%;
/* Font smoothing. That was taken from TWBS
-webkit-font-smoothing: antialiased
-moz-osx-font-smoothing: grayscale
/* Uncomment for 3D effect
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3);
.pi-collection-plus:before
content: '\e800'
/* ''
.pi-log-in:before
content: '\e801'
/* ''
.pi-log-out:before
content: '\e802'
/* ''
.pi-home:before
content: '\e803'
/* ''
.pi-social-twitter:before
content: '\e804'
/* ''
.pi-folder-create:before
content: '\e805'
/* ''
.pi-social-facebook:before
content: '\e806'
/* ''
.pi-license-cc-by:before
content: '\e807'
/* ''
.pi-license-cc:before
content: '\e808'
/* ''
.pi-blender-cloud:before
content: '\e809'
/* ''
.pi-texture:before
content: '\e80a'
/* ''
.pi-folder-texture:before
content: '\e80b'
/* ''
.pi-markdown:before
content: '\e80c'
/* ''
.pi-folder:before
content: '\e80d'
/* ''
.pi-more-vertical:before
content: '\e80e'
/* ''
.pi-blender-network:before
content: '\e810'
/* ''
.pi-blender-logo:before
content: '\e811'
/* ''
.pi-blender-cloud-logo:before
content: '\e812'
/* ''
.pi-notifications-active:before
content: '\e813'
/* ''
.pi-notifications-off:before
content: '\e814'
/* ''
.pi-notifications-none:before
content: '\e815'
/* ''
.pi-happy:before
content: '\e816'
/* ''
.pi-unhappy:before
content: '\e817'
/* ''
.pi-displeased:before
content: '\e818'
/* ''
.pi-grin:before
content: '\e819'
/* ''
.pi-laugh:before
content: '\e81a'
/* ''
.pi-spin:before
content: '\e81b'
/* ''
.pi-search:before
content: '\e81c'
/* ''
.pi-film-thick:before
content: '\e81d'
/* ''
.pi-image:before
content: '\e81e'
/* ''
.pi-cancel-circle:before
content: '\e81f'
/* ''
.pi-thumbs-up:before
content: '\e820'
/* ''
.pi-thumbs-down:before
content: '\e821'
/* ''
.pi-download-cloud:before
content: '\e822'
/* ''
.pi-edit:before
content: '\e823'
/* ''
.pi-document:before
content: '\e824'
/* ''
.pi-file-archive:before
content: '\e825'
/* ''
.pi-angle-double-left:before
content: '\e826'
/* ''
.pi-angle-double-right:before
content: '\e827'
/* ''
.pi-whoosh:before
content: '\e828'
/* ''
.pi-toggle-off:before
content: '\e829'
/* ''
.pi-toggle-on:before
content: '\e82a'
/* ''
.pi-circle-filled:before
content: '\e82b'
/* ''
.pi-circle-empty:before
content: '\e82c'
/* ''
.pi-circle:before
content: '\e82d'
/* ''
.pi-circle-notch:before
content: '\e82e'
/* ''
.pi-circle-dot:before
content: '\e82f'
/* ''
.pi-email:before
content: '\e830'
/* ''
.pi-layout:before
content: '\e831'
/* ''
.pi-plus:before
content: '\e832'
/* ''
.pi-back:before
content: '\e833'
/* ''
.pi-upload-cloud:before
content: '\e834'
/* ''
.pi-vcard:before
content: '\e835'
/* ''
.pi-rss:before
content: '\e836'
/* ''
.pi-share:before
content: '\e837'
/* ''
.pi-angle-down:before
content: '\e838'
/* ''
.pi-angle-left:before
content: '\e839'
/* ''
.pi-angle-right:before
content: '\e83a'
/* ''
.pi-angle-up:before
content: '\e83b'
/* ''
.pi-list:before
content: '\e83c'
/* ''
.pi-credit-card:before
content: '\e83d'
/* ''
.pi-heart:before
content: '\e83e'
/* ''
.pi-heart-filled:before
content: '\e83f'
/* ''
.pi-star:before
content: '\e840'
/* ''
.pi-star-filled:before
content: '\e841'
/* ''
.pi-resize-full:before
content: '\e842'
/* ''
.pi-resize-normal:before
content: '\e843'
/* ''
.pi-comment:before
content: '\e844'
/* ''
.pi-picture:before
content: '\e845'
/* ''
.pi-picture-album:before
content: '\e846'
/* ''
.pi-filter:before
content: '\e847'
/* ''
.pi-filter-remove:before
content: '\e848'
/* ''
.pi-menu:before
content: '\e849'
/* ''
.pi-check:before
content: '\e84a'
/* ''
.pi-cancel:before
content: '\e84b'
/* ''
.pi-info:before
content: '\e84c'
/* ''
.pi-lock:before
content: '\e84d'
/* ''
.pi-lock-open:before
content: '\e84e'
/* ''
.pi-download:before
content: '\e84f'
/* ''
.pi-attention:before
content: '\e850'
/* ''
.pi-play:before
content: '\e851'
/* ''
.pi-trash:before
content: '\e852'
/* ''
.pi-zoom-in:before
content: '\e853'
/* ''
.pi-zoom-out:before
content: '\e854'
/* ''
.pi-paper-plane:before
content: '\e855'
/* ''
.pi-mic-outline:before
content: '\e856'
/* ''
.pi-license-cc-nc:before
content: '\e857'
/* ''
.pi-license-cc-sa:before
content: '\e858'
/* ''
.pi-license-cc-nd:before
content: '\e859'
/* ''
.pi-license-cc-zero:before
content: '\e85a'
/* ''
.pi-folder-special:before
content: '\e85b'
/* ''
.pi-refresh:before
content: '\e85c'
/* ''
.pi-license-publicdomain:before
content: '\e85d'
/* ''
.pi-fire:before
content: '\e85e'
/* ''
.pi-audio:before
content: '\e85f'
/* ''
.pi-link-ext:before
content: '\e860'
/* ''
.pi-document-text:before
content: '\e861'
/* ''
.pi-license-copyright:before
content: '\e862'
/* ''
.pi-blender:before
content: '\e863'
/* ''
.pi-replay:before
content: '\e864'
/* ''
.pi-puzzle:before
content: '\e865'
/* ''
.pi-cog:before
content: '\e866'
/* ''
.pi-move:before
content: '\e867'
/* ''
.pi-warning:before
content: '\e868'
/* ''
.pi-sort:before
content: '\e869'
/* ''
.pi-character:before
content: '\e86a'
/* ''
.pi-tree-flow:before
content: '\e86b'
/* ''
.pi-volume-on:before
content: '\e86c'
/* ''
.pi-volume-off:before
content: '\e86d'
/* ''
.pi-pause:before
content: '\f00e'
/* ''
.pi-globe:before
content: '\f019'
/* ''
.pi-heart-broken:before
content: '\f028'
/* ''
.pi-newspaper:before
content: '\f1ea'
/* ''

1029
src/styles/_homepage.sass Normal file

File diff suppressed because it is too large Load Diff

176
src/styles/_join.sass Normal file
View File

@@ -0,0 +1,176 @@
$slide-height: 600px
.join
position: relative
.slide
position: relative
display: flex
justify-content: center
width: 100%
&.icons
margin: 50px auto 100px auto
flex-direction: row
justify-content: space-around
.join__points_item
display: inline-block
width: 20%
padding: 0 20px
text-align: center
.join__points_item-icon
font-size: 4em
display: block
color: $color-text-dark-secondary
margin: 0 0 15px 0
.join__points_item-title
display: block
font-family: $font-headings
font-size: 1.5em
line-height: 1.2em
color: lighten($color-text, 10%)
.join__points_item-description
margin-top: 25px
display: block
&.image
flex-direction: column
height: $slide-height
width: 100%
background:
repeat: no-repeat
size: cover
color: white
.lead
z-index: 1
max-width: 500px
font-size: 1.2em
padding: 0 50px
text-shadow: 0 0 25px rgba(black, .8)
h1
font:
family: $font-headings
a
color: white
&.right
align-self: flex-end
&.main
text-align: center
align-items: center
h1
z-index: 1
font:
family: $font-headings
font-size: 3.5em
margin: 0
padding: 0 0 15px 0
text-shadow: 0 0 30px rgba(black, .5)
h2
z-index: 1
font:
size: 1.4em
family: $font-headings
margin: 0
padding: 0
weight: 400
max-width: 500px
text-shadow: 0 0 25px rgba(black, .8)
&.subscribe
.box
padding: 15px 65rpx
margin: 25px auto
+container-box
h2
text-align: center
padding: 0 0 15px 0
ul
max-width: 300px
margin: 0 30px
padding: 0
list-style-type: none
li
position: relative
padding: 0
margin: 0
&:after
position: absolute
content: '\f00c'
font-size: 75%
top: 3px
left: -15px
font-family: "FontAwesome"
color: $color-success
a.subscribe
display: inline-block
width: 100%
text-align: center
button.subscribe
color: $color-text-dark
border-color: $color-text-dark
margin: 25px auto 25px auto
transition: opacity .1s
&:hover
opacity: .8
i
padding-left: 10px
.overlay
z-index: 1
.gradient
position: absolute
width: 100%
height: 100%
top: 0
left: 0
right: 0
bottom: 0
z-index: 0
background: linear-gradient(to top, rgba(5,6,6,0.6) 0%, transparent 80%)
&.left
background: linear-gradient(to right, rgba(5,6,6,0.6) 0%, transparent 80%)
&.right
background: linear-gradient(to left, rgba(5,6,6,0.6) 0%, transparent 80%)
button
color: white
border-color: white
padding: 5px 25px
margin: 45px auto 0 auto
font:
family: $font-headings
size: 1.6em
&:hover, &:focus
background-color: rgba(white, .2)
&:active
background-color: rgba(white, .4)
color: white

427
src/styles/_normalize.scss Normal file
View File

@@ -0,0 +1,427 @@
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}

View File

@@ -0,0 +1,284 @@
/* Notifications */
#notifications
display: none
& #notifications-refresh,
& #notifications-markallread
float: right
color: $color-primary
cursor: pointer
padding: 8px 10px 0 10px
margin: 0
height: initial
font:
family: $font-body
text:
shadow: none
transform: initial
&:hover, &:focus
color: lighten($color-primary, 10%)
&:active
color: $color-secondary
#notifications-toggle
font-size: 1.5em
cursor: pointer
position: relative
user-select: none
& .flyout-hat
width: 0
height: 0
position: absolute
border-style: solid
border-width: 0 8px 8px 8px
border-color: transparent transparent white transparent
bottom: -15px
right: 22px
visibility: hidden
&.active
& .flyout-hat
visibility: visible
#notifications-count
opacity: 0
position: absolute
top: 10px
right: 14px
width: 18px
height: 18px
background-color: $color-secondary
border-radius: 50%
text-align: center
color: white
font-size: .5em
font:
weight: bolder
family: sans-serif
padding: 0
user-select: none
transform: scale(0)
transition: transform 250ms ease-in-out
span
position: relative
top: -2px
&.bloom
opacity: 1
transform: scale(1) !important
-webkit-transform: scale(1) !important
#notifications-list
list-style-type: none
padding: 0 5px
margin: 0
& .nc-item, & .nc-loading
position: relative
padding: 10px 0
border-top: thin solid lighten($color-text, 62%)
margin: 0
width: 100%
display: block
clear: both
&:hover
& .nc-read_toggle,
& .nc-subscription_toggle
visibility: visible
&.is_read
opacity: 0.6
color: lighten($color-text, 20%)
&.nc-item-empty
color: lighten($color-text, 20%)
text-align: center
padding: 24px 0
user-select: none
& .nc-link
position: absolute
top: 0
right: 0
bottom: 0
background-color: rgba(0,0,0,.1)
& .nc-avatar
float: left
width: 10%
text-align: center
& img
border-radius: 50%
width: 24px
height: 24px
& .nc-text
width: 90%
& .nc-date
display: block
padding-top: 5px
margin-left: 40px
font-size: .85em
color: lighten($color-text, 20%)
& a
color: lighten($color-text, 20%)
&:hover
color: lighten($color-text, 30%)
& .nc-loading
text-align: center
font-size: 1.5em
color: lighten($color-text, 50%)
& .nc-button
color: $color-text-dark-secondary
text-shadow: none
visibility: hidden
cursor: pointer
display: block
float: right
position: absolute
top: 5px
right: 3px
padding: 5px
&.active
color: $color-primary
i.spin
&:before
content: '\e800'
& .nc-subscription_toggle
top: 25px
right: 4px
#notifications-loader
margin: 5px auto
text-align: center
color: $color-text-dark-hint
font-size: 1.2em
#notification-pop
visibility: hidden
display: none
opacity: 0
background-color: #327588
color: $color-text-light-primary
border-radius: 3px
overflow: hidden
width: 300px
position: fixed
z-index: 9998
bottom: 20px
left: 20px
cursor: pointer
box-shadow: 1px 1px 0 rgba(black, .1), 0 0 25px rgba(black, .2)
transition: opacity 250ms ease-in-out
&:hover
#pop-close
opacity: 1
&.in
opacity: 1
visibility: visible
display: block
animation:
name: grow-bounce-in
duration: 500ms
delay: 0
fill-mode: forwards
iteration-count: 1
timing-function: ease-out
.nc-progress
animation:
name: background-fill-left-right
duration: 10s
delay: 0
fill-mode: forwards
iteration-count: 1
timing-function: linear
&.in.out
animation:
name: grow-bounce-out
duration: 500ms
delay: 0
fill-mode: forwards
iteration-count: 1
timing-function: ease-out
.nc-progress
position: absolute
top: 0
left: 0
width: 100%
height: 2px
background: linear-gradient(to left, transparent 50%, lighten($color-primary, 10%) 50%)
background-size: 200% 100%
color: white
#pop-close
opacity: 0
position: absolute
z-index: 9999
top: 5px
right: 5px
font-size: .8em
color: $color-text-light-hint
cursor: pointer
padding: 3px
&:hover
color: $color-text-light-secondary
.nc-item
padding: 10px
display: flex
align-items: center
a
color: white
.nc-text
width: 90%
padding: 0 10px
font-size: .9em
.nc-author
font-weight: 500
color: white
.nc-avatar
width: 10%
text-align: center
align-self: flex-start
& img
border-radius: 50%
width: 24px
height: 24px
margin: 0 auto
.nc-date
display: block
padding-top: 5px
font-size: .85em
color: $color-text-light-secondary
& a
color: $color-text-light-secondary
&:hover
color: $color-text-light-primary

506
src/styles/_pages.sass Normal file
View File

@@ -0,0 +1,506 @@
/* Pages (services, about, etc) */
#page-header
display: flex
align-items: center
justify-content: center
flex-direction: column
min-height: 350px
color: white
border-bottom: 2px solid $color-background-light
.page-title-icons
font-size: 3em
display: flex
align-items: center
justify-content: center
margin-top: 15px
text-shadow: 2px 2px 1px rgba(black, .2)
.pi-blender-cloud
position: relative
left: -10px
.pi-heart-filled
font-size: .6em
padding: 0 15px
color: $color-danger
animation:
name: grow-bounce-heartbeat
duration: 2.5s
iteration-count: infinite
.page-title
text-align: center
font:
family: $font-headings
size: 2.8em
weight: 300
padding: 15px
text-shadow: 0 0 30px black, 0 0 50px black, 2px 2px 1px rgba(black, .2)
+media-xs
font-size: 2.5em
i.pi-blender-cloud-logo
width: 230px
display: inline-block
em
padding-left: 10px
font-size: .7em
.page-title-summary
text-align: center
padding: 10px 15px
font-size: 1.2em
.page-header-cta-container
display: flex
flex-direction: row
justify-content: center
align-items: center
margin: 30px auto
a.page-header-cta
+button-rounded-filled($color-info, 3px)
font:
size: 1.3em
weight: 500
padding: 4px 50px
display: inline-block
opacity: 1
a.page-header-cta-extra
display: inline-block
font-size: 1.2em
padding: 5px 25px
color: white
#page-content
background-color: white
width: 100%
.page-triplet-container
+container-behavior
padding: 25px 30px
.row, div[class^="col-md"]
+media-md
display: flex
+media-lg
display: flex
.triplet-card
width: 100%
+container-box
display: flex
flex-direction: column
.triplet-card-thumbnail
img
border-top-left-radius: 3px
border-top-right-radius: 3px
width: 100%
border-bottom: 3px solid $color-primary
+img-responsive
.triplet-card-info
padding: 15px
text-align: center
flex: 1
display: flex
flex-direction: column
h3
margin-top: 10px
color: $color-danger
padding-bottom: 20px
position: relative
&:after
content: ''
border-bottom: 2px solid rgba($color-danger, .8)
width: 35px
position: absolute
left: 50%
bottom: 10px
transform: translateX(-50%)
p
display: block
font-size: 1.1em
line-height: 1.5em
color: $color-text-dark-primary
flex: 1
.triplet-cta
display: block
font-size: .9em
padding: 10px 0
color: $color-primary-dark
cursor: pointer
&:hover
text-decoration: underline
$page-card-icon-size: 75px
.page-card-header
position: relative
width: 100%
text-align: center
color: $color-info
display: block
padding: 40px 0 25px 0
margin-bottom: -50px
font:
size: 2.4em
weight: 300
&:after
content: '';
border-bottom: 2px solid rgba(104,179,200,0.8);
width: 120px;
position: absolute;
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
bottom: 7px;
span
display: block
padding: 10px 0
font-size: .6em
color: $color-text-light-secondary
a.page-card-cta
font-size: .5em
min-width: 250px
margin:
right: 0
top: 10px
bottom: 10px
&.dark
color: white
&:after
border-bottom-color: rgba(white, .6)
.page-card
display: flex
align-items: center
justify-content: center
min-height: 200px
margin: 0 auto
background:
color: white
size: cover
position: 50% 50%
+clearfix
+media-sm
min-height: 150px
+media-xs
min-height: 150px
flex-direction: column-reverse
&:nth-child(even)
background-color: $color-background-light
hr
width: 100%
height: 1px
display: block
margin: 10px auto
padding: 0
border: none
background-color: $color-background
&.right
flex-direction: row-reverse
+media-xs
flex-direction: column-reverse
&.dark
background-color: #2b2b2b
&:nth-child(even)
background-color: darken(#2b2b2b, 5%)
a
color: $color-info
hr
background-color: rgba(white, .1)
.page-card-summary
color: #ccc
.page-card-side
width: 50%
padding: 60px 0
max-width: 450px
position: relative
align-self: stretch
+list-bullets
+media-sm
padding: 40px 0
+media-xs
width: 90%
max-width: 90%
padding: 40px 0
.page-card-title
font:
size: 2em
weight: 300
color: $color-danger
padding-bottom: 15px
position: relative
margin-top: 0
&:after
content: ''
border-bottom: 2px solid rgba($color-danger, .8)
width: 35px
position: absolute
left: 0
bottom: 7px
small
color: $color-text-dark-hint
font-size: .8em
padding-left: 5px
.page-card-summary
font-size: 1.4em
color: $color-text-dark-primary
padding: 15px 0 25px 0
.page-card-image
width: 100%
text-align: center
img
max-width: 100%
span
display: block
text-align: center
padding: 20px 0
color: $color-text-dark-primary
.page-card-icon
display: block
position: relative
+position-center-translate
margin: 0 auto
width: $page-card-icon-size * 2
height: $page-card-icon-size * 2
border: 2px solid rgba($color-text-dark-hint, .5)
border-radius: 50%
background:
position: 50% 50%
repeat: no-repeat
size: $page-card-icon-size
+media-sm
width: $page-card-icon-size * 1.5
height: $page-card-icon-size * 1.5
background-size: $page-card-icon-size * 0.75
+media-xs
width: $page-card-icon-size * 1.5
height: $page-card-icon-size * 1.5
background-size: $page-card-icon-size * 0.75
margin: 40px auto
svg
+position-center-translate
/* Subscribe specifics */
&.subscribe
background-color: $color-background-nav
text-align: center
padding: 60px 0
.page-card-side
width: 100%
max-width: 100%
.page-card-title
color: $color-info
text-align: center
padding-bottom: 0
&:after
border: none
.page-card-summary
color: $color-text-light-primary
.page-card-cta
text-align: center
font-size: 1.5em
padding: 10px 40px
margin-right: initial
a.page-card-cta
+button-rounded-filled($color-primary, 3px)
padding: 5px 30px
margin-right: 25px
font-size: 1em
font-weight: 500
display: inline-block
opacity: 1
color: white !important
i
position: relative
left: -10px
margin-right: 0
&.download
+button-rounded-filled($color-success, 3px)
opacity: 1
i
position: relative
left: 0
.page-section-container
background:
color: transparent
size: cover
position: 50% 50%
.page-card, .page-card.dark,
.page-card-header, .page-card-header.dark,
background-color: transparent
.page-card.dark:nth-child(even)
background-color: rgba(black, .1)
border-top: 2px solid rgba(black, .35)
.page-card-side
max-width: 500px
padding: 90px 25px
/* Page specific changes */
.page-triplet-container.homepage
.triplet-card
cursor: pointer
transition: box-shadow 150ms ease-in-out
&:hover
box-shadow: 0 0 25px rgba(black, .2)
.join.navbar-backdrop
position: relative
video
display: block
+position-center-translate
+media-xs
display: none
+media-sm
display: none
+media-md
width: 100%
+media-lg
width: 100%
.services.navbar-backdrop-overlay
background: rgba(black, .5)
.services
.page-card-side
max-width: 500px
padding: 75px 25px
.page-card-header.dark
&:after
border: none
.join.navbar-backdrop-overlay
background: linear-gradient(rgba(white, .3) 0%, transparent 60%)
.join
#page-header
.page-title
font:
size: 2.8em
.page-title-summary
font:
style: normal
size: 1.4em
weight: 400
.page-card
&:nth-child(even)
background-color: white
&.right
background-color: $color-background-light
.page-card-title,
.page-card-summary
padding-left: 25px
.page-card-title:after
left: 25px
&.dark.right
.page-card-title,
.page-card-summary
padding-left: 0
.page-card-title:after
left: 0
&.oneofus
display: block
text-align: center
min-height: initial
padding: 25px
color: transparent
background-color: $color-background-light
border-top: thin solid $color-background
font:
size: 1.4em
transition: color 350ms ease-in-out
a, strong
color: transparent
transition: color 350ms ease-in-out
&.active
color: $color-text-dark-primary
a
color: $color-primary
strong
color: $color-danger
.page-card-header
margin-bottom: 0
.page-card-header.dark
margin-bottom: -50px
&:after
border: none
.page-card-side
+media-xs
width: 100%
max-width: 100%
.page-card-image
img
border-radius: 3px

View File

@@ -0,0 +1,323 @@
.dashboard-container
section#home,
section#projects
background-color: $color-background
border-bottom-left-radius: 3px
border-bottom-right-radius: 3px
section#sub-nav-tabs.home,
section#sub-nav-tabs.projects
background-color: $color-background-light
border-bottom: thin solid $color-background-dark
li.nav-tabs__list-tab
padding: 15px 20px 10px 20px
&:hover
border-color: rgba($color-primary, .4)
&.active
border-color: $color-primary
color: $color-primary-dark
a, i
color: $color-primary-dark
span
color: $color-text-dark-secondary
section#home
background-color: $color-background-dark
section.nav-tabs__tab
display: none
&.active
display: block
#home-images__list
padding: 15px 20px
.home-images__list-item
display: block
break-inside: avoid
padding: 0
margin: 10px auto
background-color: white
border: thin solid darken($color-background-dark, 5%)
border-bottom: 2px solid darken($color-background-dark, 5%)
border-right: 2px solid darken($color-background-dark, 5%)
border-radius: 3px
&:first-child
margin-top: 0
&:last-child
margin-bottom: 0
a.home-images__list-thumbnail
position: relative
display: block
width: 100%
min-height: 100px
img
width: 100%
i
font-size: 1.2em
color: $color-text-dark-hint
+position-center-translate
.home-images__list-details
a.title
display: inline-block
width: 100%
padding: 10px
color: $color-text-dark-primary
font-weight: 500
border-bottom: 2px solid $color-background
font-size: larger
ul.meta
+list-meta
width: 100%
font-size: .85em
background-color: $color-background-light
color: $color-text-dark-secondary
padding: 10px
border-bottom-left-radius: 3px
border-bottom-right-radius: 3px
li.delete-image
a
&.delete-prompt:hover,
&.delete-confirm:hover
color: $color-danger
&.delete-cancel:hover
color: $color-success
&.delete-confirm,
&.delete-cancel
padding-left: 10px
span.delete-confirm
display: none
.tab_header-container
background-color: $color-background
padding: 10px 0
.tab_header-intro_text
width: 75%
.tab_header-intro_icons
width: 25%
.tab_header-intro
display: flex
position: relative
z-index: 1
padding: 15px 35px
margin: 10px -10px
color: white
border-top-left-radius: 3px
border-top-right-radius: 3px
box-shadow: 0 3px 0 rgba(black, .2)
background-position: 50%
h2
font-size: 1.6em
margin: 10px 0
p
line-height: 1.6em
a
color: white
text-decoration: underline
&:before, &:after
content: ""
bottom: -.7em
position: absolute
display: block
border:
style: solid
color: rgba(black, .5) transparent transparent transparent
z-index: -1
&:before
left: 0
border-width: .75em 0 0 .75em
&:after
right: 0
border-width: .75em .75em 0 0
.tab_header-intro_icons
display: flex
align-items: center
justify-content: center
font-size: 2.5em
color: white
.pi-heart-filled
font-size: .6em
padding: 0 15px
color: $color-danger
.pi-blender-cloud,
.pi-picture-album
position: relative
left: -15px
.blender_sync-main
background-color: white
border: thin solid $color-background-dark
border-radius: 3px
margin: 30px 20px 10px 20px
&.empty
color: $color-text-dark-secondary
text-align: center
i.pi-blender
padding-right: 10px
.blender_sync-main-header
padding: 10px 20px
display: flex
align-items: center
.blender_sync-main-title
margin:
top: 10px
bottom: 10px
flex: 1
a.download
display: block
margin: 15px auto 0 auto
width: 200px
@include button-rounded($color-success, 3px)
padding: 5px 10px
.blender_sync-main-last
text-align: right
color: $color-text-dark-secondary
#tab-images
.tab_header-intro
background-position-x: 0
ul#home-text__list
margin: 0
padding: 10px 10px
list-style: none
background-color: $color-background-dark
li.home-text__list-item
display: block
break-inside: avoid
padding: 0
margin: 10px 5px
background-color: white
border: thin solid darken($color-background-dark, 5%)
border-bottom: 2px solid darken($color-background-dark, 5%)
border-right: 2px solid darken($color-background-dark, 5%)
border-radius: 3px
.home-text__list-details
display: flex
flex-direction: column
a.title
padding: 10px
color: $color-text-dark-primary
font-weight: 500
border-bottom: 2px solid $color-background
ul.meta
+list-meta
width: 100%
font-size: .85em
background-color: $color-background-light
color: $color-text-dark-secondary
padding: 10px
border-bottom-left-radius: 3px
border-bottom-right-radius: 3px
ul.projects__list
margin: 0
padding: 5px 0 10px 0
list-style: none
li.projects__list-item
display: flex
align-items: center
padding: 10px 0
margin: 10px 20px
background-color: white
border: thin solid $color-background-dark
border-radius: 3px
box-shadow: 1px 1px 0 rgba(black, .1)
&:hover
cursor: pointer
.projects__list-details a.title
color: $color-text-dark-primary
&.active
border-color: $color-primary
.spin
animation-duration: 5s
.projects__list-details a.title
color: $color-primary
a.projects__list-thumbnail
position: relative
margin: 0 15px
width: 50px
height: 50px
border-radius: 3px
background-color: $color-background-light
display: flex
align-items: center
justify-content: center
img
width: 100%
i
transform: scale(1.2)
color: $color-text-dark-hint
.projects__list-details
display: flex
flex-direction: column
a.title
font-size: 1.2em
padding-bottom: 2px
color: $color-text-dark-primary
ul.meta
font-size: .9em
+list-meta
width: 100%
color: $color-text-dark-primary
li.leave
span
&.user-remove-prompt:hover,
&.user-remove-confirm:hover
color: $color-danger
&.user-remove-cancel:hover
color: $color-success
&.user-remove-prompt,
&.user-remove-confirm,
&.user-remove-cancel
cursor: pointer
&.user-remove
display: none
&.user-remove-confirm,
&.user-remove-cancel
padding-left: 10px

View File

@@ -0,0 +1,164 @@
ul.sharing-users-list
list-style: none
margin: 0
padding: 0
li.sharing-users-item
padding: 10px 0
display: flex
align-items: center
cursor: default
&:hover
.sharing-users-action
visibility: visible
.sharing-users-details
.sharing-users-name
color: $color-text-dark-primary
&.active, &.added
color: $color-primary
.sharing-users-details
.sharing-users-name, .sharing-users-extra
color: $color-primary
.sharing-users-avatar
img
transform: scale(1.2)
&.added
.sharing-users-details
.sharing-users-name, .sharing-users-extra
color: $color-success
&.self
.sharing-users-details
.sharing-users-name
color: $color-info
.sharing-users-action
visibility: visible
.sharing-users-avatar img
border: 2px solid $color-info
.sharing-users-avatar
width: 15%
max-width: 60px
text-align: center
img
margin: 0 auto
width: 40px
height: 40px
border-radius: 50%
overflow: hidden
border: thin solid lighten($color-text-dark-hint, 10%)
transition: transform 350ms ease-in-out
.sharing-users-details
width: 85%
display: flex
flex-direction: column
.sharing-users-name
font:
size: 1.2em
weight: 400
color: $color-text-dark
transition: color 350ms ease-in-out
small
color: $color-text-dark-hint
.sharing-users-extra
font:
size: .9em
weight: 300
color: $color-text-dark-primary
transition: color 350ms ease-in-out
.sharing-users-action
visibility: hidden
span
font-size: .9em
color: $color-text-dark-hint
button
background: none
border: none
&.user-remove
color: $color-danger
&:hover
color: lighten($color-danger, 10%)
.sharing-users-intro,
.sharing-users-info
h4
margin: 0
font-family: $font-body
.sharing-users-info
padding-left: 15px
border-left: thin solid $color-text-dark-hint
+media-sm
margin-top: 25px
h4
padding-bottom: 10px
p
font:
size: 1.1em
weight: 300
.sharing-users-search
.disabled
color: $color-text-dark-secondary
padding-bottom: 10px
.algolia-autocomplete
position: relative
width: 100%
&:before
content: '\e81c'
font-family: 'pillar-font'
position: absolute
font-size: .9em
top: 7px
left: 15px
z-index: 1
color: $color-text-dark-secondary
.algolia-autocomplete .aa-input
padding-left: 35px
width: 100%
.algolia-autocomplete .aa-hint
width: 100%
color: $color-text-dark-hint
.algolia-autocomplete .aa-dropdown-menu
width: 100%
background-color: white
border: 1px solid $color-text-dark-hint
border-top: none
border-bottom-left-radius: 3px
border-bottom-right-radius: 3px
box-shadow: 0 15px 30px rgba(black, .1)
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion
cursor: pointer
padding: 5px 4px
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor,
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor em
background-color: $color-primary
color: white
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion em
font-style: normal
color: $color-primary

2580
src/styles/_project.sass Normal file

File diff suppressed because it is too large Load Diff

784
src/styles/_search.sass Normal file
View File

@@ -0,0 +1,784 @@
$search-hit-width: 160px
$search-hit-width_list: 48px
$search-hit-width_grid: 100px
.search-site-result
width: 100%
&.advanced
width: 350px
z-index: 101
.search-hit
background: white
border-top: thin solid $color-background !important
.search-hit-name
font-weight: 400
padding-top: 8px
color: $color-primary-dark
.search-hit
padding: 0
margin: 0
display: block
color: $color-text
text:
transform: initial
shadow: none
font:
size: .9em
weight: 400
family: $font-body
style: initial
width: 100%
+text-overflow-ellipsis
+clearfix
& em
color: $color-primary-dark
font-style: normal
&:hover
padding: 0
margin: 0
background: $color-background
&:first-child
border: none
.search-hit-name
top: 6px
margin: 0
padding-top: 0
.search-hit-meta
padding-top: 8px
margin: 0
.search-hit-thumbnail
border-radius: 0
transition: margin-right 100ms ease-in-out
.twitter-typeahead
.tt-hint
color: $color-text-light-hint
.tt-menu
top: 47px !important
background-color: white
box-shadow: 1px 1px 0 rgba(black, .05), 0 20px 50px rgba(black, .25)
min-width: 350px
border-bottom-left-radius: 3px
border-bottom-right-radius: 3px
border-top: 3px solid lighten($color-primary, 5%)
overflow: hidden
.tt-suggestion
&:hover, &.tt-cursor
.search-hit
text-decoration: none
background-color: lighten($color-background, 3%)
.search-hit-name
color: $color-background-nav
.search-hit-meta
span.project, span.node_type, span.media
color: $color-background-nav
.search-hit-thumbnail
opacity: .9
margin-right: 15px
&.tt-cursor:hover .search-hit
background-color: lighten($color-background, 5%)
#search-container
display: flex
border-radius: 0
min-height: 500px
background-color: white
+container-behavior
+media-lg
border-top-left-radius: 3px
border-top-right-radius: 3px
padding-left: 0
padding-right: 0
#search-sidebar
width: 20%
background-color: $color-background-nav
border-right: thick solid $color-background-nav-dark
+media-lg
border-top-left-radius: 3px
input.search-field
background-color: $color-background-nav-dark
font-size: 1.1em
color: white
margin-bottom: 10px
border: none
border-bottom: 2px solid rgba($color-primary, .2)
border-radius: 0
width: 100%
padding: 5px 15px
height: 50px
transition: border 100ms ease-in-out
&::placeholder
color: $color-text-light-secondary
&:placeholder-shown
border-bottom-color: $color-primary
&:focus
outline: none
border: none
border-bottom: 2px solid lighten($color-primary, 5%)
.search-list-filters
padding:
left: 10px
right: 10px
.panel.panel-default
margin-bottom: 10px
border-radius: 3px
border: none
background-color: lighten($color-background-nav, 5%)
a
text-decoration: none
.toggleRefine
display: block
padding-left: 7px
color: $color-text-light-primary
text-transform: capitalize
&:hover
text-decoration: none
color: $color-primary
&.refined
color: $color-primary
&:hover
color: $color-danger
span
&:before
/* x icon */
content: '\e84b'
font-family: 'pillar-font'
span
&:before
/* circle with dot */
content: '\e82f'
font-family: 'pillar-font'
position: relative
left: -7px
font-size: .9em
span
&:before
/* empty circle */
content: '\e82c'
font-family: 'pillar-font'
position: relative
left: -7px
font-size: .9em
.facet_count
color: $color-text-light-secondary
.panel-title, .panel-heading
color: $color-text-light-secondary
font:
size: 1em
weight: 500
.panel-body
padding-top: 0
.panel-title
position: relative
&:after
content: '\e83b'
font-family: 'pillar-font'
position: absolute
right: 0
color: $color-background-nav-dark
.collapsed
.panel-title:after
content: '\e838'
.search-list-stats
color: lighten($color-background-nav, 30%)
padding: 10px 15px 0 15px
text-align: center
font-size: .9em
+clearfix
#pagination
ul.search-pagination
text-align: center
list-style-type: none
margin: 0
padding: 0
width: 100%
display: flex
+clearfix
li
display: inline-block
margin: 5px auto
&:last-child
border-color: transparent
a
font-weight: 500
padding: 5px 4px
color: $color-text-light-secondary
&:hover
color: $color-text-light-primary
&.disabled
opacity: .6
&.active a
color: white
#search-list
width: 40%
height: 100%
padding: 0
position: relative
#hits
position: relative
width: 100%
#no-hits
padding: 10px 15px
color: $color-text-dark-secondary
.search-hit
#search-loading
visibility: hidden
background-color: transparent
font:
size: 1.5em
weight: 600
position: absolute
top: 0
left: 0
right: 0
bottom: 0
z-index: $z-index-base + 5
opacity: 0
cursor: default
transition: opacity 50ms ease-in-out
&.active
visibility: visible
opacity: 1
.spinner
color: $color-background-nav
background-color: white
padding: 0
width: 20px
height: 20px
border-radius: 50%
position: absolute
top: 7px
right: 10px
span
padding: 5px
+pulse
#search-details
position: relative
width: 40%
border-left: 2px solid darken(white, 3%)
#search-hit-container
position: absolute // for custom scrollbars
width: 100%
.ps-scrollbar-y-rail
z-index: 1
#error_container
position: relative
background: white
padding: 20px
#search-error
display: none
margin: 20px auto
color: $color-danger
text-align: center
#node-container
width: 100%
max-width: 100%
.node-preview
max-height: initial
&.group
background: white
.overlay
display: none
.node-title
font-size: 1.8em
padding-right: 30px
width: 100%
+text-overflow-ellipsis
.backdrop
opacity: .15
.node-preview-thumbnail
max-height: 320px
.node-children
&.group
background-color: white
position: relative
z-index: 1
.list-node-children-item.browse-list
width: 98%
.list-node-children-item-name
+text-overflow-ellipsis
font-size: 1.1em
.list-node-children-item-meta
+text-overflow-ellipsis
padding-right: 20px
span
&.free
font-size: .7em
.node-details-container
&.group
position: initial
.node-details-description
max-width: 100%
p
margin-bottom: 0
padding-bottom: 10px
.node-details-header
clear: both
width: 100%
max-width: 100%
.node-title
font-size: 1.7em
.node-details-meta
&.header
clear: both
width: 100%
max-width: 100%
padding: 10px
.node-details-meta-list
display: inline-block
float: right
font-size: .9em
.node-details-meta-list-item
&.status, &.author
display: none
&.type, &.length
padding-top: 10px
&.access
padding-top: 5px
&.footer
.node-details-meta-list
.node-details-meta-list-item
&.status
display: none
&.preview
bottom: initial
top: 10px
.node-details-meta-list
.node-details-meta-list-item
&.status, &.author, &.date
display: none
&.texture
.texture-title
font:
size: 2em
family: $font-body
padding: 15px 10px 10px 15px
.node-row
background: white
&.texture-map
width: 100%
section.node-preview.texture
max-height: 180px
max-width: 180px
.node-details-header
padding:
left: 15px
right: 15px
bottom: 0
min-height: initial
.node-title
font-size: 1.5em
.node-details-attributes
font-size: .9em
padding:
left: 15px
right: 15px
bottom: 10px
span.sizes
padding: 0
span.extra
padding: 0
.node-preview.texture
max-width: 128px
img.node-preview-thumbnail
pointer-events: none
.node-details-meta
padding: 15px
.node-details-meta-list
width: 100%
flex-wrap: wrap
margin: 0 auto
.node-details-meta-list-item
text-align: center
margin: 0 auto 0 0
padding: 5px 0
font-size: .9em
&.image.download
width: 100%
padding: 5px 0 0 0
button
width: 100%
.search-hit
float: left
box-shadow: none
border: thin solid transparent
border-top-color: darken(white, 8%)
border-left: 3px solid transparent
color: $color-background-nav
width: 100%
position: relative
margin: 0
padding: 7px 10px 7px 10px
+clearfix
&:first-child
border: thin solid transparent
border-left: 3px solid transparent
&:hover
opacity: 1
text-decoration: none
cursor: default
color: darken($color-primary, 20%)
background-color: $color-background-light
& .search-hit-name i
color: darken($color-primary, 20%)
& .search-hit-thumbnail
& .search-hit-thumbnail-icon
transform: translate(-50%, -50%) scale(1.1)
.search-hit-name
text-decoration: none
&:hover
color: darken($color-primary, 10%)
.search-hit-thumbnail
cursor: pointer
.search-hit-thumbnail-icon
transform: translate(-50%, -50%) scale(1)
&:active
background-color: rgba($color-background, .5)
opacity: .8
color: $color-primary
& .search-hit-name i
color: $color-primary
&:focus
border-color: rgba($color-primary, .2)
/* Class that gets added when we click on the item */
&.active
background-color: lighten($color-background, 2%)
border-left: 3px solid $color-primary
.search-hit-name
color: darken($color-primary, 10%)
.search-hit-meta
span.when
display: none
span.context
display: inline-block
.search-hit-thumbnail
position: relative
float: left
min-width: $search-hit-width_list * 1.49
max-width: $search-hit-width_list * 1.49
height: $search-hit-width_list
border-radius: 3px
background: $color-background
margin-right: 12px
text-align: center
overflow: hidden
+media-xs
display: none
+media-sm
min-width: $search-hit-width_list
max-width: $search-hit-width_list
img
height: $search-hit-width_list
width: auto
.pi-video:before, .pi-file:before,
.pi-group:before
font-family: 'pillar-font'
.pi-video:before
content: '\e81d'
.pi-file:before
content: '\e825'
.pi-group:before
content: '\e80d'
.search-hit-thumbnail-icon
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
color: white
font-size: 1.2em
transition: none
color: $color-text-dark-secondary
.dark
text-shadow: none
font-size: 1.3em
.search-hit-name
position: relative
font-size: 1.1em
color: $color-text-dark-primary
background-color: initial
width: initial
max-width: initial
+text-overflow-ellipsis
padding-top: 5px
&:hover
cursor: pointer
text-decoration: underline
em
color: darken($color-primary, 15%)
font-style: normal
.search-hit-ribbon
+ribbon
right: -30px
top: 5px
span
font-size: 60%
margin: 1px 0
padding: 2px 35px
.search-hit-meta
position: relative
font-size: .9em
color: $color-text-dark-secondary
background-color: initial
padding: 3px 0 0 0
text-decoration: none
+text-overflow-ellipsis
span
&.project
color: $color-text-dark-secondary
margin-right: 3px
&.updated
color: $color-text-dark-hint
&.status
font-size: .8em
color: $color-text-dark-secondary
border: thin solid $color-text-dark-hint
padding: 3px 8px
text-transform: uppercase
border-radius: 3px
margin-right: 5px
&.media, &.node_type
color: $color-text-dark-secondary
text-transform: capitalize
margin: 0 3px
&.when
margin: 0 3px
float: right
display: block
+media-lg
display: block
+media-md
display: block
+media-sm
display: none
+media-xs
display: none
&.context
margin: 0
float: right
display: none
&:hover
cursor: pointer
.search-hit-name-user
color: $color-primary
&.users
em
font-style: normal
color: $color-primary
.search-hit-name
font-size: 1.2em
small
margin-left: 5px
color: $color-text-dark-secondary
.search-hit-roles
font-size: .9em
color: $color-text-dark-secondary
margin-left: 15px
.view-grid
display: flex
justify-content: space-between
padding: 10px !important
.search-hit
width: 30%
clear: none
padding: 0
margin: 5px
overflow: hidden
border-radius: 3px
border-left: none
border: 2px solid $color-background
background-color: $color-background
transition: border-color 150ms ease-in-out
&.active
background-color: $color-primary
border-color: $color-primary
.search-hit-name
font-weight: 500
color: white
background-color: $color-primary
.search-hit-name
font-size: .9em
color: $color-text-dark-primary
width: 100%
position: absolute
bottom: 0
padding: 4px 5px
background-color: $color-background
.search-hit-meta
display: none
.search-hit-thumbnail
display: flex
flex-direction: column
background-color: transparent
width: 100%
max-width: 100%
height: auto
min-height: $search-hit-width_grid
overflow: hidden
margin: 0 auto
+media-xs
display: none
+media-sm
img
+position-center-translate
top: 40%
width: auto
height: auto
.search-hit-thumbnail-icon,
.search-hit-thumbnail-icon .dark
top: 40%
.filter-list
width: 100%
color: $color-text-light-secondary
padding: 5px 0
margin-bottom: 10px
display: flex
align-items: center
ul
margin: 0 0 0 auto
align-self: flex-end
display: inline-block
width: auto
list-style: none
color: white
border-radius: 3px
overflow: hidden
padding: 0
li
display: inline-block
padding: 3px 10px
cursor: pointer
background-color: $color-background-nav-light
&.active
background-color: $color-background-nav-dark

2
src/styles/_stats.sass Normal file
View File

@@ -0,0 +1,2 @@
#stats-container
+container-box

202
src/styles/_user.sass Normal file
View File

@@ -0,0 +1,202 @@
#login-container
width: 60%
margin: 40px auto
padding: 15px 35px 25px 35px
+container-box
.login-title,
.settings-title
text-align: center
color: $color-primary
font:
family: $font-headings
size: 2.2em
weight: 300
padding: 15px 0
.login-info
font:
weight: 300
color: $color-text-dark-primary
text-align: center
padding-bottom: 25px
.buttons
display: flex
margin: 35px auto 35px auto
width: 100%
align-items: center
justify-content: center
.login-button-container
margin: 0 25px
text-align: center
position: relative
a.forgot
width: 100%
position: absolute
bottom: -25px
left: 50%
transform: translateX(-50%)
display: block
font-size: .8em
color: $color-text-dark-secondary
.button-login,
.button-submit
min-width: 200px
@include button-rounded-filled($color-primary, 999em)
.button-register
min-width: 150px
@include button-rounded($color-primary, 999em)
#settings
display: flex
align-items: stretch
margin: 25px auto
#settings-sidebar
width: 30%
+container-box
margin-right: 15px
.settings-content
padding: 0
ul
padding: 0
margin: 0
list-style: none
a
&:hover
text-decoration: none
li
background-color: lighten($color-background, 5%)
li
padding: 25px
margin: 0
border-bottom: thin solid $color-background
border-left: thick solid transparent
transition: all 100ms ease-in-out
i
font-size: 1.1em
padding-right: 15px
.active
li
background-color: lighten($color-background, 5%)
border-left: thick solid $color-info
#settings-container
width: 70%
+container-box
.settings-header
background-color: $color-background
border-top-left-radius: 3px
border-top-right-radius: 3px
.settings-title
padding: 25px 15px 5px 15px
font:
size: 2em
family: $font-headings
weight: 300
.settings-content
padding: 25px
p
color: $color-text-dark-primary
.settings-billing-info
font-size: 1.2em
.subscription-active
color: $color-success
padding-bottom: 20px
.subscription-demo
color: $color-info
margin-top: 0
.subscription-missing
color: $color-danger
margin-top: 0
.button-submit
display: block
clear: both
min-width: 200px
margin: 0 auto
@include button-rounded-filled($color-primary, 999em)
#settings-container
#settings-form
width: 100%
.settings-form
display: flex
align-items: center
justify-content: center
.left, .right
padding: 25px 0
.left
width: 60%
float: left
.right
width: 40%
float: right
text-align: center
.settings-avatar
img
border-radius: 999em
span
display: block
padding: 15px 0
font:
size: .9em
.settings-password
color: $color-text-dark-primary
#user-edit-container
padding: 15px
#user-edit-header
.user-edit-name
font-size: 1.5em
.user-edit-username, .user-edit-email
color: $color-text-dark-secondary
#user-edit-form
padding: 10px 0
#submit_edit_user
+button-rounded-filled($color-success, 999em)
#button-cancel
+button-rounded(#aaa, 999em)
margin: 0 10px
#user-edit-notification
float: right
color: $color-text-dark-secondary
padding: 10px 0
&.success
color: $color-success
&.fail
color: $color-danger

566
src/styles/_utils.sass Normal file
View File

@@ -0,0 +1,566 @@
/* Collection of mixins that can be plugged everywhere */
=clearfix
clear: both
&:after
// Basically same as .clearfix from bootstrap
clear: both
display: block
content: ' '
=container-behavior
width: 100%
+media-lg
width: $screen-lg-min
margin-left: auto
margin-right: auto
box-sizing: border-box
+clearfix
@mixin button-rounded($mixin-color, $roundness)
opacity: .9
padding:
left: 20px
right: 20px
text-transform: uppercase
color: $mixin-color
border: 1px solid $mixin-color
border-radius: $roundness
background-color: transparent
transition: color 350ms ease-out, border 150ms ease-in-out, opacity 150ms ease-in-out, background-color 150ms ease-in-out
text-shadow: none
&:hover
opacity: 1
cursor: pointer
color: $mixin-color
border-color: $mixin-color
background-color: rgba($mixin-color, .1)
&:active, &:focus
outline: none
box-shadow: none
border-color: $mixin-color
background-color: $mixin-color
color: white
i
margin-right: 10px
small
font-size: .6em
@mixin button-rounded-filled($mixin-color, $roundness)
font-family: $font-body
text-transform: uppercase
opacity: .9
padding:
left: 20px
right: 20px
color: white
border: thin solid darken($mixin-color, 5%)
border-radius: $roundness
background: linear-gradient(lighten($mixin-color, 2%), $mixin-color)
text-shadow: 1px 1px 0 rgba(black, .15)
transition: color 150ms ease-out, opacity 100ms ease-in-out, background 100ms ease-out
&:hover
opacity: 1
cursor: pointer
color: white
border-color: lighten($mixin-color, 5%)
background: linear-gradient(lighten($mixin-color, 5%), lighten($mixin-color, 5%))
text-decoration: none
&:active, &:focus
outline: none
box-shadow: none
border-color: lighten($mixin-color, 5%)
background: $mixin-color
color: white
i
margin-right: 10px
small
font-size: .6em
@mixin overlay($from-color, $from-percentage, $to-color, $to-percentage)
position: absolute
top: 0
left: 0
right: 0
bottom: 0
background: linear-gradient(to bottom, $from-color $from-percentage, $to-color $to-percentage)
@mixin stripes($color-light, $color-dark, $deg, $size)
background-size: $size $size
background-image: linear-gradient($deg, $color-light 25%, $color-dark 25%, $color-dark 50%, $color-light 50%, $color-light 75%, $color-dark 75%, $color-dark)
=stripes-animate
animation:
name: background-slide
duration: 1s
delay: 0s
// fill-mode: forwards
iteration-count: infinite
timing-function: linear
=container-box
position: relative
background-color: white
border-radius: 3px
box-shadow: 0 0 0 1px rgba(black, .1), 1px 1px 2px rgba(black, .1)
=text-overflow-ellipsis
overflow: hidden
white-space: nowrap
text-overflow: ellipsis
=position-center-translate
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
=input-generic
color: $color-text-dark
box-shadow: none
font-family: $font-body
border-radius: 3px
border-color: $color-background-dark
background-color: $color-background-light
&:focus
border-color: $color-info
box-shadow: none
=label-generic
color: $color-text-dark
font-family: $font-body
font-weight: 300
@mixin badge($mixin-color, $roundness)
padding:
left: 10px
right: 10px
text-transform: uppercase
color: $mixin-color
border: 1px solid $mixin-color
border-radius: $roundness
i
margin-right: 10px
/* Smallest, like phones on portrait.
** Menu is collapsed, columns stack, no brand */
=media-xs
@media (max-width: #{$screen-tablet - 1px})
@content
/* Small but wide: phablets, iPads
** Menu is collapsed, columns stack, no brand */
=media-sm
@media (min-width: #{$screen-tablet}) and (max-width: #{$screen-desktop - 1px})
@content
/* Tablets portrait.
** Menu is expanded, but columns stack, brand is shown */
=media-md
@media (min-width: #{$screen-desktop})
@content
=media-lg
@media (min-width: #{$screen-lg-desktop})
@content
=media-print
@media print
@content
=spin
animation:
name: spin-once
duration: 1s
delay: 0s
fill-mode: forwards
iteration-count: infinite
timing-function: linear
=spin-once
+spin
animation:
iteration-count: 1
=pulse
animation:
name: pulse
duration: 1s
delay: 0s
fill-mode: forwards
iteration-count: infinite
=pulse-75
animation:
name: pulse-75
duration: 1s
delay: 0
fill-mode: forwards
iteration-count: infinite
@mixin badge($mixin-color, $roundness)
=anim-grow-bounce
animation:
name: grow-bounce
duration: .25s
delay: 0s
fill-mode: forwards
iteration-count: 1
timing-function: linear
.anim-grow-bounce
+anim-grow-bounce
.spin
position: relative
+spin
&:before, &:after
+spin
margin:
left: 0 !important
right: 0 !important
.spinner
position: relative
+spin
&:before, &:after
+spin
@keyframes spin-once
from
transform: rotate(0deg)
to
transform: rotate(360deg)
@keyframes pulse
0
opacity: 1
50%
opacity: 0
100%
opacity: 1
@keyframes pulse-75
0
opacity: 1
50%
opacity: .8
100%
opacity: 1
@keyframes background-fill-left-right
from
background-position: right bottom
to
background-position: left bottom
@keyframes grow-bounce-in
0
transform: scale(0.8)
opacity: 0
50%
transform: scale(1.05)
opacity: 1
85%
transform: scale(1.0)
90%
transform: scale(0.99)
100%
transform: scale(1.0)
@keyframes grow-bounce-out
0
transform: scale(1.0)
opacity: 1
100%
transform: scale(0.9)
opacity: 0
@keyframes background-slide
from
background-position: 0 0
to
background-position: 50px 50px
@keyframes grow-bounce
0
transform: scale(1.0)
opacity: 1
50%
transform: scale(1.01)
opacity: .9
85%
transform: scale(1.0)
90%
transform: scale(0.99)
opacity: 1
100%
transform: scale(1.0)
@keyframes grow-bounce-heartbeat
0
transform: scale(1.0)
85%
transform: scale(1.0)
90%
transform: scale(1.15)
94%
transform: scale(0.9)
96%
transform: scale(1.05)
100%
transform: scale(1.0)
=list-bullets
ul
padding-left: 20px
list-style: none
li:before
content: '·'
font-weight: 400
position: relative
left: -10px
=node-details-description
padding: 15px 0 25px 0
color: darken($color-text-dark, 5%)
font:
family: $font-body
weight: 300
size: 1.2em
word-break: break-word
clear: both
+clearfix
+media-xs
font-size: 1.1em
strong, b
font-weight: 400
a:not([class])
color: $color-text-dark-primary
text-decoration: underline
&:hover
color: $color-primary
p
padding:
left: 20px
right: 20px
margin-bottom: 20px
line-height: 1.5em
word-wrap: break-word
h1, h2, h3, h4, h5, h6
padding:
top: 20px
left: 20px
right: 20px
blockquote
background-color: lighten($color-background, 5%)
text-shadow: 1px 1px 0 rgba(white, .2)
margin:
left: 20px
right: 20px
bottom: 30px
font-size: 1em
p
padding: 0
margin: 0
ul li blockquote
margin:
left: 0
top: 15px
img,
p img,
ul li img
max-width: 100%
padding:
top: 25px
// bottom: 10px
bottom: 25px
h2
margin-bottom: 15px
+media-xs
font-size: 1.5em
/* e.g. YouTube embed */
iframe
margin-top: 20px
width: 100%
max-width: 100%
height: auto
min-height: 354px
+media-sm
iframe
min-height: 314px
+media-xs
iframe
min-height: 314px
iframe[src^="https://w.soundcloud"]
min-height: auto
+list-bullets
ul
padding-left: 40px
margin-bottom: 25px
li
margin-bottom: 7px
img
display: block
padding:
top: 25px
bottom: 10px
ul, ul li ul
margin-top: 15px
padding-left: 20px
code, kbd, pre, samp
font-size: 1.3rem
pre
background-color: lighten($color-background, 5%)
border-color: $color-background
border-radius: 3px
color: $color-text
/* when <pre> is outside <p> */
margin:
left: 20px
right: 20px
pre+p
margin-top: 30px
p+pre
/* a <pre> right after a <p> usually are related, remove some spacing */
margin-top: -10px
p
pre
/* We already have spacing on the sides inside <p> */
margin:
left: 0
right: 0
=markdown-preview-container
border:
top: 1px solid $color-background
bottom: 1px solid $color-background
position: relative
margin: 40px auto 25px auto
padding: 10px 10px 25px 10px
color: $color-text-dark-primary
cursor: default
transition: all 150ms ease-in-out
+node-details-description
// Funny, normalize.css doesn't normalize when it's outside
h1
font-size: 2.8em
h2
margin-bottom: 15px
=ribbon
background-color: $color-success
cursor: default
overflow: hidden
white-space: nowrap
position: absolute
right: -40px
top: 10px
-webkit-transform: rotate(45deg)
-moz-transform: rotate(45deg)
-ms-transform: rotate(45deg)
-o-transform: rotate(45deg)
transform: rotate(45deg)
span
border: thin dashed rgba(white, .5)
color: white
display: block
font-size: 70%
margin: 1px 0
padding: 3px 50px
text:
align: center
transform: uppercase
@mixin text-background($text-color, $background-color, $roundness, $padding)
border-radius: $roundness
padding: $padding
background-color: rgba($background-color, .9)
box-shadow: 0.5em 0 0 rgba($background-color, .9),-0.5em 0 0 rgba($background-color, .9)
box-decoration-break: clone
color: $text-color
=list-meta
margin: 0
padding: 0
list-style: none
color: $color-text-dark-primary
li
display: inline-block
padding-left: 15px
position: relative
&:before
content: '·'
position: relative
top: 1px
left: -7px
color: $color-text-dark-secondary
&:first-child
padding-left: 0
&:before
content: ''
a
color: $color-text-dark-secondary
&:hover
color: $color-primary
/* Bootstrap's img-responsive class */
=img-responsive
display: block
max-width: 100%
height: auto

View File

@@ -0,0 +1,617 @@
@import url(../css/font_attract.css)
$color_text-default: #444
$color_link-default: #337ab7
$color_link-accent: lighten($color_link-default, 20%)
/* Label Status */
$color_status-todo: lightgray
$color_status-on_hold: #fff2cc /* UNUSED */
$color_status-in_progress: #fff2cc
$color_status-review: #e4f5f9
$color_status-final: #e7f5d3
$color_status-active: #E6F3FD
$color_status-updated: #e7f5d3
=text-overflow-ellipsis
overflow: hidden
white-space: nowrap
text-overflow: ellipsis
body
padding-top: 50px
$color-link-default: #337ab7
$color-link-accent: lighten($color-link-default, 20%)
.list-group
margin-top: 20px
.panel .list-group
margin-top: 0
.list-group.list-shots a.list-group-item
padding: 0
.list-group-item img
width: 80px
.stats-knobs div
text-align: center
input.dial
box-shadow: none
transition: none
border-radius: auto
height: auto
cursor: default
input.dial:focus
box-shadow: none
.btn-group-cell .btn
/*width: 80px*/
.btn-group-cell a.btn
/*text-align: left*/
.btn-group-cell a.btn span
text-align: right
.expanded-edit input[type="text"]
height: 14px
font-size: 10px
line-height: 14px
width: 90%
.expanded-edit textarea
width: 90%
height: 99px
/* Edit shot page */
.row-task
margin-bottom: 10px
/* Stats page */
.row.status-progress
margin-bottom: 10px
margin-top: 10px
.progress-bar
&.progress-bar-todo
background-color: #CCC
&.progress-bar-on_hold
background-color: #D9534F
&.progress-bar-final
background-color: #5CB85C
&.progress-bar-review,
&.progress-bar-review_required
background-color: #5BC0DE
&.progress-bar-in_progress
background-color: #F0AD4E
.stats-legend
position: relative
display: inline-block
text-align: center
margin: 20px auto
.stats-legend_item
text-align: center
background-color: #ddd
border-radius: 50%
height: 80px
width: 80px
display: inline-block
float: left
margin: 0 15px
color: white
font-size: .8em
span
position: absolute
top: 50%
transform: translateY(-50%)
width: 80px
display: block
text-align: center
&.in_progress
background-color: #F0AD4E
&.review,
&.review_required
background-color: #5BC0DE
&.final
background-color: #5CB85C
&.on_hold
background-color: #D9534F
&.todo
background-color: #CCC
#shots tbody
font-size: 12px
& td
position: relative
padding: 0
vertical-align: middle
border: none
box-shadow: 0 1px 0 rgba(black, .1)
h3.stats-title
display: inline
ul.files
list-style-type: none
margin: 0
.file.active
background: #DDD
tr.success .make-current
display: none
.table-thumbnail
max-width: 120px
// height: auto
// width: 100%
textarea#description.form-control
min-height: 150px !important
textarea#notes.form-control
min-height: 200px !important
table td:nth-child(2)
+text-overflow-ellipsis
table td:nth-child(3)
//max-width: 300px
+text-overflow-ellipsis
/* Shots Main */
#shots-main
background-color: white
border-left: thin solid #dcdcdc
position: fixed
left: 0
top: 50px
bottom: 0
overflow: auto
padding: 15px
/* Table Shots (DataTable) */
#shots
.shots-shot_thumbnail
text-align: center
.shots-shot_name
font-weight: 500
& a
padding: 12px 8px
.table.dataTable tbody tr a
color: $color-text-dark
& a
color: $color-text-dark
/* Style active rows */
.table.dataTable tbody tr.active:nth-child(odd) td,
.table.dataTable tbody tr.active:nth-child(odd):hover td,
.table.dataTable tbody tr.active:nth-child(even) td,
.table.dataTable tbody tr.active:nth-child(even):hover td,
.table.dataTable tbody tr.active:nth-child(odd) th,
.table.dataTable tbody tr.active:nth-child(odd):hover th,
.table.dataTable tbody tr.active:nth-child(even) th,
.table.dataTable tbody tr.active:nth-child(even):hover th
background-color: inherit !important
color: $color-link-accent !important
font-weight: 600
& a
color: $color-link-accent !important
font-weight: 600
.table.dataTable tbody tr.updated:nth-child(odd) td,
.table.dataTable tbody tr.updated:nth-child(odd) th
animation: fx-updated-row-odd 1.5s ease forwards
.table.dataTable tbody tr.updated:nth-child(even) td,
.table.dataTable tbody tr.updated:nth-child(even) th
animation: fx-updated-row-even 8s ease forwards
$table_row_background_odd: #f9f9f9
$table_row_background_even: white
@-webkit-keyframes fx-updated-row-even
0%
background-color: $table_row_background_even
10%
background-color: $color-status-updated
40%
background-color: $color-status-updated
100%
background-color: $table_row_background_even
@keyframes fx-updated-row-even
0%
background-color: $table_row_background_even
10%
background-color: $color-status-updated
40%
background-color: $color-status-updated
100%
background-color: $table_row_background_even
@-webkit-keyframes fx-updated-row-odd
0%
background-color: $table_row_background_odd
10%
background-color: $color-status-updated
40%
background-color: $color-status-updated
100%
background-color: $table_row_background_odd
@keyframes fx-updated-row-odd
0%
background-color: $table_row_background_odd
10%
background-color: $color-status-updated
40%
background-color: $color-status-updated
100%
background-color: $table_row_background_odd
/* Shots Sidebar */
#shots-sidebar
background-color: white
border-left: thin solid #dcdcdc
position: fixed
right: 0
top: 50px
bottom: 0
position: fixed
overflow-y: auto
overflow-x: hidden
padding: 15px 0 15px 0
#shots-sidebar
/* First hide everything */
& [id^="field_"]
display: none
/* then show some fields */
& #field_status,
& #field_cut_in,
& #field_cut_out,
& #field_description,
& #field_notes,
& #field_revision,
& #field_filepath,
& #field_owners__users,
display: block
& .action_submit,
& .action_cancel
display: none
#field_status
& .form-control
text-transform: capitalize
&:focus
box-shadow: none !important
&.todo
background-color: lightgray
border: thin solid darken(lightgray, 10%)
color: #444
&.in-progress,
&.in_progress,
&.on_hold
background-color: $color-status-in_progress
border: thin solid darken($color-status-in_progress, 20%)
color: #b86811
&.review
background-color: $color-status-review
border: thin solid darken($color-status-review, 10%)
color: #174773
&.done,
&.approved,
&.final,
&.final1,
&.final2
background-color: $color-status-final
border: thin solid darken($color-status-final, 10%)
color: #38761d
&.cbb
border: thin solid #A7CA98
color: #38761d
&.conflict
border: thin solid crimson !important
color: crimson !important
font-weight: bold
background-color: white !important
#field_owners__users
.form-control
min-height: 150px
#shots-sidebar_edit
h2
text-transform: capitalize
#field_description
display: none
#description_print
font-size: 1.2em
padding: 2px
margin: 0
border: 1px solid transparent
color: lighten(black, 40%)
min-height: 100px
cursor: text
#description_form
& textarea
font-size: 1.2em
width: 100%
min-height: 100px
margin: 0
#description_container
position: relative
min-height: 120px
&:hover
.description_toggle
opacity: 1.0
.node__image_preview
width: 100%
.task-update,
.task-success,
.task-error
min-width: 150px
position: absolute
bottom: 0
right: 15px
display: block
.task-success,
.task-error
user-select: none
pointer-events: none
cursor: default
visible: hidden
.task-success
color: #398439
border-color: #398439
.task-error, .task-delete
color: #d9534f
border-color: #d43f3a
.task-delete:hover
background-color: #d9534f
color: white
.task-edit
color: lightblue
border-color: lightblue
&:hover
background-color: CornflowerBlue
color: white
/* Shots List */
.table td
position: relative
.task-add
display: block
visibility: hidden
margin: 0 auto
text-align: center
width: 90%
cursor: pointer
border: thin dashed gray
position: absolute
top: 50%
transform: translateY(-50%)
&:hover
background-color: white
border: thin dashed gray
.table td:hover
.task-add
visibility: visible
.load-task-view
cursor: pointer
display: block
width: 90%
font-size: .9em
font-weight: 400
line-height: 1.5em
margin: 0 auto
user-select: none
position: absolute
top: 50%
transform: translateY(-50%)
&:hover
opacity: .8
.table td.shots-shot_name a
display: block
width: auto
margin: 0 auto
visibility: visible
color: $color-link-default !important
position: relative
top: 17px
transform: translateY(-15px)
.table tr:hover
.load-shot-view
visibility: visible
.node_title
.parent_name
color: lighten($color-text-default, 30%)
border-right: 2px solid lighten($color-text-default, 60%)
padding-right: 15px
margin-right: 5px
.status
.label
display: block
width: 90%
font-size: .9em
font-weight: 400
line-height: 1.5em
margin: 0 auto
.table
tr
color: $color_text-default
tr:hover
opacity: 0.9
cursor: pointer
color: $color-link-accent
a
color: $color-link-accent
tr[data-shot-status ~= "final"]
background-color: $color-status-final !important
// tr[data-shot-status ~= "in_progress"]
// background-color: $color-status-in_progress
tr[data-shot-status ~= "review"],
tr[data-shot-status ~= "review_required"]
background-color: $color-status-review !important
tr[data-shot-status ~= "todo"]
background-color: $color-status-todo !important
tr[data-shot-status ~= "in-progress"],
tr[data-shot-status ~= "in_progress"]
background-color: $color_status-in_progress !important
tr[data-shot-status ~= "on_hold"]
//background-color: $color_status-on_hold !important
color: crimson
tr.active
color: #08C
a
color: #08C !important
.table tr.review_required
background-color: $color-status-review
/* Labels */
.label-todo
background-color: lightgray
border: thin solid darken(lightgray, 10%)
color: #444
&.active
border: 2px dashed darken(lightgray, 15%)
.label-in-progress, .label-in_progress, .label-on_hold
background-color: $color-status-in_progress
border: thin solid darken($color-status-in_progress, 20%)
color: #b86811
&.active
border: 2px dashed darken($color-status-in_progress, 25%)
.label-review
background-color: $color-status-review
border: thin solid darken($color-status-review, 10%)
color: #174773
&.active
border: 2px dashed darken($color-status-review, 25%)
.label-done,
.label-approved,
.label-final,
.label-final1,
.label-final2
background-color: $color-status-final
border: thin solid darken($color-status-final, 10%)
color: #38761d
&.active
border: 2px dashed darken($color-status-final, 15%)
.label-cbb
border: thin solid #A7CA98
color: #38761d
&.active
border: 2px dashed darken(#A7CA98, 5%)
.label-conflict
border: thin solid crimson !important
color: crimson !important
font-weight: bold
background-color: white !important
&:hover
opacity: 1.0 !important
&.active
border: 2px dashed crimson !important
.shots-shot_notes
max-width: 300px

650
src/styles/blog.sass Normal file
View File

@@ -0,0 +1,650 @@
@import _normalize
@import _config
@import _utils
@import _comments
@import _error
@import _base
@import _notifications
/* CSS comes from fontello.com using static/assets/font/config.json */
@import _font-pillar
@import plugins/js_perfectscrollbar
#blog_container
margin: 0
padding:
top: 0
bottom: 15px
display: flex
justify-content: center
+media-xs
flex-direction: column
padding-top: 0
#blog_post-edit-form
padding: 20px
.form-group
position: relative
margin: 0 auto 30px auto
font-family: $font-body
input, textarea, select
+input-generic
&#fileupload
color: $color-info
&.tags .select2-container
.select2-selection
+input-generic
select
text-transform: capitalize
label
+label-generic
text-transform: capitalize
&.error
color: $color-danger
background-color: $color-background-light
padding: 10px 15px
border: thin solid lighten($color-danger, 10%)
border-top: 2px solid $color-danger
border-bottom-left-radius: 3px
border-bottom-right-radius: 3px
label
color: $color-danger
font-weight: 500
&.file
background-color: $color-background-light
padding: 10px 15px
border: thin solid $color-background
border-top: 2px solid $color-info
border-bottom-left-radius: 3px
border-bottom-right-radius: 3px
.form-upload-progress
.form-upload-progress-bar
margin-top: 5px
background-color: $color-success
height: 5px
min-width: 0
border-radius: 3px
.form-group
.node-preview-thumbnail
display: block
&.attachments
+clearfix
.form-control
padding: 0
margin: 0
border: none
list-style-type: none
+clearfix
label[for^='attachments-']
margin-top: 15px
font-weight: 400
div[id^='attachments-']
margin-bottom: 15px
border-top: thin solid $color-text-dark-hint
.form-group.description,
.form-group.summary,
.form-group.content
position: relative
textarea
width: 100%
min-height: 220px
line-height: 1.5em
border: 1px solid $color-background-dark
border-radius: 3px
margin: 0 auto 10px auto
padding: 10px
color: $color-text-dark
transition: all 300ms ease-in-out
resize: vertical
&:focus
border: 1px solid $color-info
outline: none
&.field-error
border-color: $color-danger
.md-preview-loading
position: absolute
left: 85px
padding-top: 5px
font-size: .9em
color: $color-text-dark-secondary
display: none
.node-edit-form-md-preview
+markdown-preview-container
margin:
left: -20px
right: -20px
padding:
top: 20px
left: 0
right: 0
&:before
content: 'Live Preview'
position: absolute
top: -25px
left: 20px
font-size: .7em
color: $color-text-dark-secondary
transition: color 150ms ease-in-out
&:after
content: 'Markdown Supported'
position: absolute
top: -25px
right: 20px
font-size: .7em
color: $color-text-dark-hint
transition: color 150ms ease-in-out
.node-edit-form-md-preview:empty
color: transparent
margin: 0 auto
padding: 0 10px
&:before, &:after
content: ''
color: transparent
#node-edit-form-md-preview
padding: 20px
&.post-create
flex-direction: row-reverse
#blog_post-create-container,
#blog_post-edit-container
padding: 25px
#blog_index-container,
#blog_post-container,
#blog_post-create-container,
#blog_post-edit-container
+container-box
width: 75%
+media-xs
border-radius: 0
width: 100%
clear: both
display: block
+media-sm
width: 60%
+media-md
width: 70%
+media-lg
width: 75%
.blog_index-header
width: 100%
height: 300px
max-height: 300px
overflow: hidden
position: relative
display: block
border-top-left-radius: 3px
border-top-right-radius: 3px
img
+position-center-translate
width: 100%
.blog_index-item
margin: 0 auto 15px auto
&:hover
.item-info a
color: $color-primary
.item-picture
position: relative
width: 100%
max-height: 350px
min-height: 200px
height: auto
overflow: hidden
border-top-left-radius: 3px
border-top-right-radius: 3px
+clearfix
img
+position-center-translate
width: 100%
border-top-left-radius: 3px
border-top-right-radius: 3px
+media-xs
min-height: 150px
+media-sm
min-height: 150px
+media-md
min-height: 250px
+media-lg
min-height: 250px
.item-title
display: block
font:
family: $font-body
size: 2em
weight: 400
padding: 25px 25px 10px 25px
.item-info
color: $color-text-dark-secondary
font-size: 1em
padding:
left: 25px
right: 25px
a
color: $color-text-dark-secondary
&:hover
color: $color-primary
.item-content
+node-details-description
padding: 35px 5px 35px 5px
font-size: 1.3em
+media-xs
padding:
left: 0
right: 0
.item-meta
color: $color-text-dark-secondary
padding:
left: 25px
right: 25px
+media-xs
padding:
left: 10px
right: 10px
.button-create, .button-edit
position: absolute
z-index: 2
top: 20px
right: 20px
+button-rounded-filled($color-success, 6px)
.button-edit
right: 170px
top: 15px
.item-picture+.button-back+.button-edit
right: 20px
top: 20px
#comments-container
padding:
left: 20px
right: 20px
max-width: 680px
margin: 0 auto
+media-lg
padding:
left: 0
right: 0
.comment-reply-form
+media-xs
padding:
left: 0
#blog_post-edit-form
padding: 0
.form-group
label
text-transform: capitalize
.form-group.content
.form-control
min-height: 350px
.form-group.url
label
text-transform: uppercase
.form-group.picture
+text-overflow-ellipsis
img
display: block
max-width: 100%
a
display: block
padding: 10px 0
text-align: right
.original a
color: $color-info
.delete a
color: $color-danger
.form-upload-file-meta
width: initial
#blog_post-edit-title
padding: 0
color: $color-text
font:
family: $font-headings
size: 1.8em
weight: 300
margin: 0 20px 15px 0
#blog_index-sidebar
width: 25%
padding: 0 15px
+media-xs
width: 100%
clear: both
display: block
margin-top: 25px
+media-sm
width: 40%
+media-md
width: 30%
+media-lg
width: 25%
.button-create
display: block
width: 100%
+button-rounded($color-success, 6px)
margin: 0
.button-back
display: block
width: 100%
+button-rounded($color-info, 6px)
margin: 15px 0 0 0
#blog_post-edit-form
.form-group
.form-control
background-color: white
.blog_index-sidebar,
.blog_project-sidebar
+container-box
background-color: lighten($color-background, 5%)
padding: 20px
.blog_project-card
position: relative
width: 100%
border-radius: 3px
overflow: hidden
background-color: white
color: lighten($color-text, 10%)
box-shadow: 0 0 30px rgba(black, .2)
margin:
top: 0
bottom: 15px
left: auto
right: auto
a.item-header
position: relative
width: 100%
height: 100px
display: block
background-size: 100% 100%
overflow: hidden
.overlay
z-index: 1
width: 100%
height: 100px
@include overlay(transparent, 0%, white, 100%)
img.background
width: 100%
// We can't have nice things because of Edge
// filter: blur(5px)
transform: scale(1.4)
.card-thumbnail
position: absolute
z-index: 2
height: 90px
width: 90px
display: block
top: 35px
left: 50%
transform: translateX(-50%)
background-color: white
border-radius: 3px
overflow: hidden
&:hover
img.thumb
opacity: .9
img.thumb
width: 100%
border-radius: 3px
transition: opacity 150ms ease-in-out
+position-center-translate
.item-info
padding: 10px 20px
background-color: white
border-bottom-left-radius: 3px
border-bottom-right-radius: 3px
a.item-title
display: inline-block
width: 100%
padding: 30px 0 15px 0
color: $color-text-dark
text-align: center
font:
size: 1.6em
family: $font-headings
weight: 300
transition: color 150ms ease-in-out
&:hover
text-decoration: none
color: $color-primary
#blog_post-container
.button-create
+button-rounded($color-success, 6px)
.button-back
position: absolute
top: 15px
right: 15px
z-index: 2
+button-rounded-filled($color-info, 6px)
.button-create, .button-edit
position: absolute
z-index: 2
top: 15px
right: 15px
+button-rounded-filled($color-success, 6px)
.button-edit
right: 170px
top: 15px
.button-back+.button-edit
right: 15px
top: 15px
#blog_container
&.cloud-blog
#blog_index-container,
#blog_post-container,
#blog_post-create-container,
#blog_post-edit-container
width: 90%
padding: 25px 30px 20px 30px
#blog_index-container+#blog_index-sidebar,
#blog_post-container+#blog_index-sidebar
display: none
&.cloud-blog #blog_index-container,
&.cloud-blog #blog_post-container,
#blog_index-container, #blog_post-container
padding: 0 0 50px 0
+media-sm
width: 100%
padding: 25px 20px 20px 20px
+media-xs
width: 100%
padding: 0 0 20px 0
.blog_index-item
padding: 25px 80px 20px 80px
position: relative
+media-xs
width: 100%
padding: 25px 0 20px 0
&.list
margin: 0 auto
padding: 15px 0
margin: 0 80px
border-bottom: thin solid $color-background
&:last-child
border-bottom: none
+media-xs
width: 100%
padding: 15px 10px
margin: 0
a.item-title
padding:
top: 0
bottom: 5px
font:
size: 1.6em
weight: 400
family: $font-body
.item-info
color: $color-text-dark-secondary
font-size: .9em
padding:
left: 25px
right: 25px
.item-header
width: 50px
height: 50px
position: absolute
top: 20px
border-radius: 3px
background-color: $color-background
overflow: hidden
img
+position-center-translate
width: 100%
i
+position-center-translate
font-size: 1.2em
color: $color-text-dark-hint
&.nothumb
border-radius: 50%
a.item-title, .item-info
padding-left: 70px
h4.blog_index-title
margin-left: 80px
padding-top: 25px
font:
family: $font-body
color: $color-text-dark-secondary
#blog_index-container,
#blog_post-container
.blog_index-item
padding: 25px 10px 20px 10px
position: relative
+media-xs
padding: 25px 0 20px 0
&.list
padding: 15px 10px
margin: 0 25px
+media-xs
width: 100%
padding: 15px 10px
margin: 0
h4.blog_index-title
margin-left: 35px
&.cloud-blog #blog_post-container,
#blog_post-container
.blog_index-item
padding-top: 55px
font-size: .9em

27
src/styles/main.sass Normal file
View File

@@ -0,0 +1,27 @@
@import _normalize
@import _config
@import _utils
// @import attract/_main
@import _comments
@import _project
@import _project-sharing
@import _project-dashboard
@import _user
@import _join
@import _homepage
@import _error
@import _stats
@import _search
@import _base
@import _notifications
/* services, about, etc */
@import _pages
/* CSS comes from fontello.com using static/assets/font/config.json */
@import _font-pillar
@import plugins/_jstree
@import plugins/js_perfectscrollbar
@import plugins/_js_select2

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,147 @@
$color-scrollbars-base: lighten($color-background-nav, 25%)
.ps-container
.ps-scrollbar-y
opacity: 0
transition: opacity 150ms ease-in-out
&:hover
.ps-scrollbar-y
opacity: 1
/* perfect-scrollbar v0.6.3 */
.ps-container
-ms-touch-action: none
overflow: hidden !important
.ps-container.ps-active-x > .ps-scrollbar-x-rail,
.ps-container.ps-active-y > .ps-scrollbar-y-rail
display: block
.ps-container > .ps-scrollbar-x-rail
display: none
position: absolute
/* please don't change 'position' */
-webkit-border-radius: 4px
-moz-border-radius: 4px
-ms-border-radius: 4px
border-radius: 4px
opacity: 0
-webkit-transition: background-color .2s linear, opacity .2s linear
-moz-transition: background-color .2s linear, opacity .2s linear
-o-transition: background-color .2s linear, opacity .2s linear
transition: background-color .2s linear, opacity .2s linear
bottom: 3px
/* there must be 'bottom' for ps-scrollbar-x-rail */
height: 8px
.ps-container > .ps-scrollbar-x-rail > .ps-scrollbar-x
position: absolute
/* please don't change 'position' */
background-color: $color-scrollbars-base
-webkit-border-radius: 4px
-moz-border-radius: 4px
-ms-border-radius: 4px
border-radius: 4px
-webkit-transition: background-color .2s linear
-moz-transition: background-color .2s linear
-o-transition: background-color .2s linear
transition: background-color .2s linear
bottom: 0
/* there must be 'bottom' for ps-scrollbar-x */
height: 5px
.ps-container > .ps-scrollbar-y-rail
display: none
position: absolute
/* please don't change 'position' */
-webkit-border-radius: 4px
-moz-border-radius: 4px
-ms-border-radius: 4px
border-radius: 4px
opacity: 0.6
-webkit-transition: background-color .2s linear, opacity .2s linear
-moz-transition: background-color .2s linear, opacity .2s linear
-o-transition: background-color .2s linear, opacity .2s linear
transition: background-color .2s linear, opacity .2s linear
right: 3px
/* there must be 'right' for ps-scrollbar-y-rail */
width: 5px
margin:
top: 5px
bottom: 5px
#project_nav.ps-container > .ps-scrollbar-y-rail
margin-top: $project_header-height + 10
.ps-container > .ps-scrollbar-y-rail > .ps-scrollbar-y
position: absolute
/* please don't change 'position' */
background-color: $color-scrollbars-base
-webkit-border-radius: 4px
-moz-border-radius: 4px
-ms-border-radius: 4px
border-radius: 4px
-webkit-transition: background-color .2s linear
-moz-transition: background-color .2s linear
-o-transition: background-color .2s linear
transition: background-color .2s linear, height .2s linear
right: 0
/* there must be 'right' for ps-scrollbar-y */
width: 5px
.ps-container.ps-in-scrolling
pointer-events: none
&.ps-x
.ps-scrollbar-x-rail
background-color: rgba($color-scrollbars-base, .2)
opacity: 0.9
.ps-scrollbar-x
background-color: rgba($color-scrollbars-base, .2)
&.ps-y
.ps-scrollbar-y-rail
background-color: rgba($color-scrollbars-base, .2)
opacity: 0.9
.ps-scrollbar-y
background-color: rgba($color-scrollbars-base, .2)
.ps-container:hover
.ps-scrollbar-x-rail, .ps-scrollbar-y-rail
opacity: 0.6
&:hover
background-color: rgba($color-scrollbars-base, .2)
opacity: 0.9
.ps-scrollbar-x, .ps-scrollbar-y
background-color: $color-scrollbars-base
&.ps-in-scrolling
pointer-events: none
&.ps-x
.ps-scrollbar-x-rail
background-color: rgba($color-scrollbars-base, .2)
opacity: 0.9
.ps-scrollbar-x
background-color: rgba($color-scrollbars-base, .1)
&.ps-y
.ps-scrollbar-y-rail
background-color: rgba($color-scrollbars-base, .2)
opacity: 0.9
.ps-scrollbar-y
background-color: rgba($color-scrollbars-base, .1)

View File

@@ -0,0 +1,449 @@
.select2-container
box-sizing: border-box
display: inline-block
margin: 0
position: relative
vertical-align: middle
.select2-selection--single
box-sizing: border-box
cursor: pointer
display: block
height: 28px
user-select: none
-webkit-user-select: none
.select2-selection__rendered
display: block
padding-left: 8px
padding-right: 20px
overflow: hidden
text-overflow: ellipsis
white-space: nowrap
.select2-selection__clear
position: relative
&[dir="rtl"] .select2-selection--single .select2-selection__rendered
padding-right: 8px
padding-left: 20px
.select2-selection--multiple
box-sizing: border-box
cursor: pointer
display: block
min-height: 32px
user-select: none
-webkit-user-select: none
.select2-selection__rendered
display: inline-block
overflow: hidden
padding-left: 8px
text-overflow: ellipsis
white-space: nowrap
.select2-search--inline
float: left
.select2-search__field
box-sizing: border-box
border: none
font-size: 100%
margin-top: 5px
padding: 0
&::-webkit-search-cancel-button
-webkit-appearance: none
.select2-dropdown
background-color: white
border: 1px solid #aaa
border-radius: 4px
box-sizing: border-box
display: block
position: absolute
left: -100000px
width: 100%
z-index: 1051
.select2-results
display: block
.select2-results__options
list-style: none
margin: 0
padding: 0
.select2-results__option
padding: 6px
user-select: none
-webkit-user-select: none
&[aria-selected]
cursor: pointer
.select2-container--open
.select2-dropdown
left: 0
.select2-dropdown--above
border-bottom: none
border-bottom-left-radius: 0
border-bottom-right-radius: 0
.select2-dropdown--below
border-top: none
border-top-left-radius: 0
border-top-right-radius: 0
.select2-search--dropdown
display: block
padding: 4px
.select2-search__field
padding: 4px
width: 100%
box-sizing: border-box
&::-webkit-search-cancel-button
-webkit-appearance: none
&.select2-search--hide
display: none
.select2-close-mask
border: 0
margin: 0
padding: 0
display: block
position: fixed
left: 0
top: 0
min-height: 100%
min-width: 100%
height: auto
width: auto
opacity: 0
z-index: 99
background-color: #fff
filter: alpha(opacity = 0)
.select2-hidden-accessible
border: 0 !important
clip: rect(0 0 0 0) !important
height: 1px !important
margin: -1px !important
overflow: hidden !important
padding: 0 !important
position: absolute !important
width: 1px !important
.select2-container--default
.select2-selection--single
background-color: #fff
border: 1px solid #aaa
border-radius: 4px
.select2-selection__rendered
color: #444
line-height: 28px
.select2-selection__clear
cursor: pointer
float: right
font-weight: bold
.select2-selection__placeholder
color: #999
.select2-selection__arrow
height: 26px
position: absolute
top: 1px
right: 1px
width: 20px
b
border-color: #888 transparent transparent transparent
border-style: solid
border-width: 5px 4px 0 4px
height: 0
left: 50%
margin-left: -4px
margin-top: -2px
position: absolute
top: 50%
width: 0
&[dir="rtl"] .select2-selection--single
.select2-selection__clear
float: left
.select2-selection__arrow
left: 1px
right: auto
&.select2-container--disabled .select2-selection--single
background-color: #eee
cursor: default
.select2-selection__clear
display: none
&.select2-container--open .select2-selection--single .select2-selection__arrow b
border-color: transparent transparent #888 transparent
border-width: 0 4px 5px 4px
.select2-selection--multiple
background-color: white
border: 1px solid #aaa
border-radius: 4px
cursor: text
.select2-selection__rendered
box-sizing: border-box
list-style: none
margin: 0
padding: 0 5px
width: 100%
.select2-selection__placeholder
color: #999
margin-top: 5px
float: left
.select2-selection__clear
cursor: pointer
float: right
font-weight: bold
margin-top: 5px
margin-right: 10px
.select2-selection__choice
background-color: #e4e4e4
border: 1px solid #aaa
border-radius: 4px
cursor: default
float: left
margin-right: 5px
margin-top: 5px
padding: 0 5px
.select2-selection__choice__remove
color: #999
cursor: pointer
display: inline-block
font-weight: bold
margin-right: 2px
&:hover
color: #333
&[dir="rtl"] .select2-selection--multiple
.select2-selection__choice, .select2-selection__placeholder, .select2-search--inline
float: right
.select2-selection__choice
margin-left: 5px
margin-right: auto
.select2-selection__choice__remove
margin-left: 2px
margin-right: auto
&.select2-container--focus .select2-selection--multiple
border: solid $color-text-dark 1px
outline: 0
&.select2-container--disabled
.select2-selection--multiple
background-color: #eee
cursor: default
.select2-selection__choice__remove
display: none
&.select2-container--open
&.select2-container--above
.select2-selection--single, .select2-selection--multiple
border-top-left-radius: 0
border-top-right-radius: 0
&.select2-container--below
.select2-selection--single, .select2-selection--multiple
border-bottom-left-radius: 0
border-bottom-right-radius: 0
.select2-search--dropdown .select2-search__field
border: 1px solid #aaa
.select2-search--inline .select2-search__field
background: transparent
border: none
outline: 0
box-shadow: none
-webkit-appearance: textfield
.select2-results > .select2-results__options
max-height: 200px
overflow-y: auto
.select2-results__option
&[role=group]
padding: 0
&[aria-disabled=true]
color: #999
&[aria-selected=true]
background-color: #ddd
.select2-results__option
padding-left: 1em
.select2-results__group
padding-left: 0
.select2-results__option
margin-left: -1em
padding-left: 2em
.select2-results__option
margin-left: -2em
padding-left: 3em
.select2-results__option
margin-left: -3em
padding-left: 4em
.select2-results__option
margin-left: -4em
padding-left: 5em
.select2-results__option
margin-left: -5em
padding-left: 6em
.select2-results__option--highlighted[aria-selected]
background-color: #5897fb
color: white
.select2-results__group
cursor: default
display: block
padding: 6px
.select2-container--classic
.select2-selection--single
background-color: #f7f7f7
border: 1px solid #aaa
border-radius: 4px
outline: 0
background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%)
background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%)
background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%)
background-repeat: repeat-x
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)
&:focus
border: 1px solid #5897fb
.select2-selection__rendered
color: #444
line-height: 28px
.select2-selection__clear
cursor: pointer
float: right
font-weight: bold
margin-right: 10px
.select2-selection__placeholder
color: #999
.select2-selection__arrow
background-color: #ddd
border: none
border-left: 1px solid #aaa
border-top-right-radius: 4px
border-bottom-right-radius: 4px
height: 26px
position: absolute
top: 1px
right: 1px
width: 20px
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%)
background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%)
background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%)
background-repeat: repeat-x
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)
b
border-color: #888 transparent transparent transparent
border-style: solid
border-width: 5px 4px 0 4px
height: 0
left: 50%
margin-left: -4px
margin-top: -2px
position: absolute
top: 50%
width: 0
&[dir="rtl"] .select2-selection--single
.select2-selection__clear
float: left
.select2-selection__arrow
border: none
border-right: 1px solid #aaa
border-radius: 0
border-top-left-radius: 4px
border-bottom-left-radius: 4px
left: 1px
right: auto
&.select2-container--open
.select2-selection--single
border: 1px solid #5897fb
.select2-selection__arrow
background: transparent
border: none
b
border-color: transparent transparent #888 transparent
border-width: 0 4px 5px 4px
&.select2-container--above .select2-selection--single
border-top: none
border-top-left-radius: 0
border-top-right-radius: 0
background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%)
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%)
background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%)
background-repeat: repeat-x
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)
&.select2-container--below .select2-selection--single
border-bottom: none
border-bottom-left-radius: 0
border-bottom-right-radius: 0
background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%)
background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%)
background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%)
background-repeat: repeat-x
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)
.select2-selection--multiple
background-color: white
border: 1px solid #aaa
border-radius: 4px
cursor: text
outline: 0
&:focus
border: 1px solid #5897fb
.select2-selection__rendered
list-style: none
margin: 0
padding: 0 5px
.select2-selection__clear
display: none
.select2-selection__choice
background-color: #e4e4e4
border: 1px solid #aaa
border-radius: 4px
cursor: default
float: left
margin-right: 5px
margin-top: 5px
padding: 0 5px
.select2-selection__choice__remove
color: #888
cursor: pointer
display: inline-block
font-weight: bold
margin-right: 2px
&:hover
color: #555
&[dir="rtl"] .select2-selection--multiple
.select2-selection__choice
float: right
margin-left: 5px
margin-right: auto
.select2-selection__choice__remove
margin-left: 2px
margin-right: auto
&.select2-container--open
.select2-selection--multiple
border: 1px solid #5897fb
&.select2-container--above .select2-selection--multiple
border-top: none
border-top-left-radius: 0
border-top-right-radius: 0
&.select2-container--below .select2-selection--multiple
border-bottom: none
border-bottom-left-radius: 0
border-bottom-right-radius: 0
.select2-search--dropdown .select2-search__field
border: 1px solid #aaa
outline: 0
.select2-search--inline .select2-search__field
outline: 0
box-shadow: none
.select2-dropdown
background-color: white
border: 1px solid transparent
.select2-dropdown--above
border-bottom: none
.select2-dropdown--below
border-top: none
.select2-results > .select2-results__options
max-height: 200px
overflow-y: auto
.select2-results__option
&[role=group]
padding: 0
&[aria-disabled=true]
color: grey
.select2-results__option--highlighted[aria-selected]
background-color: #3875d7
color: white
.select2-results__group
cursor: default
display: block
padding: 6px
&.select2-container--open .select2-dropdown
border-color: #5897fb

View File

@@ -0,0 +1,267 @@
/* jsTree overrides */
$tree-color-text: $color-text-light-primary
$tree-color-highlight: white
$tree-color-highlight-background: $color-primary
.jstree-default
font-weight: 400
.jstree-loading
padding: 5px
color: $color-text-light-secondary
.jstree-default-responsive .jstree-anchor
+media-sm
font-weight: normal !important
text-shadow: none !important
.jstree-default .jstree-node
max-width: 100% // Needed for ellipsis on long names
padding: 0 !important
margin: 0 !important
font-weight: 400 !important
color: $tree-color-text
min-height: 12px !important
line-height: 12px !important
+media-xs
width: 100%
.ps-active-y
.jstree-default .jstree-node
max-width: 97%
.jstree-node.jstree-open
background-color: darken($color-background-nav, 2%) !important
.jstree-node.jstree-open > .jstree-anchor:first-of-type
color: white !important
background-color: darken($color-background-nav, 2%) !important
.jstree-default .jstree-node.jstree-open,
.jstree-default .jstree-node.jstree-leaf
// border-left: 2px solid lighten($color-background-nav, 15%) /* Whatever after the top level */
.jstree-default .jstree-node.jstree-closed
// border-left: 2px solid lighten($color-background-nav, 20%) /* Closed Folder */
.jstree-default .jstree-node.jstree-closed .jstree-icon.jstree-ocl,
.jstree-default .jstree-node.jstree-open .jstree-icon.jstree-ocl
position: absolute
z-index: 1
opacity: 0
min-width: 30px
float: left
.jstree-default .jstree-ocl:before
+media-xs
font-weight: bold
font-size: 100%
top: 10%
left: 46%
.jstree-anchor
padding-right: 5px
+media-xs
width: 98%
padding: 0 !important
.jstree-default .jstree-node.jstree-closed .jstree-icon.jstree-ocl + .jstree-anchor,
.jstree-default .jstree-node.jstree-open .jstree-icon.jstree-ocl + .jstree-anchor
padding-left: 28px !important
.jstree .jstree-open > .jstree-children
padding-top: 0 !important
.jstree-default .jstree-anchor /* The text of the last level item */
width: 100%
padding-left: 28px !important
height: inherit !important
line-height: 26px !important
white-space: nowrap
text-overflow: ellipsis
overflow: hidden
border-bottom: thin solid transparent
transition: none
.jstree-default li
transition: none
/* expanded item, like a folder */
.jstree-default .jstree-open > .jstree-ocl,
.jstree-default .jstree-open > .jstree-anchor,
.jstree-default .jstree-open > .jstree-ocl .jstree-icon.jstree-themeicon,
.jstree-default .jstree-open > .jstree-anchor .jstree-icon.jstree-themeicon
color: $tree-color-text !important
/* active item text */
.jstree-default .jstree-clicked,
.jstree-default .jstree-clicked > .jstree-ocl
background-color: transparent !important
border-radius: 0
box-shadow: none
border-bottom: thin solid transparent
// max-width: 95%
.jstree-default .jstree-leaf .jstree-clicked
width: 100% !important
/* hovered text */
.jstree-default .jstree-hovered,
.jstree-default .jstree-open .jstree-hovered,
.jstree-default .jstree-hovered .jstree-icon.jstree-themeicon
color: $tree-color-highlight !important
.jstree-default .jstree-hovered
background-color: rgba($tree-color-highlight, .1) !important
/* active item text + icon */
.jstree-default .jstree-clicked,
.jstree-default .jstree-clicked > .jstree-ocl,
.jstree-default .jstree-clicked .jstree-icon.jstree-themeicon
color: $tree-color-highlight !important
.jstree-leaf[aria-selected='true']
a.jstree-anchor.jstree-clicked
background-color: rgba($tree-color-highlight-background, .5) !important
padding-left: 28px !important
.jstree-default li[aria-selected='true']
a.jstree-anchor
padding-right: 15px
&:after
content: '\e83a'
font-family: 'pillar-font'
color: $tree-color-highlight
position: absolute
right: 7px
top: 1px
li
a.jstree-anchor:after
content: ''
.jstree-default li[aria-expanded='true']
background-color: transparent
a.jstree-anchor.jstree-clicked
background-color: rgba($tree-color-highlight-background, .5) !important
a.jstree-anchor.jstree-clicked+ul li a.jstree-anchor.jstree-clicked
background-color: rgba($tree-color-highlight-background, .7) !important
a.jstree-anchor.jstree-clicked+ul li a.jstree-anchor.jstree-clicked+ul li a.jstree-anchor.jstree-clicked
background-color: rgba($tree-color-highlight-background, .8) !important
a.jstree-anchor+ul
border:
top: none !important
bottom: none !important
a.jstree-anchor
color: darken($color-text-light-primary, 10%) !important
&.jstree-hovered,
&.jstree-clicked
color: white !important
&+ul
a.jstree-anchor
color: darken($color-text-light-primary, 10%) !important
&.jstree-hovered,
&.jstree-clicked
color: white !important
/* hover an active item */
.jstree-default .jstree-clicked.jstree-hovered,
.jstree-default .jstree-clicked.jstree-hovered .jstree-icon.jstree-themeicon
color: lighten($tree-color-highlight, 5%) !important
box-shadow: none
.jstree-anchor > .jstree-themeicon
margin-right: 10px !important
margin-left: 20px !important
.jstree-default .jstree-hovered,
.jstree-default .jstree-hovered .jstree-ocl
background-color: transparent !important
box-shadow: none
color: white !important
.jstree-default .jstree-hovered
box-shadow: none
.jstree-default .jstree-node,
.jstree-default .jstree-icon
background-image: none !important
position: relative
.jstree-default .jstree-icon /* Folder that you click to collapse/expand */
width: 20px !important
text-align: left !important
background-color: transparent !important
position: absolute
i.jstree-icon.jstree-ocl
height: 26px !important
color: rgba($tree-color-text, .5) !important
+media-xs
height: 40px !important
.jstree-default .jstree-icon:empty
line-height: 26px
left: 5px
.jstree-children .jstree-open i.jstree-icon.jstree-ocl
height: 26px !important
.jstree-anchor > .jstree-themeicon
margin-right: 3px !important
margin-left: 0 !important
color: rgba($tree-color-text, .7) !important
font-size: 95% !important
.jstree-open > .jstree-anchor > .jstree-themeicon
color: $color-primary !important
.jstree-leaf > .jstree-clicked > .jstree-themeicon
color: $color-secondary !important
.jstree-leaf .jstree-icon /* Icon of the files (not nodes) */
opacity: 0
width: 0px !important
.jstree-leaf .jstree-icon.jstree-themeicon
opacity: 1
width: 24px !important
color: lighten($color-text, 30%) !important
&.pi-image
font-size: .85em !important
.jstree .jstree-open > .jstree-children > .jstree-node
padding-left: 15px !important
.jstree-icon:empty
left: 20px !important
// Tweaks for specific icons
&.pi-file-archive
left: 22px !important
&.pi-folder
left: 21px !important
font-size: .9em !important
&.pi-film-thick
left: 22px !important
font-size: .85em !important
.jstree-anchor
border-left: thin solid lighten($color-background-nav, 5%)
/* /jsTree overrides */

View File

@@ -0,0 +1,19 @@
@import _normalize
@import _config
@import _utils
@import _comments
@import _project
@import _project-sharing
@import _project-dashboard
@import _error
@import _base
@import _notifications
@import _search
@import _font-pillar
@import plugins/_jstree
@import plugins/js_perfectscrollbar
@import plugins/_js_select2

213
src/styles/theatre.sass Normal file
View File

@@ -0,0 +1,213 @@
@import _normalize
@import _config
@import _utils
@import _font-pillar
@import _base
@import _comments
@import _notifications
@import plugins/js_perfectscrollbar
$color-theatre-background: #222
$color-theatre-background-light: lighten($color-theatre-background, 5%)
$color-theatre-background-dark: darken($color-theatre-background, 5%)
$theatre-width: 350px
body.theatre,
body.theatre .container-page
background-color: $color-theatre-background
.navbar-transparent
+media-lg
background-color: $color-background-nav
background-image: none
a.navbar-item.info
font-size: 1.4em
.page-content
position: absolute
top: 0
left: 0
right: 0
bottom: 0
display: flex
align-items: center
justify-content: center
.page-body
height: 100%
width: 100%
#theatre-container
display: flex
position: relative
height: 100%
overflow: hidden
#theatre-media
display: flex
align-items: center
justify-content: center
height: 100%
width: 100%
padding: 25px
position: relative
&:hover
ul#theatre-tools
display: block
img
display: block
border: thin solid $color-theatre-background-light
box-shadow: 1px 1px 10px rgba(black, .2)
max-width: 100%
max-height: 100%
height: auto
margin: 0 auto
+position-center-translate
&.zoomed-out
cursor: zoom-in
&.zoomed-in
display: block
padding: 0
img
cursor: zoom-out
position: initial
top: initial
left: initial
transform: initial
height: initial
width: initial
max-width: initial
max-height: initial
ul#theatre-tools
display: none
position: fixed
z-index: 9
bottom: 15px
right: 15px
margin: 0
padding: 0
list-style: none
color: white
background-color: rgba(black, .5)
border-radius: 3px
box-shadow: 0 0 15px rgba(black, .2)
li
display: inline-block
a, span
display: block
padding: 10px
cursor: default
color: white
&:hover a, &:hover span
color: $color-primary
&.download
a
cursor: pointer
&:hover
color: $color-primary
&.disabled
a
cursor: not-allowed
color: $color-text-dark-hint
&.active
background-color: rgba(black, .8)
a, span
color: $color-primary
&.theatre-tool-resize.active span i:before
content: '\e843'
&.with-info
#theatre-media ul#theatre-tools
right: $theatre-width + 15
#theatre-info
right: 0
visibility: visible
position: relative
#comments-container #comments-list .comment-reply-container
.comment-reply-form .comment-reply-field textarea
display: block
#theatre-info
visibility: hidden
width: $theatre-width
min-width: $theatre-width
height: 100%
position: relative
top: 0
right: -$theatre-width
background-color: white
border-left: 2px solid $color-background-nav
transition: right 200ms ease-in-out
position: absolute
.theatre-info-header
border-bottom: thin solid $color-background
padding-bottom: 10px
.theatre-info-title
padding: 20px 10px 5px 20px
font:
size: 1.2em
weight: 500
.theatre-info-user,
.theatre-info-date
display: inline-block
padding: 0 0 0 20px
font-size: .9em
color: $color-text-dark-secondary
ul.theatre-info-details
padding: 10px 20px 0 20px
margin: 0
list-style: none
color: $color-text-dark-primary
li
display: flex
padding: 2px 0
span
display: inline-block
flex: 2
&:first-child
flex: 1
#comments-container
height: 100%
padding: 0 15px
#comments-list
#comments-list-header
#comments-list-title
font-size: 1.2em
.comment-container,
.comment-reply-container
padding-top: 10px
padding-bottom: 10px
.comment-reply-form
.comment-reply-field textarea
display: none
.comment-reply-preview
padding-top: 25px
.comment-reply-meta
.comment-author,
.comment-rules
display: none
button.comment-action-submit
min-width: auto
font-size: .9em

75
src/styles/vrview.sass Normal file
View File

@@ -0,0 +1,75 @@
html, body
background-color: #000
color: #eee
margin: 0px
padding: 0px
position: fixed
overflow: hidden
top: 0
bottom: 0
left: 0
right: 0
.dialog
display: none
align-items: center
justify-content: center
font-family: sans-serif
font-size: 170%
position: absolute
width: 100%
height: 100%
background: rgba(255, 255, 255, 0.2)
-webkit-user-select: none
-moz-user-select: none
user-select: none
&.visible
display: flex
.wrap
padding: 30px 60px
margin: 20px auto
width: 60%
background: rgba(0, 0, 0, 0.8)
border-radius: 5px
h1
margin: 0
a
color: skyblue
&:visited
color: skyblue
#title
-webkit-user-select: none
-moz-user-select: none
user-select: none
position: absolute
top: 10%
width: 100%
font-size: 3em
font-family: 'Dosis'
opacity: 1
-webkit-transition: all 0.3s ease-in-out
-moz-transition: all 0.3s ease-in-out
transition: all 0.3s ease-in-out
margin: 0 0.5rem
&.hidden
opacity: 0
#watermark img
position: fixed
overflow: hidden
left: 0
bottom: 0
opacity: 0.3
width: 24px
height: 24px
padding: 12px
&:hover
opacity: 1
-webkit-filter: drop-shadow(white 0 0 5px)
canvas
cursor: -webkit-grab
&:active
cursor: -webkit-grabbing

View File

@@ -0,0 +1,27 @@
| {% macro add_new_menu(node_types) %}
| {% for node_type in node_types %}
| {% if node_type['name'] in ['group', 'group_texture', 'group_hdri', 'asset', 'texture', 'page', 'hdri'] %}
| {% set node_type_name = node_type['name'] %}
| {% if node_type_name == 'group' %}
| {% set node_type_name = 'folder' %}
| {% endif %}
li(class="button-{{ node_type['name'] }}")
a.item_add_node(
href="#",
title="{{ node_type['description'] }}",
data-node-type-name="{{ node_type['name'] }}",
data-toggle="tooltip",
data-placement="left")
i.pi(class="icon-{{ node_type['name'] }}")
| {% if node_type_name == 'group_texture' %}
| Texture Folder
| {% elif node_type_name == 'group_hdri' %}
| HDRi Folder
| {% else %}
| {{ node_type_name }}
| {% endif %}
| {% endif %}
| {% endfor %}
| {% endmacro %}

View File

@@ -0,0 +1,18 @@
| {% macro render_file_uploader() %}
#fileUploaderModal.modal.fade(
tabindex="-1", role="dialog", aria-labelledby="fileUploaderModalLabel", aria-hidden="true")
.modal-dialog
.modal-content
.modal-header
button.close(type="button", data-dismiss="modal", aria-label="Close")
span(aria-hidden="true") &times;
h4.modal-title Upload file
.modal-body
| {% include '_macros/include _file_uploader_form.html' %}
.modal-footer
button.btn.btn-default(type="button", data-dismiss="modal") Close
| {% endmacro %}

View File

@@ -0,0 +1,70 @@
#node-add-container
#node-add-header
.node-add-title Upload Assets
#node-add-form
p.
This is the first step in the asset creation process. Once files are uploaded,
they are stored in a temp location until further actions are taken.
You can upload multiple assets simultaneously, but you can only edit one at a time.
hr
// The file upload form used as target for the file upload widget
form#fileupload(action="{{url_for('files.upload')}}", method='POST', enctype='multipart/form-data')
// Redirect browsers with JavaScript disabled to the origin page
noscript
input(type='hidden', name='redirect', value="{{url_for('files.upload')}}")
// The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload
.row.fileupload-buttonbar
.col-lg-7
// The fileinput-button span is used to style the file input field as button
span.btn.btn-success.fileinput-button
i.pi-plus
span Add files...
input(type='file', name='file', multiple='')
button.btn.btn-primary.start(type='submit')
i.pi-upload
span Start upload
button.btn.btn-warning.cancel(type='reset')
i.pi-cancel
span Cancel upload
button.btn.btn-danger.delete(type='button')
i.pi-trash
span Delete
input.toggle(type='checkbox')
// The global file processing state
span.fileupload-process
// The global progress state
.col-lg-5.fileupload-progress.fade
// The global progress bar
.progress.progress-striped.active(role='progressbar', aria-valuemin='0', aria-valuemax='100')
.progress-bar.progress-bar-success(style='width:0%;')
// The extended global progress state
.progress-extended  
// The table listing the files available for upload/download
table.table(role='presentation')
tbody.files
br
h3 Notes
ul
li
| The maximum file size for this interface is
strong {{ config.MAX_CONTENT_LENGTH | filesizeformat }}
| .
li
| Only the following formats are allowed:
br
strong.text-uppercase
| {% for f in config.ALLOWED_EXTENSIONS %}
| {{f}}
| {% endfor %}

View File

@@ -0,0 +1,133 @@
| {% raw %}
// The template to display files available for upload
script#template-upload(type="text/x-tmpl").
| {% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-upload fade">
<td>
<span class="preview"></span>
</td>
<td>
<span class="name">{%=file.name%}</span>
<strong class="error text-danger"></strong>
</td>
<td>
<p class="size">Processing...</p>
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="progress-bar progress-bar-success" style="width:0%;"></div></div>
</td>
<td>
{% if (!i && !o.options.autoUpload) { %}
<button class="btn btn-primary start" disabled>
<i class="ion-upload"></i>
<span>Start</span>
</button>
{% } %}
{% if (!i) { %}
<button class="btn btn-warning cancel">
<i class="ion-close-round"></i>
<span>Cancel</span>
</button>
{% } %}
</td>
</tr>
| {% } %}
// The template to display files available for download
script#template-download(type="text/x-tmpl").
| {% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-download fade">
<td>
<span class="preview">
{% if (file.thumbnailUrl) { %}
<a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" data-gallery><img src="{%=file.thumbnailUrl%}"></a>
{% } %}
</span>
</td>
<td>
<span class="name">
{% if (file.url) { %}
<a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" {%=file.thumbnailUrl?'data-gallery':''%}>{%=file.name%}</a>
{% } else { %}
<span>{%=file.name%}</span>
{% } %}
</span>
{% if (file.error) { %}
<div><span class="label label-danger">Error</span> {%=file.error%}</div>
{% } %}
</td>
<td>
<span class="size">{%=o.formatFileSize(file.size)%}</span>
</td>
<td>
{% if (file.deleteUrl) { %}
<button class="btn btn-danger delete" data-type="{%=file.deleteType%}" data-url="{%=file.deleteUrl%}"{% if (file.deleteWithCredentials) { %} data-xhr-fields='{"withCredentials":true}'{% } %}>
<i class="ion-trash-b"></i>
<span>Delete</span>
</button>
<input type="checkbox" name="delete" value="1" class="toggle">
<div class="btn btn-success create" data-name="{%=file.name%}" data-type="{%=file.type%}">
<i class="ion-upload"></i>
Create
</div>
{% } else { %}
<button class="btn btn-warning cancel">
<i class="ion-close-round"></i>
<span>Cancel</span>
</button>
{% } %}
</td>
</tr>
| {% } %}
| {% endraw %}
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.iframe-transport.min.js') }}")
script(src="{{ url_for('static_pillar', filename='javascript-templates/js/tmpl.min.js') }}")
script(src="{{ url_for('static_pillar', filename='javascript-load-image/js/load-image.all.min.js') }}")
script(src="{{ url_for('static_pillar', filename='javascript-canvas-to-blob/js/canvas-to-blob.min.js') }}")
script(src="{{ url_for('static_pillar', filename='gallery/js/jquery.blueimp-gallery.min.js') }}")
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.iframe-transport.min.js') }}")
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.fileupload.min.js') }}")
script(src="{{ url_for('static_pillar', filename='jquery-file-upload/js/jquery.fileupload-process.js') }}")
script(src="{{ url_for('static_pillar', filename='jquery-file-upload/js/jquery.fileupload-image.js') }}")
script(src="{{ url_for('static_pillar', filename='jquery-file-upload/js/jquery.fileupload-audio.js') }}")
script(src="{{ url_for('static_pillar', filename='jquery-file-upload/js/jquery.fileupload-video.js') }}")
script(src="{{ url_for('static_pillar', filename='jquery-file-upload/js/jquery.fileupload-validate.js') }}")
script(src="{{ url_for('static_pillar', filename='jquery-file-upload/js/jquery.fileupload-ui.js') }}")
script(src="{{ url_for('static_pillar', filename='jquery-file-upload/js/main.js') }}")
script().
$('body').unbind('click');
$('body').on('click', '.create', function(event) {
// Start the asset creation process
event.preventDefault();
var parent_id = ProjectUtils.nodeId();
$('#node-add-form').text('Please wait...');
$.post('{{url_for('nodes.assets_create')}}', {
name: $(this).attr('data-name'),
project_id: ProjectUtils.projectId(),
type: $(this).attr('data-type'),
parent_id: parent_id})
.done(function(data) {
if (parent_id) {
// We are in embedded mode and try to call the editNode function
editNode(data.asset_id);
} else {
window.location.replace("/nodes/" + data.asset_id + "/edit");
}
//alert( "Data Loaded: " + data.message );
});
});
// Temporary list of CSS to style the upload form
var cssLinks = ['/static/assets/css/blueimp/blueimp-gallery.min.css',
'/static/jquery-file-upload/css/jquery.fileupload.css',
'/static/jquery-file-upload/css/jquery.fileupload-ui.css']
$.each(cssLinks, function(index, value) {
// Check if the CSS is needed
if (!$("link[href='" + value + "']").length) {
$('<link href="' + value + '" rel="stylesheet">').appendTo("head");
}
});

View File

@@ -0,0 +1,15 @@
| {% macro navigation_tabs(title) %}
section#nav-tabs
ul#nav-tabs__list
li.nav-tabs__list-tab(
class="{% if title == 'homepage' %}active{% endif %}")
a(href="{{ url_for('main.homepage') }}") Activity
li.nav-tabs__list-tab(
class="{% if title == 'dashboard' %}active{% endif %}")
a(href="{{ url_for('projects.index') }}") My Projects
li.nav-tabs__list-tab(
class="{% if title == 'home' %}active{% endif %}")
a(href="{{ url_for('projects.home_project') }}") Home
| {% endmacro %}

View File

@@ -0,0 +1,34 @@
| {% macro render_field(field) %}
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
| {% if field.type == 'BooleanField' %}
.checkbox
label
| {{ field(class='checkbox') }}
| {{ field.label }}
| {% elif field.type == 'FieldList' %}
ul.fieldlist#files
| {% for file in field %}
li.fieldlist-item
| {% for subfield in file %}
| {{ render_field(subfield) }}
| {% endfor %}
| {% endfor %}
| {% else %}
| {{ field.label }}
| {{ field(class='form-control') }}
| {% endif %}
| {% if field.errors %}
ul.error
| {% for error in field.errors %}
li {{ error }}
| {% endfor %}
| {% endif %}
| {% endmacro %}

13
src/templates/_modal.jade Normal file
View File

@@ -0,0 +1,13 @@
// Modal
#modal-default.modal.fade(tabindex='-1', role='dialog', aria-labelledby='modal-default-label')
.modal-dialog(role='document')
.modal-content
.modal-header
button.close(type='button', data-dismiss='modal', aria-label='Close')
span(aria-hidden='true') ×
h4#modal-default-label.modal-title Modal title
.modal-body
| ...
.modal-footer
button.btn.btn-default(type='button', data-dismiss='modal') Close
button.btn.btn-primary(type='button') Save changes

View File

@@ -0,0 +1,4 @@
ul#notifications-list
li.nc-item
#notifications-loader
i.pi-spin.spin

View File

@@ -0,0 +1,4 @@
| {% extends "errors/layout.html" %}
| {% block body %}
| {% include "errors/403_embed.html" %}
| {% endblock %}

View File

@@ -0,0 +1,25 @@
#error_container.403
#error_box
.error-top-container
.error-title Sorry to bother you, but...
.error-lead.
This content is only available to Cloud subscribers.<br/>
Get full access to the Blender Cloud for only $9.90 per month.
.buttons
a.sign-up.btn.btn-outline(href="https://store.blender.org/product/membership/")
i.pi-check
| Subscribe Now
| {% if not current_user.is_authenticated %}
a.sign-in.btn.btn-empty(href="{{ url_for('users.login') }}")
i.pi-log-in
| Log in
| {% endif %}
hr
.error-lead.extra.
If you have just subscribed, please <a href="{{ url_for('users.logout') }}">log out</a> and in again.<br/>
For any other issue get in touch with <a href="mailto:cloudsupport@blender.org">cloudsupport@blender.org</a>

View File

@@ -0,0 +1,17 @@
| {% extends "errors/layout.html" %}
| {% block body %}
#error_container.404.standalone
#error_box
.error-top-container
.error-title Not found.
.error-lead
p.
The error has been logged and we're working on getting it fixed.
hr
p.
Looking for the Open Movies? Check out <a href="https://www.youtube.com/BlenderFoundation">Blender Foundation's YouTube</a> channel. <br/> Were you looking for tutorials instead? <a href="http://www.blender.org/support/tutorials/">blender.org</a> has a good selection.
.error-lead.extra.
We'll be back soon, in the meantime follow <a href="https://twitter.com/Blender_Cloud">@Blender_Cloud</a> for updates.
| {% endblock %}

View File

@@ -0,0 +1,12 @@
#error_container.404
#error_box
.error-title 404. Not Found.
.error-lead.
Whatever you're looking for, it's not here.
hr
.error-lead.extra.
Is this content missing? Let us know on <a href="https://twitter.com/Blender_Cloud">Twitter</a>
or email <a href="mailto:cloudsupport@blender.org">cloudsupport@blender.org</a>

View File

@@ -0,0 +1,42 @@
doctype
html(lang="en")
head
meta(charset="utf-8")
title Blender Cloud
meta(name="viewport", content="width=device-width, initial-scale=1.0")
meta(name="description", content="Blender Cloud is a web based service developed by Blender Institute that allows people to access the training videos and all the data from the past open projects.")
meta(name="author", content="Blender Institute")
meta(name="theme-color", content="#3e92aa")
script.
!function(e){"use strict";e.loadCSS=function(t,n,o){var r,i=e.document,l=i.createElement("link");if(n)r=n;else{var d=(i.body||i.getElementsByTagName("head")[0]).childNodes;r=d[d.length-1]}var a=i.styleSheets;l.rel="stylesheet",l.href=t,l.media="only x",r.parentNode.insertBefore(l,n?r:r.nextSibling);var f=function(e){for(var t=l.href,n=a.length;n--;)if(a[n].href===t)return e();setTimeout(function(){f(e)})};return l.onloadcssdefined=f,f(function(){l.media=o||"all"}),l},"undefined"!=typeof module&&(module.exports=e.loadCSS)}(this);
loadCSS( "//fonts.googleapis.com/css?family=Roboto:300,400,500" );
loadCSS( "//fonts.googleapis.com/css?family=Lato:300,400" );
link(href="{{ url_for('static_pillar', filename='assets/ico/favicon.png') }}", rel="shortcut icon")
link(href="{{ url_for('static_pillar', filename='assets/ico/apple-touch-icon-precomposed.png') }}", rel="icon apple-touch-icon-precomposed", sizes="192x192")
link(href="{{ url_for('static_pillar', filename='assets/css/main.css') }}", rel="stylesheet")
body.error
#error_container.500.standalone
#error_box
.error-top-container
.error-title Something went wrong.
.error-lead
p.
The error has been logged and we're working on getting it fixed.
hr
p.
Looking for the Open Movies? Check out <a href="https://www.youtube.com/BlenderFoundation">Blender Foundation's YouTube</a> channel. <br/> Were you looking for tutorials instead? <a href="http://www.blender.org/support/tutorials/">blender.org</a> has a good selection.
.error-lead.extra.
We'll be back soon, in the meantime follow <a href="https://twitter.com/Blender_Cloud">@Blender_Cloud</a> for updates.
noscript
link(href='//fonts.googleapis.com/css?family=Roboto:300,400,500', rel='stylesheet', type='text/css')
link(href='//fonts.googleapis.com/css?family=Lato:300,400', rel='stylesheet', type='text/css')

View File

@@ -0,0 +1,27 @@
doctype
html(lang="en")
head
meta(charset="utf-8")
title Blender Cloud
meta(name="viewport", content="width=device-width, initial-scale=1.0")
meta(name="description", content="Blender Cloud is a web based service developed by Blender Institute that allows people to access the training videos and all the data from the past open projects.")
meta(name="author", content="Blender Institute")
meta(name="theme-color", content="#3e92aa")
script.
!function(e){"use strict";e.loadCSS=function(t,n,o){var r,i=e.document,l=i.createElement("link");if(n)r=n;else{var d=(i.body||i.getElementsByTagName("head")[0]).childNodes;r=d[d.length-1]}var a=i.styleSheets;l.rel="stylesheet",l.href=t,l.media="only x",r.parentNode.insertBefore(l,n?r:r.nextSibling);var f=function(e){for(var t=l.href,n=a.length;n--;)if(a[n].href===t)return e();setTimeout(function(){f(e)})};return l.onloadcssdefined=f,f(function(){l.media=o||"all"}),l},"undefined"!=typeof module&&(module.exports=e.loadCSS)}(this);
loadCSS( "//fonts.googleapis.com/css?family=Roboto:300,400,500" );
loadCSS( "//fonts.googleapis.com/css?family=Lato:300,400" );
link(href="{{ url_for('static_pillar', filename='assets/ico/favicon.png') }}", rel="shortcut icon")
link(href="{{ url_for('static_pillar', filename='assets/ico/apple-touch-icon-precomposed.png') }}", rel="icon apple-touch-icon-precomposed", sizes="192x192")
link(href="{{ url_for('static_pillar', filename='assets/css/main.css') }}", rel="stylesheet")
body.error
| {% block body %}{% endblock %}
noscript
link(href='//fonts.googleapis.com/css?family=Roboto:300,400,500', rel='stylesheet', type='text/css')
link(href='//fonts.googleapis.com/css?family=Lato:300,400', rel='stylesheet', type='text/css')

378
src/templates/homepage.jade Normal file
View File

@@ -0,0 +1,378 @@
| {% extends 'layout.html' %}
| {% from '_macros/_navigation.html' import navigation_tabs %}
| {% set title = 'homepage' %}
| {% block og %}
meta(property="og:title", content="Blender Cloud")
meta(property="og:url", content="https://cloud.blender.org/")
meta(property="og:image", content="{% if main_project.picture_header %}{{ main_project.picture_header.thumbnail('l', api=api) }}{% else %}{{ url_for('static', filename='assets/img/backgrounds/background_caminandes_3_02.jpg')}}{% endif %}")
| {% endblock %}
| {% block tw %}
meta(name="twitter:title", content="Blender Cloud")
meta(name="twitter:description", content="Blender Cloud is a web based service developed by Blender Institute that allows people to access the training videos and all the data from the open projects.")
meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_project.picture_header.thumbnail('l', api=api) }}{% else %}{{ url_for('static', filename='assets/img/backgrounds/background_caminandes_3_02.jpg')}}{% endif %}")
| {% endblock %}
| {% block body %}
.dashboard-container
section#main
| {{ navigation_tabs(title) }}
section#stream
h3#activity-stream__title
| Activity Stream
ul#activity-stream__filters
li Filter
li.filter.active(
data-filter='image',
title="List images")
i.pi-picture
li.filter.active(
data-filter='video',
title="List videos")
i.pi-film-thick
li.filter.active(
data-filter='file',
title="List files")
i.pi-file-archive
li.filter.active(
data-filter='post',
title="List blog posts")
i.pi-newspaper
li.filter(
data-filter='comment',
title="List comments")
i.pi-comment
ul#activity-stream__list
| {% for n in activity_stream %}
| {% if n.node_type == 'comment' %}
li.activity-stream__list-item.hidden(
class="{{ n.node_type }}",
data-url="{{ url_for_node(node=n) }}")
a.activity-stream__list-thumbnail(href="{{ url_for_node(node=n) }}")
i.pi-comment
.activity-stream__list-details
a.title(href="{{ url_for_node(node=n) }}") {{ n.properties.content }}
ul.meta
li.who {{ n.user.full_name }}
li.where-project
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
li.where-parent
a(href="{{ url_for_node(node_id=n.attached_to._id) }}") {{ n.attached_to.name }}
li.when
a(href="{{ url_for_node(node=n) }}", title="{{ n._created }}") {{ n._created | pretty_date_time }}
| {% elif n.node_type == 'asset' %}
li.activity-stream__list-item(
class="{{ n.node_type }} {{ n.properties.content_type }}",
data-url="{{ url_for_node(node=n) }}")
a.activity-stream__list-thumbnail(
class="{{ n.properties.content_type }}",
href="{{ url_for_node(node=n) }}")
| {% if n.properties.content_type == 'video' %}
i.pi-film-thick
| {% elif n.properties.content_type == 'image' %}
i.pi-picture
| {% elif n.properties.content_type == 'file' %}
i.pi-file-archive
| {% else %}
i.pi-folder
| {% endif %}
.activity-stream__list-details
| {% if n.picture %}
a.image(href="{{ url_for_node(node=n) }}")
| {% if n.properties.content_type == 'video' %}
i.pi-play
| {% endif %}
img(src="{{ n.picture.thumbnail('l', api=api) }}")
| {% endif %}
a.date(href="{{ url_for_node(node=n) }}", title="{{ n._created }}") {{ n._created | pretty_date_time }}
a.title(href="{{ url_for_node(node=n) }}")
| {{ n.name }}
| {% if n.permissions.world %}
.ribbon
span free
| {% endif %}
ul.meta
li.what {{ n.properties.content_type }}
li.who {{ n.user.full_name }}
li.where-project
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
| {% elif n.node_type == 'post' %}
li.activity-stream__list-item(
class="{{ n.node_type }}",
data-url="{{ url_for_node(node=n) }}")
a.activity-stream__list-thumbnail(href="{{ url_for_node(node=n) }}")
i.pi-newspaper
.activity-stream__list-details
| {% if n.picture %}
a.image(href="{{ url_for_node(node=n) }}")
img(src="{{ n.picture.thumbnail('l', api=api) }}")
| {% endif %}
a.date(href="{{ url_for_node(node=n) }}", title="{{ n._created }}") {{ n._created | pretty_date_time }}
a.title(href="{{ url_for_node(node=n) }}") {{ n.name }}
ul.meta
li.what Blog Post
li.where-project
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}")
| {{ n.project.name }}
| {% endif %}
| {% endfor %}
li.activity-stream__list-item.empty#activity-stream__empty
| No items to list.
section#side
section#announcement
| {% if main_project.picture_header %}
a(href="https://cloud.blender.org/blog/introducing-blender-sync")
img.header(
src="{{ main_project.picture_header.thumbnail('l', api=api) }}")
| {% endif %}
.text
.title
a(href="https://cloud.blender.org/blog/introducing-blender-sync") Blender Sync
.lead
span.
Save your settings once. Use them anywhere.
Carry your Blender configuration with you, use our free add-on to sync your keymaps and preferences.
<hr/>
Syncing is free for everyone. No subscription required.
.buttons
a.btn.btn-default.btn-outline.orange(
href="https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip")
i.pi-download
| Download <small>v</small> {{ config.BLENDER_CLOUD_ADDON_VERSION }}
a.btn.btn-default.btn-outline.blue(
href="https://cloud.blender.org/blog/introducing-blender-sync")
| Learn More
section#random-asset
h3
a(href="/search") Explore the Cloud
span.section-lead Random selection of the best assets &amp; tutorials
ul.random-asset__list
| {% for n in random_featured %}
| {% if n.picture and loop.first %}
li.random-asset__list-item.featured
| {% if n.permissions.world %}
.ribbon
span free
| {% endif %}
a.random-asset__thumbnail(
href="{{ url_for_node(node=n) }}",
class="{{ n.properties.content_type }}")
| {% if n.picture %}
img(src="{{ n.picture.thumbnail('l', api=api) }}")
| {% if n.properties.content_type == 'video' %}
i.pi-play
| {% endif %}
| {% endif %}
a.title(href="{{ url_for_node(node=n) }}")
| {{ n.name }}
ul.meta
li.what
a(href="{{ url_for_node(node=n) }}")
| {% if n.properties.content_type %}{{ n.properties.content_type }}{% else %}Folder{% endif %}
li.where
a(href="{{ url_for('projects.view', project_url=n.project.url) }}")
| {{ n.project.name }}
| {% else %}
li.random-asset__list-item
| {% if n.permissions.world %}
.ribbon
span free
| {% endif %}
a.random-asset__list-thumbnail(
href="{{ url_for_node(node=n) }}",
class="{{ n.properties.content_type }}")
| {% if n.picture %}
img.image(src="{{ n.picture.thumbnail('s', api=api) }}")
| {% else %}
| {% if n.properties.content_type == 'video' %}
i.pi-film-thick
| {% elif n.properties.content_type == 'image' %}
i.pi-picture
| {% elif n.properties.content_type == 'file' %}
i.pi-file-archive
| {% else %}
i.pi-folder
| {% endif %}
| {% endif %}
.random-asset__list-details
a.title(href="{{ url_for_node(node=n) }}") {{ n.name }}
ul.meta
li.what
a(href="{{ url_for_node(node=n) }}")
| {% if n.properties.content_type %}{{ n.properties.content_type }}{% else %}Folder{% endif %}
li.where
a(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
| {% endif %}
| {% endfor %}
section#blog-stream
a.feed(
href="{{ url_for('main.feeds_blogs') }}",
title="Blender Cloud & Projects Blog Feed",
data-toggle="tooltip",
data-placement="left")
i.pi-rss
h3
a(href="{{ url_for('main.main_blog') }}") Blog
ul#blog-stream__list
| {% if latest_posts %}
| {% for n in latest_posts %}
| {% if n.picture and loop.first %}
li.blog-stream__list-item.featured
a.blog-stream__thumbnail(
href="{{ url_for_node(node=n) }}")
img(src="{{ n.picture.thumbnail('l', api=api) }}")
a.title(href="{{ url_for_node(node=n) }}")
| {{ n.name }}
ul.meta
li.when
a(href="{{ url_for_node(node=n) }}",
title="Updated {{ n._updated | pretty_date }}")
| {{ n._created | pretty_date }}
li.where-project
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
| {% else %}
li.blog-stream__list-item
a.blog-stream__list-thumbnail(href="{{ url_for_node(node=n) }}")
| {% if n.picture %}
img.image(src="{{ n.picture.thumbnail('s', api=api) }}")
| {% else %}
i.pi-newspaper
| {% endif %}
.blog-stream__list-details
a.title(href="{{ url_for_node(node=n) }}") {{ n.name }}
ul.meta
li.when
a(href="{{ url_for_node(node=n) }}",
title="Updated {{ n._updated | pretty_date }}")
| {{ n._created | pretty_date }}
li.where-project
a.project(href="{{ url_for('projects.view', project_url=n.project.url) }}") {{ n.project.name }}
| {% endif %}
| {% endfor %}
| {% else %}
li.blog-stream__list-item
.blog-stream__list-details
ul.meta
li.when No updates yet
| {% endif %}
li.blog-stream__list-item.more
a(href="{{ url_for('main.main_blog') }}") See All Blog Posts
| {% endblock %}
| {% block footer_scripts %}
script.
$(function () {
/* cleanup mentions in comments */
$('.activity-stream__list-details a.title').each(function(){
$(this).text($(this).text().replace(/\*|\@|\<(.*?)\>/g, ''));
});
function saveFilters(){
var filtersEnabled = [];
$('ul#activity-stream__filters li.filter.active').each(function(){
filtersEnabled.push($(this).attr('data-filter'));
});
setJSONCookie('bcloud_ui', 'homepage_activity_filters', filtersEnabled);
}
function loadFilters(){
var filters = Cookies.getJSON('bcloud_ui');
if (filters) {
if (filters.homepage_activity_filters && filters.homepage_activity_filters.length){
/* Clear style on filters/items */
$('ul#activity-stream__filters li.filter').removeClass('active');
$('ul#activity-stream__list li.activity-stream__list-item').addClass('hidden');
for (var f in filters.homepage_activity_filters){
var savedFilter = filters.homepage_activity_filters[f];
/* Style each filter type */
$('ul#activity-stream__filters li.filter').each(function(){
if ($(this).attr('data-filter') == savedFilter){
$(this).addClass('active');
}
});
/* Show items that are on the cookie */
$('ul#activity-stream__list li.activity-stream__list-item').each(function(){
if ($(this).hasClass(savedFilter)) {
$(this).removeClass('hidden');
}
});
}
}
}
}
/* Toggle filters */
$('ul#activity-stream__filters li.filter').click(function(){
// Style the filter button
$(this).toggleClass('active');
var filterType = $(this).attr('data-filter');
saveFilters();
// Toggle hidden class on list item if it has class matching the filter
$('ul#activity-stream__list li.activity-stream__list-item').each(function(){
if ($(this).hasClass(filterType)) {
$(this).toggleClass('hidden');
}
});
var hiddenItems = $('ul#activity-stream__list li.activity-stream__list-item.hidden').length;
if (hiddenItems == '{{ activity_stream|length }}'){
$('#activity-stream__empty').show();
}
});
loadFilters();
/* Click on the whole asset/comment row to go */
$('li.activity-stream__list-item.asset, li.activity-stream__list-item.comment').click(function(e){
window.location.href = $(this).data('url');
$(this).addClass('active');
$(this).find('.activity-stream__list-thumbnail i')
.removeAttr('class')
.addClass('pi-spin spin');
});
hopToTop(); // Display jump to top button
});
| {% endblock %}

537
src/templates/join.jade Normal file
View File

@@ -0,0 +1,537 @@
| {% extends 'layout.html' %}
| {% block page_title %}Welcome{% endblock %}
| {% set title = 'join' %}
| {% block og %}
meta(property="og:title", content="Join the Blender Cloud")
meta(property="og:url", content="https://cloud.blender.org/")
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_services.jpg')}}")
| {% endblock %}
| {% block header_backdrop %}
.navbar-backdrop.join(
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/background_andy_hdribot_01.jpg')}})")
| {% endblock %}
| {% block page_overlay %}
#page-overlay.video
.video-embed
| {% endblock %}
| {% block body %}
#page-container.join
#page-header
.page-title-icons
i.pi-blender
i.pi-heart-filled
i.pi-blender-cloud
.page-title
| Your Own Production Platform
//- .page-title-summary
//- | Get inspiration, knowledge, and tools all in one place.
.page-header-cta-container
a.page-header-cta(href="https://store.blender.org/product/membership/")
| Join Now
a.page-header-cta-extra(href="{{ url_for('main.nodes_search_index') }}")
| Explore
i.pi-angle-right
#page-content
.page-triplet-container.homepage
.row
.col-md-4
.triplet-card(data-url="{{ url_for('main.open_projects') }}")
.triplet-card-thumbnail
img(
alt="Open Projects",
src="{{ url_for('static', filename='assets/img/features/open_movies_02.jpg')}}")
.triplet-card-info
h3 Open Projects
p.
The iconic Blender Institute Open Movies,
featuring all the production files, assets, artwork, and never-seen-before content.
span.triplet-cta
| LEARN MORE
.col-md-4
.triplet-card(data-url="{{ url_for('main.training') }}")
.triplet-card-thumbnail
img(
alt="Training and Tutorials",
src="{{ url_for('static', filename='assets/img/features/training_02.jpg')}}")
.triplet-card-info
h3 Training &amp; Tutorials
p.
Character modeling, 3D printing, VFX, rigging and more. We offer
12 complete training series with +100 hours of training.
span.triplet-cta
| LEARN MORE
.col-md-4
.triplet-card(data-url="{{ url_for('main.services') }}")
.triplet-card-thumbnail
img(
alt="Services and Tools",
src="{{ url_for('static', filename='assets/img/features/services_01.jpg')}}")
.triplet-card-info
h3 Services
p.
Crate your personal projects, collaborate with other members, store
and sync your Blender settings across multiple workstations.
span.triplet-cta
| LEARN MORE
section.page-card-header
h2 Download 1000s of files and assets
.page-triplet-container.homepage
.row
.col-md-4
.triplet-card(data-url="{{ url_for('main.redir_hdri') }}")
.triplet-card-thumbnail
img(
alt="HDRI",
src="{{ url_for('static', filename='assets/img/features/hdri_01.jpg')}}")
.triplet-card-info
h3 HDRI
p.
Up to 8K and 18 EVs (extremely high) HDR images to light your renders.
span.triplet-cta
| LEARN MORE
.col-md-4
.triplet-card(data-url="{{ url_for('main.redir_textures') }}")
.triplet-card-thumbnail
img(
alt="Textures",
src="{{ url_for('static', filename='assets/img/features/textures_01.jpg')}}")
.triplet-card-info
h3 Textures
p.
More than 1500 texture maps.
Browse online or from Blender with our awesome add-on.
span.triplet-cta
| LEARN MORE
.col-md-4
.triplet-card(data-url="{{ url_for('main.redir_characters') }}")
.triplet-card-thumbnail
img(
alt="Characters",
src="{{ url_for('static', filename='assets/img/features/characters_01.jpg')}}")
.triplet-card-info
h3 Characters
p.
Production quality, fully rigged and shaded characters ready to animate.
span.triplet-cta
| LEARN MORE
section.page-card-header
h2 Learn by Example
section.page-card.services-projects
.page-card-side
h2.page-card-title Exclusive Production Insights
.page-card-summary
p.
Watch the original authors of shots breaking it down into a detailed
explanation and share their insight in the production process. Watch
animation reviews, narrated timelapses, shot walk-throughs.
.page-card-side
.page-card-image
img(
alt="Exclusive Production Insights",
src="{{ url_for('static', filename='assets/img/features/animation_review_01.gif')}}")
section.page-card.right.services-projects
.page-card-side
h2.page-card-title Production Quality Files
.page-card-summary
p.
From fully rigged characters ready to animate to an Art Gallery
curated by the best Blender artists, access top quality blendfiles to learn
new techniques and improve your art.
.page-card-side
.page-card-image
img(
alt="High Quality Assets",
src="{{ url_for('static', filename='assets/img/features/locomotive_01.jpg')}}")
#blender-addon.page-section-container(
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/pattern_bw_01.jpg')}})")
section.page-card-header.dark Blender Cloud Add-on
span.page-card-header_lead.dark Connect Blender with the Cloud
a.page-card-cta.download(
href="https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip")
i.pi-download
| Download <small>v</small>{{ config.BLENDER_CLOUD_ADDON_VERSION }}
section.page-card.dark.right
.page-card-side
h2.page-card-title Blender Sync
.page-card-summary.
Save your settings once. Use them anywhere.
Carry your Blender configuration with you,
use our free add-on to sync your keymaps and preferences.
<hr/>
<small>Syncing settings is free for everyone! No subscription required.</small>
a.page-card-cta(
href="https://cloud.blender.org/blog/introducing-blender-sync")
| Learn More
.page-card-side
.page-card-icon
svg(xmlns="http://www.w3.org/2000/svg",
width="64", height="54", viewBox="0 0 64 54")
g(fill="none", stroke="#aaa", stroke-width="2", stroke-miterlimit="10")
path(d="M29 47H5l-4-4v-2h24l2 2h2M29 3H10C8.344 3 7 4.343 7 6v32M35 51h24l4-4v-2H39l-2 2h-2M35 7h19c1.656 0 3 1.343 3 3v32M32 34v20M32 20v8M32 0v14")
g
path(d="M32 31c-3.866 0-7-3.134-7-7M32 17c3.866 0 7 3.134 7 7M32 31h8M24 17h8M36 35l4-4-4-4M28 21l-4-4 4-4")
path(d="M29 37H11V7h18M35 11h18v30H35")
section.page-card.dark
.page-card-side
h2.page-card-title Texture Browser
.page-card-summary
p.
Access the <a href="https://cloud.blender.org/p/textures/">Blender Cloud Textures and HDRI</a>
libraries from within Blender.
Create, manage and share <em>your own</em> texture libraries!
a.page-card-cta.watch-video(
href="https://www.youtube.com/watch?v=-srXYv2Osjw",
data-youtube-id="-srXYv2Osjw")
i.pi-play
| Watch Video
.page-card-side
.page-card-icon
svg(xmlns="http://www.w3.org/2000/svg",
width="64", height="60",
viewBox="0 0 64 60")
g(fill="#aaa")
path(d="M32 60c-.188 0-.377-.053-.542-.16l-31-20C.173 39.656 0 39.34 0 39s.173-.656.458-.84l31-20c.33-.213.754-.213 1.084 0l31 20c.285.184.458.5.458.84s-.173.656-.458.84l-31 20c-.165.107-.354.16-.542.16zM2.845 39L32 57.81 61.155 39 32 20.19 2.845 39z")
path(d="M32 51c-.188 0-.377-.053-.542-.16l-31-20C.173 30.656 0 30.34 0 30s.173-.656.458-.84l31-20c.33-.213.754-.213 1.084 0l31 20c.285.184.458.5.458.84s-.173.656-.458.84l-31 20c-.165.107-.354.16-.542.16zM2.845 30L32 48.81 61.155 30 32 11.19 2.845 30z")
path(d="M32 42c-.188 0-.377-.053-.542-.16l-31-20C.173 21.656 0 21.34 0 21s.173-.656.458-.84l31-20c.33-.213.754-.213 1.084 0l31 20c.285.184.458.5.458.84s-.173.656-.458.84l-31 20c-.165.107-.354.16-.542.16zM2.845 21L32 39.81 61.155 21 32 2.19 2.845 21z")
path(d="M31 27h2v2h-2zm0-4h2v2h-2zm0 8h2v2h-2zm4-4h2v2h-2zm4 2h2v2h-2zm-16 0h2v2h-2zm12 2h2v2h-2zm-8-4h2v2h-2zm0 4h2v2h-2zm4 4h2v2h-2zm31 15h2v2h-2zm0-4h2v2h-2zm0 8h2v2h-2zm0 4h2v2h-2zm0-54h2v2h-2zm0-4h2v2h-2zm0 8h2v2h-2zm0 4h2v2h-2zM0 50h2v2H0zm0-4h2v2H0zm0 8h2v2H0zm0 4h2v2H0zM0 4h2v2H0zm0-4h2v2H0zm4 0h2v2H4zm4 0h2v2H8zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm22 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zM4 58h2v2H4zm4 0h2v2H8zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm22 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zM0 8h2v2H0zm0 4h2v2H0z")
section.page-card.dark.right
.page-card-side
h2.page-card-title
| Image Sharing
.page-card-summary
p.
Got a nice render, a Blender oddity, a cool screenshot?
Share it instantly from within Blender to the Cloud, to the world!
a.page-card-cta(
href="https://cloud.blender.org/blog/introducing-image-sharing")
| Learn More
.page-card-side
.page-card-icon
svg(xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64")
g(fill="none",
stroke="#aaa",
stroke-width="2",
stroke-linejoin="round",
stroke-miterlimit="10")
path(d="M1 1h62v62H1zM4 59h2M8 59h2M12 59h2M60 49H48M46 49H27M60 53H40")
path(d="M5 5h54v40H5z")
path(d="M9 45v-3c0-1.656 1.344-3 3-3h6c1.656 0 3 1.344 3 3v3M29 45v-3c0-1.656 1.344-3 3-3h6c1.656 0 3 1.344 3 3v3M13 45v-3M17 45v-3M33 45v-3M37 45v-3M22 31h-5c-2.762 0-5 2.238-5 5v3M38 39v-3c0-2.762-2.238-5-5-5h-5M31 20c0 3.313-1 9-6 9s-6-5.687-6-9c0-1 0-5 6-5s6 4 6 5z")
path(d="M29 27l-2 8h-4l-2-8M18 31c-4-3-5-9-5-9l6-3M32 31c4-3 5-9 5-9l-6-3M59 24L44 9l-8 8M44 9l8 36")
circle(cx="12", cy="12", r="3")
section.page-card
.page-card-side
h2.page-card-title Private Projects
.page-card-summary.
Create and manage your own personal projects.
Upload assets and collaborate with other Blender Cloud members.
a.page-card-cta(
href="https://cloud.blender.org/blog/introducing-private-projects")
| Learn More
.page-card-side
.page-card-icon
svg(xmlns='http://www.w3.org/2000/svg', width='56', height='64', viewbox='0 0 56 64')
g(fill='#555')
path(d='M42 38H14V26h28v12zm-26-2h24v-8H16v8zm-4-5H8c-1.654 0-3-1.346-3-3V15h2v13c0 .55.45 1 1 1h4v2z')
path(d='M9.293 19.707L6 16.414l-3.293 3.293-1.414-1.414 4-4c.39-.39 1.023-.39 1.414 0l4 4-1.414 1.414zM48 31h-4v-2h4c.55 0 1-.45 1-1V15h2v13c0 1.654-1.346 3-3 3z')
path(d='M53.293 19.707L50 16.414l-3.293 3.293-1.414-1.414L50 13.586l4.707 4.707M27 15h2v9h-2z')
path(d='M31.293 19.707L28 16.414l-3.293 3.293-1.414-1.414L28 13.586l4.707 4.707M7 49H5V36c0-1.654 1.346-3 3-3h4v2H8c-.55 0-1 .45-1 1v13z')
path(d='M6 50c-.256 0-.512-.098-.707-.293l-4-4 1.414-1.414L6 47.586l3.293-3.293 1.414 1.414-4 4c-.195.195-.45.293-.707.293zm45-1h-2V36c0-.55-.45-1-1-1h-4v-2h4c1.654 0 3 1.346 3 3v13z')
path(d='M50 50.414l-4.707-4.707 1.414-1.414L50 47.586l3.293-3.293 1.414 1.414M27 40h2v9h-2z')
path(d='M28 50.414l-4.707-4.707 1.414-1.414L28 47.586l3.293-3.293 1.414 1.414M6 12c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zM6 2C3.794 2 2 3.794 2 6s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zm22 10c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zm22 10c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zM6 64c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zm22 10c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zm22 10c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zM27 31h2v2h-2zm-4 0h2v2h-2zm8 0h2v2h-2z')
section.pricing
.container
.row
.col-md-12
h2 Simple Pricing. Any payment method.
.row
.col-md-4.col-sm-4
.box.monthly
h3 Monthly
.pricing-display
span.currency-sign $
span.digit-int 9
span.digit-dec .90 / month*
.pricing-caption
p * with a 3-months minimum period
a.sign-up-now(href="https://store.blender.org/product/membership/")
| Subscribe Now
.col-md-4.col-sm-4
.box.yearly
h3 Yearly
.pricing-display
span.currency-sign $
span.digit-int 109
span.digit-dec .00 / year
.pricing-caption
p 1 month free!
p Free copy of the <a href="https://store.blender.org/product/art-of-blender-2/">Art of Blender</a>
a.sign-up-now(href="https://store.blender.org/product/membership/")
| Subscribe Now
.col-md-4.col-sm-4
.box.education
h3 Education
.pricing-caption
p.
We also provide flexible options for group subscription
ideal for schools or teams.
p.
Get in touch to discuss direct support, custom solutions,
team management tools and Single Sign-on.
a.sign-up-now(href="mailto:cloudsupport@blender.org")
i.pi-email
| Get in Touch
section.team(
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/pattern_01.jpg')}})")
.container
.row
.col-md-12
h2.
A restless team of artists and developers <br/>
wants to share their work with you.
.people-container
.people-intro
h3 Blender Institute
span Amsterdam, The Netherlands
.people-faces
.face(data-blenderhead='ton')
img(alt="Ton", src="{{ url_for('static', filename='assets/img/people/ton.jpg')}}")
.face(data-blenderhead='francesco')
img(alt="Francesco", src="{{ url_for('static', filename='assets/img/people/francesco.jpg')}}")
.face(data-blenderhead='pablo')
img(alt="Pablo", src="{{ url_for('static', filename='assets/img/people/pablo.jpg')}}")
.face(data-blenderhead='andy')
img(alt="Andy", src="{{ url_for('static', filename='assets/img/people/andy.jpg')}}")
.face(data-blenderhead='hjalti')
img(alt="Hjalti", src="{{ url_for('static', filename='assets/img/people/hjalti.jpg')}}")
.face(data-blenderhead='sergey')
img(alt="Sergey", src="{{ url_for('static', filename='assets/img/people/sergey.jpg')}}")
.face(data-blenderhead='sybren')
img(alt="Sybren", src="{{ url_for('static', filename='assets/img/people/sybren.jpg')}}")
.people-bio
.bio#ton
h3 Ton Roosendaal
small CEO Blender Foundation. Producer Blender Institute
span The Netherlands
.bio#francesco
h3 Francesco Siddi
small Pipeline Tools & Back-end Web Development
span Italy
.bio#pablo
h3 Pablo Vázquez
small Lighting, Rendering. Front-end Web Development
span Argentina
.bio#andy
h3 Andy Goralczyk
small Shading, Lighting, Rendering, FX
span Germany
.bio#hjalti
h3 Hjalti Hjálmarsson
small Animation. Layout Artist.
span Iceland
.bio#sergey
h3 Sergey Sharybin
small Blender & Cycles Core Developer
span Russia
.bio#sybren
h3 Sybren Stüvel
small Blender Cloud Developer
span The Netherlands
.people-container.online
.people-intro
h3 Online Collaborators
span Contributing to Blender Cloud from all over the globe.
.people-faces
.face(data-blenderhead='gleb')
img(alt="Gleb", src="{{ url_for('static', filename='assets/img/people/gleb.jpg')}}")
.face(data-blenderhead='david')
img(alt="David", src="{{ url_for('static', filename='assets/img/people/david.jpg')}}")
.face(data-blenderhead='sebastian')
img(alt="Sebastian", src="{{ url_for('static', filename='assets/img/people/sebastian.jpg')}}")
.face(data-blenderhead='jpbouza')
img(alt="Juan Pablo", src="{{ url_for('static', filename='assets/img/people/jpbouza.jpg')}}")
.face(data-blenderhead='bassam')
img(alt="Bassam", src="{{ url_for('static', filename='assets/img/people/bassam.jpg')}}")
.people-bio
.bio#gleb
h3 Gleb Alexandrov
small Lighting & Shading
span Belarus
.bio#david
h3 David Revoy
small Illustrator & Concept Artist
span France
.bio#jpbouza
h3 Juan Pablo Bouza
small Rigging
span Argentina
.bio#bassam
h3 Bassam Kurdali
small Rigging & Pipeline
span United States
.bio#sebastian
h3 Sebastian König
small VFX
span Germany
section.page-card.oneofus.
Join <strong>2021</strong> awesome people, <a href="">subscribe to Blender Cloud</a> now.
section.supported-by
h2 Our projects were supported by
img.logos(alt="Supported by", src="{{ url_for('static', filename='assets/img/support_logos.png') }}")
section.page-card.subscribe(
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/background_services.jpg')}});")
.page-card-side
h2.page-card-title
| Get inspiration, knowledge, and tools in one place.
.page-card-summary
| Join us for only $9.90/month!
a.page-card-cta(
href="https://store.blender.org/product/membership/")
| Subscribe Now
| {% endblock %}
| {% block footer_scripts %}
script.
$('.triplet-card').click(function(){
window.location.replace($(this).attr('data-url'));
});
$(window).on('load scroll', function() {
var y = $(this).scrollTop();
if (y < 100){
$('.navbar-backdrop').css('background-position-y', (parseInt(-y / 4) * -1) + 'px');
}
});
function getSubscribers(){
$.get('https://store.blender.org/product-counter/?prod=cloud', function(data) {
}).done(function(data){
if (data.total_sold > 0) {
$('.page-card.oneofus').addClass('active');
$('.page-card.oneofus strong').html(data.total_sold);
}
});
}
getSubscribers();
$('.people-faces .face').hover(
function(){
var who = $(this).data('blenderhead');
$('#' + who).addClass('active');
$(this).parent().prev().addClass('active');
},
function(){
$('.bio, .people-intro').removeClass('active');
}
);
// Click anywhere in the page to hide the overlay
function hideOverlay() {
$('#page-overlay.video').removeClass('active');
$('#page-overlay.video .video-embed').html('');
}
$(document).click(function() {
hideOverlay();
});
$(document).keyup(function(e) {
if (e.keyCode == 27) {
hideOverlay();
}
});
$('a.watch-video').click(function(e){
e.preventDefault();
e.stopPropagation();
$('#page-overlay.video').addClass('active');
var videoId = $(this).attr('data-youtube-id');
$('#page-overlay .video-embed').html('<iframe src="https://www.youtube.com/embed/' + videoId +'?rel=0&amp;showinfo=0;autoplay=1" frameborder="0" allowfullscreen></iframe>')
});
| {% endblock %}

436
src/templates/layout.jade Normal file
View File

@@ -0,0 +1,436 @@
doctype
html(lang="en")
head
meta(charset="utf-8")
title {% if self.page_title() %}{% block page_title %}{% endblock %} — {% endif %}Blender Cloud
meta(name="viewport", content="width=device-width, initial-scale=1.0")
meta(name="description", content="Blender Cloud is a web based service developed by Blender Institute that allows people to access the training videos and all the data from the open projects.")
meta(name="author", content="Blender Institute")
meta(name="theme-color", content="#3e92aa")
meta(property="og:site_name", content="Blender Cloud")
| {% block og %}
meta(property="og:title", content="Blender Cloud")
meta(property="og:url", content="https://cloud.blender.org")
meta(property="og:type", content="website")
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_gleb_locomotive.jpg')}}")
| {% endblock %}
meta(name="twitter:card", content="summary_large_image")
meta(name="twitter:site", content="@Blender_Cloud")
| {% block tw %}
meta(name="twitter:title", content="Blender Cloud")
meta(name="twitter:description", content="Blender Cloud is a web based service developed by Blender Institute that allows people to access the training videos and all the data from the open projects.")
meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_gleb_locomotive.jpg')}}")
| {% endblock %}
script(src="//code.jquery.com/jquery-2.2.1.min.js")
script(src="//cdn.jsdelivr.net/typeahead.js/0.11.1/typeahead.jquery.min.js")
script(src="//cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js")
script(src="//cdnjs.cloudflare.com/ajax/libs/js-cookie/2.0.3/js.cookie.min.js",)
script.
var algolia = algoliasearch("{{config['ALGOLIA_USER']}}", "{{config['ALGOLIA_PUBLIC_KEY']}}");
var index = algolia.initIndex("{{config['ALGOLIA_INDEX_NODES']}}");
!function(e){"use strict";e.loadCSS=function(t,n,o){var r,i=e.document,l=i.createElement("link");if(n)r=n;else{var d=(i.body||i.getElementsByTagName("head")[0]).childNodes;r=d[d.length-1]}var a=i.styleSheets;l.rel="stylesheet",l.href=t,l.media="only x",r.parentNode.insertBefore(l,n?r:r.nextSibling);var f=function(e){for(var t=l.href,n=a.length;n--;)if(a[n].href===t)return e();setTimeout(function(){f(e)})};return l.onloadcssdefined=f,f(function(){l.media=o||"all"}),l},"undefined"!=typeof module&&(module.exports=e.loadCSS)}(this);
loadCSS( "//fonts.googleapis.com/css?family=Roboto:300,400,500" );
loadCSS( "//fonts.googleapis.com/css?family=Lato:300,400" );
script(src="{{ url_for('static_pillar', filename='assets/js/markdown.min.js', v=040820161) }}")
script(src="{{ url_for('static_pillar', filename='assets/js/tutti.min.js', v=040820161) }}")
link(href="{{ url_for('static_pillar', filename='assets/ico/favicon.png') }}", rel="shortcut icon")
link(href="{{ url_for('static_pillar', filename='assets/ico/apple-touch-icon-precomposed.png') }}", rel="icon apple-touch-icon-precomposed", sizes="192x192")
link(href="{{ url_for('static_pillar', filename='assets/css/vendor/bootstrap.min.css') }}", rel="stylesheet")
| {% block head %}{% endblock %}
| {% block css %}
| {% if title == 'blog' %}
link(href="{{ url_for('static_pillar', filename='assets/css/blog.css', v=040820161) }}", rel="stylesheet")
| {% else %}
link(href="{{ url_for('static_pillar', filename='assets/css/main.css', v=040820161) }}", rel="stylesheet")
| {% endif %}
| {% endblock %}
| {% if not title %}{% set title="default" %}{% endif %}
body(class="{{ title }}")
.container-page
header.navbar-backdrop-container
| {% block header_backdrop %}
img(src="{{ url_for('static', filename='assets/img/backgrounds/pattern_02_blur.jpg')}}")
| {% endblock %}
| {% with messages = get_flashed_messages(with_categories=True) %}
| {% if messages %}
| {% for (category, message) in messages %}
.alert(role="alert", class="alert-{{ category }}")
i.alert-icon(class="{{ category }}")
span {{ message }}
button.close(type="button", data-dismiss="alert")
i.pi-cancel
| {% endfor %}
| {% endif %}
| {% endwith %}
nav.navbar.navbar-transparent.navbar-fixed-top
.navbar-overlay
.navbar-container
header.navbar-header
button.navbar-toggle(data-target=".navbar-collapse", data-toggle="collapse", type="button")
span.sr-only Toggle navigation
i.pi-menu
a.navbar-brand(
href="/",
title="Blender Cloud")
span.app-logo
i.pi-blender-cloud
| {% block navigation_search %}
.search-input
input#cloud-search(
type="text",
placeholder="Search assets, tutorials...")
i.search-icon.pi-search
| {% endblock %}
nav.collapse.navbar-collapse
ul.nav.navbar-nav.navbar-right
| {% if node and node.properties and node.properties.category %}
| {% set category = node.properties.category %}
| {% else %}
| {% set category = title %}
| {% endif %}
| {% block navigation_sections %}
li
a.navbar-item(
href="{{ url_for('main.main_blog') }}",
title="Blender Cloud Blog",
data-toggle="tooltip",
data-placement="bottom",
class="{% if category == 'blog' %}active{% endif %}")
span Blog
li(class="dropdown libraries")
a.navbar-item.dropdown-toggle(
href="#",
data-toggle="dropdown",
title="Libraries")
span Libraries
i.pi-angle-down
ul.dropdown-menu
li
a.navbar-item(
href="{{ url_for('main.redir_hdri') }}",
title="HDRI Library",
data-toggle="tooltip",
data-placement="left")
i.pi-globe
| HDRI
li
a.navbar-item(
href="{{ url_for('main.redir_textures') }}",
title="Textures Library",
data-toggle="tooltip",
data-placement="left")
i.pi-folder-texture
| Textures
li
a.navbar-item(
href="{{ url_for('main.redir_characters') }}",
title="Character Library",
data-toggle="tooltip",
data-placement="left")
i.pi-character
| Characters
li
a.navbar-item(
href="{{ url_for('main.gallery') }}",
title="Curated artwork collection",
data-toggle="tooltip",
data-placement="left")
i.pi-image
| Art Gallery
li
a.navbar-item(
href="{{ url_for('main.training') }}",
title="Training & Tutorials",
data-toggle="tooltip",
data-placement="bottom",
class="{% if category == 'training' %}active{% endif %}")
span Training
li
a.navbar-item(
href="{{ url_for('main.open_projects') }}",
title="Browse all the Open Projects",
data-toggle="tooltip",
data-placement="bottom",
class="{% if category in ['open-projects', 'film'] %}active{% endif %}")
span Open Projects
li
a.navbar-item(
href="{{ url_for('main.services') }}",
title="Blender Cloud Services",
data-toggle="tooltip",
data-placement="bottom",
class="{% if category == 'services' %}active{% endif %}")
span Services
| {% endblock %}
| {% if current_user.is_anonymous %}
li
a.navbar-item(
href="https://store.blender.org/product/membership/",
title="Sign up") Sign up
| {% endif %}
| {% if current_user.is_authenticated %}
| {% if current_user.has_role('demo') %}
| {% set subscription = 'demo' %}
| {% elif current_user.has_role('subscriber') %}
| {% set subscription = 'subscriber' %}
| {% else %}
| {% set subscription = 'none' %}
| {% endif %}
li.nav-notifications
a.navbar-item#notifications-toggle(
title="Notifications",
data-toggle="tooltip",
data-placement="bottom")
i.pi-notifications-none.nav-notifications-icon
span#notifications-count
span
.flyout-hat
#notifications.flyout.notifications
.flyout-content
span.flyout-title Notifications
a#notifications-markallread(
title="Mark All as Read",
href="/notifications/read-all")
| Mark All as Read
| {% include '_notifications.html' %}
li(class="dropdown{% if title in ['profile', 'billing-address', 'pledges', 'manage-collection']: %} active{% endif %}")
a.navbar-item.dropdown-toggle(href="#", data-toggle="dropdown", title="{{ current_user.email }}")
img.gravatar(
src="{{ current_user.gravatar }}",
class="{{ subscription }}",
alt="Avatar")
.special(class="{{ subscription }}")
| {% if subscription == 'subscriber' %}
i.pi-check
| {% elif subscription == 'demo' %}
i.pi-heart-filled
| {% else %}
i.pi-attention
| {% endif %}
ul.dropdown-menu
| {% if not current_user.has_role('protected') %}
li.subscription-status(class="{{ subscription }}")
| {% if subscription == 'subscriber' %}
a.navbar-item(
href="{{url_for('users.settings_billing')}}"
title="View subscription info")
i.pi-grin
span Your subscription is active!
| {% elif subscription == 'demo' %}
a.navbar-item(
href="{{url_for('users.settings_billing')}}"
title="View subscription info")
i.pi-heart-filled
span You have a free account.
| {% else %}
a.navbar-item(
href="https://store.blender.org/product/membership/"
title="Renew subscription")
i.pi-unhappy
span.info Your subscription is not active.
span.renew Click here to renew.
| {% endif %}
li
a.navbar-item(
href="{{ url_for('projects.home_project') }}"
title="Home")
i.pi-home
| Home
li
home_project
a.navbar-item(
href="{{ url_for('projects.index') }}"
title="My Projects")
i.pi-star
| My Projects
li
a.navbar-item(
href="{{ url_for('users.settings_profile') }}"
title="Settings")
i.pi-cog
| Settings
li
a.navbar-item(
href="{{ url_for('users.settings_billing') }}"
title="Billing")
i.pi-credit-card
| Subscription
li.divider(role="separator")
| {% endif %}
li
a.navbar-item(
href="{{ url_for('users.logout') }}")
i.pi-log-out(title="Log Out")
| Log out
| {% else %}
li.nav-item-sign-in
a.navbar-item(href="{{ url_for('users.login') }}")
| Log in
| {% endif %}
.page-content
#search-overlay
| {% block page_overlay %}
#page-overlay
| {% endblock %}
.page-body
| {% block body %}{% endblock %}
| {% block footer_container %}
#footer-container
| {% block footer_navigation %}
#footer-navigation
.container
.row
.col-md-4.col-xs-6
.footer-support
h4 Support & Feedback
p.
Let us know what you think or if you have any issues
just write to cloudsupport at blender dot org
.col-md-2.col-xs-6
ul.footer-social
li
a(href="https://twitter.com/Blender_Cloud",
title="Follow us on Twitter")
i.pi-social-twitter
li
a(href="mailto:cloudsupport@blender.org"
title="Support Email")
i.pi-email
.col-md-2.col-xs-6
h4
a(href="{{ url_for('main.homepage') }}")
| Blender Cloud
ul.footer-links
li
a(href="{{ url_for('main.main_blog') }}",
title="Blender Cloud Blog")
| Blog
li
a(href="{{ url_for('main.services') }}",
title="Blender Cloud Services")
| Services
li
a(href="https://cloud.blender.org/blog/blender-cloud-v3",
title="About Blender Cloud")
| About
.col-md-2.col-xs-6
h4
a(href="https://www.blender.org",
title="Blender official Website")
| Blender
ul.footer-links
li
a(href="https://www.blender.org",
title="Blender official Website")
| Blender.org
li
a(href="https://www.blender.org/store",
title="The official Blender Store")
| Blender Store
.col-md-2.col-xs-6.special
| With the support of the <br/> MEDIA Programme of the European Union<br/><br/>
img(alt="MEDIA Programme of the European Union",
src="https://gooseberry.blender.org/wp-content/uploads/2014/01/media_programme.png")
| {% endblock %}
| {% block footer %}
footer.container
ul.links
li
a(href="{{ url_for('main.homepage') }}")
| Blender Cloud
#hop(title="Be awesome in space")
i.pi-angle-up
| {% endblock %}
| {% endblock %}
#notification-pop(data-url="", data-read-toggle="")
.nc-progress
a#pop-close(href="#", title="Dismiss")
i.pi-cancel
.nc-item
.nc-avatar
.nc-text
span.nc-date
a(href="")
noscript
link(href='//fonts.googleapis.com/css?family=Roboto:300,400,500', rel='stylesheet', type='text/css')
link(href='//fonts.googleapis.com/css?family=Lato:300,400', rel='stylesheet', type='text/css')
script(type="text/javascript", src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js")
script(type="text/javascript", src="//cdnjs.cloudflare.com/ajax/libs/jquery.perfect-scrollbar/0.6.10/js/min/perfect-scrollbar.min.js")
script(type="text/javascript", src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js")
script.
$(document).ready(function() {
{% if current_user.is_authenticated %}
getNotificationsLoop(); // Check for new notifications in the background
// Resize #notifications and change overflow for scrollbars
$(window).on("resize", function() { notificationsResize(); });
// Load perfectScrollbar
Ps.initialize(document.getElementById('notifications'), {suppressScrollX: true});
{% endif %}
});
| {% block footer_scripts %}{% endblock %}
script.
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '{{ config.GOOGLE_ANALYTICS_TRACKING_ID }} ', 'auto', {'allowAnchor': true});
ga('send', 'pageview');

View File

@@ -0,0 +1,445 @@
#comments-container
a(name="comments")
section#comments-list
.comment-reply-container
| {% if current_user.is_authenticated %}
| {% if has_method_POST %}
.comment-reply-avatar
img(src="{{ current_user.gravatar }}")
.comment-reply-form
.comment-reply-field
textarea(
id="comment_field",
data-parent_id="{{ parent_id }}",
placeholder="Join the conversation...",)
.comment-reply-meta
.comment-details
.comment-rules
a(
title="Markdown Supported"
href="https://guides.github.com/features/mastering-markdown/")
i.pi-markdown
.comment-author
span.commenting-as commenting as
span.author-name {{ current_user.full_name }}
button.comment-action-cancel.btn.btn-outline(
type="button",
title="Cancel")
i.pi-cancel
button.comment-action-submit.btn.btn-outline(
id="comment_submit",
type="button",
title="Post Comment")
| Post Comment
span.hint (Ctrl+Enter)
.comment-reply-preview
| {% else %}
| {# * It's authenticated, but has no 'POST' permission #}
.comment-reply-form
.comment-reply-field.sign-in
textarea(
disabled,
id="comment_field",
data-parent_id="{{ parent_id }}",
placeholder="")
.sign-in
| Join the conversation!&nbsp;<a href="https://store.blender.org/product/membership/">Subscribe to Blender Cloud now.</a>
| {% endif %}
| {% else %}
| {# * It's not autenticated #}
.comment-reply-form
.comment-reply-field.sign-in
textarea(
disabled,
id="comment_field",
data-parent_id="{{ parent_id }}",
placeholder="")
.sign-in
a(href="{{ url_for('users.login') }}") Log in
| to comment.
| {% endif %}
section#comments-list-header
#comments-list-title
#comments-list-items
#comments-list-items-loading
i.pi-spin
script#comment-template(type="text/x-handlebars-template")
| {% raw %}
| {{#list items }}
.comment-container(
id="{{ _id }}",
data-node_id="{{ _id }}",
class="{{#if is_team}}is-team{{/if}}{{#if is_reply}}is-reply{{else}}is-first{{/if}}")
.comment-header
.comment-avatar
img(src="{{ gravatar }}")
.comment-author(class="{{#if is_own}}own{{/if}}")
| {{ author }}
span.username ({{ author_username }})
| {{#if is_team}}
.comment-badge.badge-team(title="Project Member") team
| {{/if}}
.comment-time {{ time_published }} {{#if time_edited }} (edited {{ time_edited }}){{/if}}
.comment-content {{{ content }}}
| {{#if is_own}}
.comment-content-preview
| {{/if}}
.comment-meta
.comment-rating(
class="{{#if is_rated}}rated{{/if}}{{#if is_rated_positive}} positive{{/if}}")
.comment-rating-value(title="Number of likes") {{ rating }}
.comment-action-rating.up(title="Like comment")
.comment-action-reply(title="Reply to this comment")
span reply
| {{#if is_own}}
.comment-action-edit
span.edit_mode(title="Edit comment") edit
span.edit_save(title="Save comment")
i.pi-check
| save changes
span.edit_cancel(title="Cancel changes")
i.pi-cancel
| cancel
| {{/if}}
| {{/list}}
| {% endraw %}
| {% block comment_scripts %}
script.
// Markdown initialization
var convert = new Markdown.getSanitizingConverter();
Markdown.Extra.init(convert);
convert = convert.makeHtml;
// Define the template for handlebars
var source = $("#comment-template").html();
var template = Handlebars.compile(source);
// Register the helper for generating the comments list
Handlebars.registerHelper('list', function(context, options) {
var ret = "";
var comments_count = 0
// Loop through all first-level comments
for(var i=0, j=context.length; i<j; i++) {
comments_count++
// Convert Markdown for each comment
context[i]['content'] = convert(context[i]['content']);
// Append compiled comment to return string
ret = ret + options.fn(context[i]);
// Search for replies to the current comment
if (context[i]['replies']) {
var replies = context[i]['replies'];
var compiled_replies = "";
// Loop through replies
for(var r=0, t=replies.length; r<t; r++) {
// Convert Markdown for each comment
replies[r]['content'] = convert(replies[r]['content']);
// Append compiled replies
compiled_replies = compiled_replies + options.fn(replies[r]);
comments_count++
}
// Append replies list to the return string
ret = ret + compiled_replies;
}
}
$("#comments-list-title").html(((comments_count > 0) ? comments_count : 'No') + ((comments_count == 1) ? ' comment' : ' comments'));
return ret;
});
// Helper for the if/else statement
Handlebars.registerHelper('if', function(conditional, options) {
if(conditional) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
/* Build the markdown preview when typing in textarea */
$(function() {
var $textarea = $('.comment-reply-field textarea'),
$container = $('.comment-reply-form'),
$preview = $('.comment-reply-preview');
// As we type in the textarea
$textarea.keyup(function(e) {
// Convert markdown
$preview.html(convert($textarea.val()));
// While we are at it, style when empty
if ($textarea.val()) {
$container.addClass('filled');
} else {
$container.removeClass('filled');
};
// Send on ctrl+enter
if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey){
$( ".comment-action-submit" ).trigger( "click" );
};
}).trigger('keyup');
});
// Get the comments list in JSON
$.getJSON( "{{url_for('nodes.comments_index')}}?parent_id={{ parent_id }}&format=json", function( data ) {
// Format using handlebars template
var comments = template(data);
if (comments && comments.trim() !="") {
$('#comments-list-items').html(comments);
} else {
$('#comments-list-items').html('');
}
})
.done(function(){
var scrollToId = location.hash;
if (scrollToId.length <= 1) return;
document.getElementById(scrollToId.substr(1)).scrollIntoView(true);
$(scrollToId).addClass('comment-linked');
});
/* Submit comment */
$('.comment-action-submit').click(function(e){
var $this = $(this);
var $textarea = $('.comment-reply-field textarea');
var commentField = document.getElementById('comment_field');
var comment = commentField.value;
function error(msg) {
// No content in the textarea
$this.addClass('button-field-error');
$textarea.addClass('field-error')
$this.html(msg);
setTimeout(function(){
$this.html('Post Comment');
$this.removeClass('button-field-error');
$textarea.removeClass('field-error');
}, 2500);
}
if (comment.length < 5) {
if (comment.length == 0) error("Say something...");
else error("Minimum 5 characters.");
return;
}
$this.addClass('submitting');
$this.html('<i class="pi-spin spin"></i> Posting...');
// Collect parent_id
parent_id = commentField.getAttribute('data-parent_id');
$.post("{{url_for('nodes.comments_create')}}",
// Submit content and parent_id for comment creation
{'content': comment, 'parent_id': parent_id}
)
.fail(function(){
$this.addClass('button-field-error');
$textarea.addClass('field-error')
$this.html("Houston! Try again?");
setTimeout(function(){
$this.html('Post Comment');
$this.removeClass('button-field-error');
$textarea.removeClass('field-error');
}, 2500);
})
.done(function(){
// Load the comments
var url = "{{url_for('nodes.comments_index')}}?parent_id={{ parent_id }}";
$.get(url, function(dataHtml) {
// Update the DOM injecting the generate HTML into the page
$('#comments-container').replaceWith(dataHtml);
})
});
});
/* Edit comment */
// Markdown convert as we type in the textarea
$(document).on('keyup','body .comment-content textarea',function(e){
var $textarea = $(this),
$container = $(this).parent(),
$preview = $container.next();
// Convert markdown
$preview.html(convert($textarea.val()));
// While we are at it, style if empty
if (!$textarea.val()) {
$container.addClass('empty');
} else {
$container.removeClass('empty');
};
}).trigger('keyup');
/* Enter edit mode */
$(document).on('click','body .comment-action-edit span.edit_mode',function(){
$(this).hide();
$(this).siblings('span.edit_cancel').show();
$(this).siblings('span.edit_save').show();
var comment_content = $(this).parent().parent().siblings('.comment-content');
var comment_id = comment_content.parent().attr('data-node_id');
var height = comment_content.height();
var url = '/nodes/' + comment_id + '/view?format=json';
$.get(url, function(data) {
var comment_raw = data['node']['properties']['content'];
comment_content.html('<textarea>' + comment_raw + '</textarea>');
comment_content.addClass('editing')
.find('textarea')
.height(height)
.focus();
comment_content.siblings('.comment-content-preview').show();
})
.fail(function(data){
statusBarSet('error', 'Error entering edit mode.', 'pi-warning');
});
});
/* Return UI to normal, when cancelling or saving */
function commentEditCancel(comment_container) {
var comment_id = comment_container.parent().attr('id');
var url = '/nodes/' + comment_id + '/view?format=json';
$.get(url, function(data) {
var comment_raw = data['node']['properties']['content'];
comment_container.html(convert(comment_raw))
.removeClass('editing');
comment_container.siblings('.comment-content-preview').html('').hide();
})
.fail(function(data){
statusBarSet('error', 'Error canceling.', 'pi-warning');
});
}
$(document).on('click','body .comment-action-edit span.edit_cancel',function(e){
$(this).hide();
$(this).siblings('span.edit_save').hide();
$(this).siblings('span.edit_mode').show();
var commentContainer = $(this).parent().parent().siblings('.comment-content');
commentEditCancel(commentContainer);
});
/* Save edited comment */
$(document).on('click','body .comment-action-edit span.edit_save',function(e){
var $this = $(this);
var commentContainer = $(this).parent().parent().siblings('.comment-content');
var commentField = commentContainer.find('textarea');
var comment = commentField.val();
var commentId = commentContainer.parent().attr('id');
function error(msg) {
// No content in the textarea
$this.addClass('error')
.html(msg);
commentField.addClass('field-error')
setTimeout(function(){
$this.html('<i class="pi-check"></i> save changes')
.removeClass('error');
commentField.removeClass('field-error');
}, 2500);
}
if (comment.length < 5) {
if (comment.length == 0) error("Say something...");
else error("Minimum 5 characters.");
return;
}
$this.addClass('saving')
.html('<i class="pi-spin spin"></i> Saving...');
$.post('/nodes/comments/' + commentId,
{'content': comment}
)
.fail(function(){
$this.addClass('error')
.html("Houston! Try again?");
commentField.addClass('field-error')
setTimeout(function(){
$this.html('Save changes')
.removeClass('error');
commentField.removeClass('field-error');
}, 2500);
})
.done(function(){
commentEditCancel(commentContainer);
commentContainer
.html(convert(comment));
commentContainer.next().text(comment);
$this.html('<i class="pi-grin"></i> saved!')
.removeClass('saving')
.siblings('span.edit_cancel').hide();
setTimeout(function(){
$this.html('<i class="pi-check"></i> save changes')
.hide()
.siblings('span.edit_mode').show();
}, 2500);
});
});
| {% endblock %}

View File

@@ -0,0 +1,189 @@
script(type="text/javascript").
/* Convert Markdown */
var convert_fields = '.node-details-description, .blog_index-item .item-content';
var convert = new Markdown.getSanitizingConverter();
Markdown.Extra.init(convert);
convert = convert.makeHtml;
/* Parse description/content fields to convert markdown */
$(convert_fields).each(function(i){
$(convert_fields).eq(i).html(convert($(convert_fields).eq(i).text()));
});
ProjectUtils.setProjectAttributes({isProject: false});
{% if node %}
ProjectUtils.setProjectAttributes({
nodeId: '{{node._id}}',
nodeType: '{{node.node_type}}'});
var node_type = ProjectUtils.nodeType();
var node_type_str = node_type;
if (node_type === 'group'){
node_type_str = 'Folder';
} else if (node_type === 'group_texture') {
node_type_str = 'Texture Folder';
} else if (node_type === 'group_hdri') {
node_type_str = 'HDRi Folder';
}
$('a', '.button-edit').html('<i class="pi-edit button-edit-icon"></i> Edit ' + node_type_str);
$('a', '.button-delete').html('<i class="pi-trash button-delete-icon"></i>Delete ' + node_type_str);
{% if parent %}
ProjectUtils.setProjectAttributes({parentNodeId: '{{parent._id}}'});
{% endif %}
// If we are im preview mode, update the image source
var page_overlay = document.getElementById('page-overlay');
if (page_overlay.classList.contains('active')) {
var node_preview = document.getElementById('node-preview');
if (node_preview){
if ($(node_preview).hasClass('image') || $(node_preview).hasClass('file')){
var src = $(node_preview).find('img').attr('src');
showOverlayPreviewImage(src);
}
} else {
$(page_overlay).html('<div class="nav-prev"></div><div class="no-preview">No Preview Available</div><div class="nav-next"></div>');
}
}
function loadComments(){
var commentsUrl = "{{ url_for('nodes.comments_index', parent_id=node._id) }}";
$.get(commentsUrl, function(dataHtml) {
})
.done(function(dataHtml){
// Update the DOM injecting the generate HTML into the page
$('#comments-container').replaceWith(dataHtml);
})
.fail(function(e, data){
statusBarSet('error', 'Couldn\'t load comments. Error: ' + data.errorThrown, 'pi-attention', 5000);
$('#comments-container').html('<a id="comments-reload"><i class="pi-refresh"></i> Reload comments</a>');
});
}
loadComments();
$('body').on('click', '#comments-reload', function(){
loadComments();
});
{% if node.has_method('PUT') %}
$('.project-mode-view').show();
{% else %}
$('.project-mode-view').hide();
{% endif %}
{% if node.picture %}
function showOverlayPreviewImage(src) {
$(page_overlay)
.addClass('active')
.html('<div class="nav-prev"></div><img src="' + src + '"/><div class="nav-next"></div>');
}
$('#node-preview.image, #node-preview.file').click(function(e){
e.preventDefault();
e.stopPropagation();
showOverlayPreviewImage("{{ node.picture.thumbnail('l', api=api) }}");
});
{% endif %}
// Click anywhere in the page to hide the overlay
function hidePageOverlay() {
$(page_overlay)
.removeAttr('class')
.html('');
}
$(page_overlay).click(function(e) {
e.stopPropagation();
e.preventDefault();
hidePageOverlay();
});
function navigateTree(prev){
var tree = $('#project_tree').jstree(true);
var curr = tree.get_selected(false);
if (prev === undefined){
var n = tree.get_next_dom(curr);
} else {
var n = tree.get_prev_dom(curr);
}
if (n && n.length > 0) {
tree.deselect_all();
tree.select_node(n);
}
}
document.onkeydown = function(e) {
var event = document.all ? window.event : e;
switch (e.target.tagName.toLowerCase()) {
case "input":
case "textarea":
case "select":
case "button":
break
default:
if (event.keyCode==27) hidePageOverlay();
if (event.keyCode==37) navigateTree(true);
if (event.keyCode==39) navigateTree();
break;
}
}
$(page_overlay).find('.nav-prev').click(function(e){
e.stopPropagation();
e.preventDefault();
navigateTree(true);
});
$(page_overlay).find('.nav-next').click(function(e){
e.stopPropagation();
e.preventDefault();
navigateTree();
});
// Auto-scale the image preview to the right aspect ratio
var node_preview = document.getElementById("node-preview-thumbnail");
if (node_preview) {
node_preview.addEventListener('load', function() {
var preview_aspect = this.naturalWidth / this.naturalHeight
if (preview_aspect > 1.0){
$('.node-preview, .node-preview-thumbnail').css({'max-height': 'auto', 'width': '100%'});
$('.node-preview img').css({'max-height': '100%'});
}
});
}
$('#node-overlay').click(function(){
$(this).removeClass('active').hide().html();
});
if (typeof $().popover != 'undefined'){
$('#asset-license').popover();
}
{% endif %}
if (typeof $().tooltip != 'undefined'){
$('[data-toggle="tooltip"]').tooltip({'delay' : {'show': 1250, 'hide': 250}});
}

View File

@@ -0,0 +1,128 @@
| {% block body %}
#node-container
#node-overlay
| {% if node.picture %}
section#node-preview.node-preview.file
img.node-preview-thumbnail#node-preview-thumbnail(
src="{{ node.picture.thumbnail('l', api=api) }}")
| {% endif %}
section.node-details-container.file
.node-details-header
.node-title#node-title
| {{node.name}}
.node-details-meta.header
ul.node-details-meta-list
| {% if node.permissions.world %}
li.node-details-meta-list-item.access.public(
data-toggle="tooltip",
data-placement="left",
title="Anybody can download. Share it!")
i.pi-lock-open
span Public
| {% endif %}
| {% if node.file %}
li.node-details-meta-list-item.type
| {{ node.file.content_type }}
li.node-details-meta-list-item.file.length
| {{ node.file.length | filesizeformat }}
| {% endif %}
| {% if node.properties.license_type %}
| {% if node.properties.license_notes %}
li.node-details-meta-list-item.license(
id="asset-license",
data-toggle="popover",
data-placement="left",
data-trigger="hover",
data-content="{{ node.properties.license_notes }}",
title="{{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% else %}
li.node-details-meta-list-item.license(
id="asset-license",
data-toggle="tooltip",
data-placement="bottom",
title="{{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% endif %}
| {% endif %}
| {% if node.file %}
li.node-details-meta-list-item.file.download(title="Download File")
| {% if node.file.link %}
a(href="{{ node.file.link }}",
title="Download file",
download="{{ node.file.filename }}")
button.btn.btn-default(type="button")
i.pi-download
| {% else %}
button.btn.btn-default.disabled.sorry(type="button")
i.pi-download
| {% endif %}
| {% endif %}
| {% if node.description %}
.node-details-description#node-description
| {{node.description}}
| {% endif %}
| {% if node.properties.license_notes %}
.node-details-meta.license
| {{ node.properties.license_notes }}
| {% endif %}
.node-details-meta.footer
ul.node-details-meta-list
li.node-details-meta-list-item.status
| {{node.properties.status}}
li.node-details-meta-list-item.author
| {{ node.user.full_name }}
li.node-details-meta-list-item.date(title="Created {{ node._created }}")
| {{ node._created | pretty_date }}
| {% if (node._created | pretty_date) != (node._updated | pretty_date) %}
span(title="Updated {{ node._updated }}") (updated {{ node._updated | pretty_date }})
| {% endif %}
#comments-container
#comments-list-items-loading
i.pi-spin
include ../../_scripts
| {% endblock %}
| {% block footer_scripts %}
script.
// Generate GA pageview
ga('send', 'pageview', location.pathname);
var content_type = $("li.node-details-meta-list-item.type").text();
var type_trimmed = content_type.substring(content_type.indexOf("/") + 1);
if (type_trimmed == 'x-blender' || type_trimmed == 'blend'){
type_trimmed = '<span class="blend"><i class="pi-blender-logo"></i></span>';
};
$("li.node-details-meta-list-item.type").html(type_trimmed);
$('.sorry').click(function() {
$.get('/403', function(data) {
$('#node-overlay').html(data).addClass('active');
})
});
| {% endblock %}

View File

@@ -0,0 +1,4 @@
| {% extends 'layout.html' %}
| {% block footer_scripts %}
| {% endblock %}

View File

@@ -0,0 +1,128 @@
| {% block body %}
#node-container
#node-overlay
| {% if node.picture %}
section#node-preview.node-preview.image
img.node-preview-thumbnail#node-preview-thumbnail(
src="{{ node.picture.thumbnail('l', api=api) }}")
| {% endif %}
section.node-details-container.image
.node-details-header
.node-title#node-title
| {{node.name}}
.node-details-meta.header
ul.node-details-meta-list
| {% if node.permissions.world %}
li.node-details-meta-list-item.access.public(
data-toggle="tooltip",
data-placement="left",
title="Anybody can download. Share it!")
i.pi-lock-open
span Public
| {% endif %}
| {% if node.short_link %}
li.node-details-meta-list-item.access.shared
a(href="{{ node.short_link }}")
i.pi-share
| Shared
| {% endif %}
| {% if node.file %}
li.node-details-meta-list-item.type
| {{ node.file.content_type }}
li.node-details-meta-list-item.image.length
| {{ node.file.length | filesizeformat }}
| {% endif %}
| {% if node.properties.license_type %}
| {% if node.properties.license_notes %}
li.node-details-meta-list-item.license(
id="asset-license",
data-toggle="popover",
data-placement="left",
data-trigger="hover",
data-content="{{ node.properties.license_notes }}",
title=" {{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% else %}
li.node-details-meta-list-item.license(
id="asset-license",
data-toggle="tooltip",
data-placement="bottom",
title="{{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% endif %}
| {% endif %}
| {% if node.file %}
li.node-details-meta-list-item.image.download(title="Download Image")
| {% if node.file.link %}
a(href="{{ node.file.link }}",
title="Download image",
download="{{ node.file.filename }}")
button.btn.btn-default(type="button")
i.pi-download
| {% else %}
button.btn.btn-default.disabled.sorry(type="button")
i.pi-download
| {% endif %}
| {% endif %}
| {% if node.description %}
.node-details-description#node-description
| {{node.description}}
| {% endif %}
| {% if node.properties.license_notes %}
.node-details-meta.license
| {{ node.properties.license_notes }}
| {% endif %}
.node-details-meta.footer
ul.node-details-meta-list
| {% if node.has_method('PUT') %}
li.node-details-meta-list-item.status
| {{node.properties.status}}
| {% endif %}
li.node-details-meta-list-item.author
| {{ node.user.full_name }}
li.node-details-meta-list-item.date(title="Created {{ node._created }}")
| {{ node._created | pretty_date }}
| {% if (node._created | pretty_date) != (node._updated | pretty_date) %}
span(title="Updated {{ node._updated }}") (updated {{ node._updated | pretty_date }})
| {% endif %}
#comments-container
#comments-list-items-loading
i.pi-spin
include ../../_scripts
| {% endblock %}
| {% block footer_scripts %}
script.
// Generate GA pageview
ga('send', 'pageview', location.pathname);
var content_type = $("li.node-details-meta-list-item.type").text();
$("li.node-details-meta-list-item.type").text(content_type.substring(content_type.indexOf("/") + 1));
$('.sorry').click(function() {
$.get('/403', function(data) {
$('#node-overlay').html(data).addClass('active');
})
});
| {% endblock %}

View File

@@ -0,0 +1,6 @@
| {% extends 'layout.html' %}
| {% from '_macros/_file_uploader_javascript.html' import render_file_uploader_javascript %}
| {% block footer_scripts %}
| {{render_file_uploader_javascript()}}
| {% endblock %}

View File

@@ -0,0 +1,158 @@
| {% block body %}
#node-container
#node-overlay
section.node-preview.video
#flowplayer_container.is-splash.play-button(
style="{% if node.picture %}background-image:url({{node.picture.thumbnail('l', api=api)}}); background-repeat:no-repeat; {% endif %}")
.fp-startscreen.fp-toggle
a.big-play-button
i.pi-play
.fp-endscreen
a.watch-again.fp-toggle
i.pi-replay
| Watch again
.fp-waiting
i.pi-spin.spin
section.node-details-container.video
.node-details-header
.node-title#node-title
| {{node.name}}
.node-details-meta.header
ul.node-details-meta-list
| {% if node.permissions.world %}
li.node-details-meta-list-item.access.public(
data-toggle="tooltip",
data-placement="bottom",
title="Anybody can download. Share it!")
i.pi-lock-open
span Public
| {% endif %}
| {% if node.properties.license_type %}
| {% if node.properties.license_notes %}
li.node-details-meta-list-item.video.license(
id="asset-license",
data-toggle="popover",
data-placement="left",
data-trigger="hover",
data-content="{{ node.properties.license_notes }}",
title="{{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% else %}
li.node-details-meta-list-item.video.license(
id="asset-license",
data-toggle="tooltip",
data-placement="bottom",
title="{{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% endif %}
| {% endif %}
| {% if node.file %}
| {% if node.file_variations %}
li.btn-group.node-details-meta-list-item.video.download(
title="Download Video")
button.btn.btn-default.dropdown-toggle(
type="button",
data-toggle="dropdown",
aria-haspopup="true",
aria-expanded="false")
i.pi-download
i.pi-angle-down.icon-dropdown-menu
ul.dropdown-menu
| {% for child in node.file_variations %}
li
a(href="{{ child.link }}",
title="Download this video format",
download)
span.length {{ child.length | filesizeformat }}
span.format {{ child.format }}
span.size {{ child.size }}
| {% endfor %}
| {% else %}
li.btn-group.node-details-meta-list-item.video.download.disabled(
title="Download Video")
button.btn.btn-default.sorry(type="button")
i.pi-download
i.pi-angle-down.icon-dropdown-menu
| {% endif %}
| {% endif %}
| {% if node.description %}
.node-details-description#node-description
| {{node.description}}
| {% endif %}
| {% if node.properties.license_notes %}
.node-details-meta.license
| {{ node.properties.license_notes }}
| {% endif %}
.node-details-meta.footer
ul.node-details-meta-list
li.node-details-meta-list-item.status
| {{node.properties.status}}
li.node-details-meta-list-item.author
| {{ node.user.full_name }}
li.node-details-meta-list-item.date(title="Created {{ node._created }}")
| {{ node._created | pretty_date }}
| {% if (node._created | pretty_date) != (node._updated | pretty_date) %}
span(title="Updated {{ node._updated }}") (updated {{ node._updated | pretty_date }})
| {% endif %}
#comments-container
#comments-list-items-loading
i.pi-spin
include ../../_scripts
| {% endblock %}
| {% block footer_scripts %}
script(type="text/javascript").
$(function(){
// Generate GA pageview
ga('send', 'pageview', location.pathname);
var content_type = $("li.node-details-meta-list-item.type").text();
$("li.node-details-meta-list-item.type").text(content_type.substring(content_type.indexOf("/") + 1));
var container = document.getElementById("flowplayer_container");
flowplayer(container, {
key: "{{config.FLOWPLAYER_KEY}}",
embed: false,
splash: true,
{% if node.video_sources %}
clip: {
sources: {{ node.video_sources | safe }}
}
{% else %}
disabled: true
{% endif %}
});
{% if not node.video_sources %}
$('#flowplayer_container, .sorry').click(function() {
$.get('/403', function(data) {
$('#node-overlay').html(data).addClass('active');
})
});
{% endif %}
});
| {% endblock %}

View File

@@ -0,0 +1,127 @@
#theatre-media
img(src="{{ node.picture.thumbnail('h', api=api) }}", onmousedown="return false")
ul#theatre-tools
li.theatre-tool-resize(title="Toggle Normal Size")
span
i.pi-resize-full
| {% if node.file and node.file.link %}
li.download
a(href="{{ node.file.link }}",
title="Download the original file",
download="{{ node.file.filename }}")
i.pi-download
| {% else %}
li.download.disabled
a(href="{{ url_for('users.login') }}",
title="Sign in to download the original file")
i.pi-download
| {% endif %}
#theatre-info
.theatre-info-header
.theatre-info-title {{ node.name }}
.theatre-info-user {{ node.user.full_name }}
.theatre-info-date {{ node._created | pretty_date_time }}
ul.theatre-info-details
li
span Type
span {{ node.file.content_type }}
li
span Dimensions
span {{ node.file.width }} <small>x</small> {{ node.file.height }}
li
span Size
span {{ node.file.length | filesizeformat }}
| {% if node.short_link %}
li
span Share link
a(href="{{ node.short_link }}") {{ node.short_link }}
| {% endif %}
#comments-container
#comments-list-items-loading
i.pi-spin
include ../_scripts
script.
$(function () {
// Load scrollbar for sidebar
Ps.initialize(document.getElementById('theatre-info'), {suppressScrollX: true});
var file_width = {{ node.file.width }};
var file_height = {{ node.file.height }};
var theatre_media = document.getElementById('theatre-media');
var $theatre_media = $(theatre_media);
function canZoom() {
return theatre_media.scrollWidth < file_width ||
theatre_media.scrollHeight < file_height;
}
// TODO: update this whenever the screen resizes.
if (canZoom()) $theatre_media.addClass('zoomed-out');
function theatreZoom() {
var started_zoomed_in = $theatre_media.hasClass('zoomed-in');
// See if we need to zoom in at all. Zooming out is always allowed.
if (!started_zoomed_in && !canZoom()) {
$theatre_media.removeClass('zoomed-out');
return;
}
// Use add/removeClass to ensure there is always exactly one of zoomed-{in,out}.
// If we were to use toggleClass() they could both be applied when we started
// without zoomed-out class.
if (started_zoomed_in) {
$theatre_media.removeClass('zoomed-in');
$theatre_media.addClass('zoomed-out');
Ps.destroy(theatre_media);
} else {
$theatre_media.addClass('zoomed-in');
$theatre_media.removeClass('zoomed-out');
Ps.initialize(theatre_media);
}
// Style toolbar button
$('ul#theatre-tools li.theatre-tool-resize').toggleClass('active');
}
$('ul#theatre-tools li.theatre-tool-resize').on('click', function (e) {
theatreZoom();
});
$('ul.nav.navbar-nav a.navbar-item.info').on('click', function (e) {
e.preventDefault();
$('#theatre-container').toggleClass('with-info');
});
$("#theatre-media img").on('click', function (e) {
var $parent = $(this).parent();
var mouse_x = e.pageX;
var mouse_y = e.pageY;
// Compute relative position before zooming in.
var pre_width = e.target.clientWidth;
var rel_x = e.offsetX / pre_width;
var rel_y = e.offsetY / e.target.clientHeight;
theatreZoom();
var post_width = e.target.clientWidth;
if (post_width > pre_width) {
// We zoomed in, scroll such that the target position is under the mouse.
var target_x = Math.round(rel_x * post_width);
var target_y = Math.round(rel_y * e.target.clientHeight);
$parent
.scrollLeft(target_x - mouse_x + e.target.parentElement.parentElement.offsetLeft)
.scrollTop(target_y - mouse_y + e.target.parentElement.parentElement.offsetTop);
}
});
});

View File

@@ -0,0 +1,130 @@
| {% extends 'layout.html' %}
| {% set title = 'blog' %}
| {% block page_title %}Blog{% endblock%}
| {% block body %}
.container.box
#blog_container(class="{% if project._id == config.MAIN_PROJECT_ID %}cloud-blog{% endif %}")
#blog_index-container
| {% if project._id == config.MAIN_PROJECT_ID and project.node_type_has_method('post', 'POST', api=api) %}
a.btn.btn-default.button-create(href="{{url_for('nodes.posts_create', project_id=project._id)}}")
i.pi-plus
| Create New Post
| {% endif %}
| {% if posts %}
| {% for node in posts %}
| {% if loop.first %}
| {% if node.picture %}
.blog_index-header
img(src="{{ node.picture.thumbnail('l', api=api) }}")
| {% endif %}
.blog_index-item
a.item-title(
href="{{ url_for_node(node=node) }}")
| {{node.name}}
.item-info.
<span title="{{node._created}}">{{node._created | pretty_date }}</span>
{% if node._created != node._updated %}
<span title="{{node._updated}}">(updated {{node._updated | pretty_date }})</span>
{% endif %}
{% if node.properties.category %}| {{node.properties.category}}{% endif %}
| by {{node.user.full_name}}
| <a href="{{ url_for_node(node=node) }}#comments">Leave a comment</a>
{% if node.properties.status != 'published' %} | {{ node.properties.status}} {% endif %}
.item-content
| {{node.properties.content}}
.item-meta
a(href="{{ url_for_node(node=node) }}#comments") Leave a comment
| {% else %}
| {% if loop.index == 2 %}
h4.blog_index-title Blasts from the past
| {% endif %}
.blog_index-item.list
| {% if node.picture %}
.item-header
img.image(src="{{ node.picture.thumbnail('s', api=api) }}")
| {% else %}
.item-header.nothumb
i.pi-document-text
| {% endif %}
a.item-title(
href="{{ url_for_node(node=node) }}")
| {{node.name}}
.item-info.
<span title="{{node._created}}">{{node._created | pretty_date }}</span>
{% if node._created != node._updated %}
<span title="{{node._updated}}">(updated {{node._updated | pretty_date }})</span>
{% endif %}
{% if node.properties.category %}| {{node.properties.category}}{% endif %}
| by {{node.user.full_name}}
{% if node.properties.status != 'published' %} | {{ node.properties.status}} {% endif %}
| {% endif %} {# loop #}
| {% endfor %} {# posts #}
| {% else %}
.blog_index-item
.item-content No posts yet.
| {% endif %} {# posts #}
| {% if project._id != config.MAIN_PROJECT_ID %}
#blog_index-sidebar
.blog_project-card
a.item-header(
href="{{ url_for('projects.view', project_url=project.url) }}")
.overlay
| {% if project.picture_header %}
img.background(src="{{ project.picture_header.thumbnail('m', api=api) }}")
| {% endif %}
a.card-thumbnail(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {% if project.picture_square %}
img.thumb(src="{{ project.picture_square.thumbnail('m', api=api) }}")
| {% endif %}
.item-info
a.item-title(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {{ project.name }}
| {% if project.summary %}
p.item-description
| {{project.summary|safe}}
| {% endif %}
| {% if project.node_type_has_method('post', 'POST', api=api) %}
.blog_project-sidebar
a.btn.btn-default.button-create(href="{{url_for('nodes.posts_create', project_id=project._id)}}")
| Create New Post
| {% endif %}
| {% endif %}
| {% endblock %}
| {% block footer_scripts %}
include ../_scripts
script hopToTop(); // Display jump to top button
| {% endblock %}

View File

@@ -0,0 +1,254 @@
| {% block body %}
#node-container
section.node-preview.group
| {% if node.picture %}
img.backdrop(src="{{ node.picture.thumbnail('l', api=api) }}")
.overlay
| {% endif %}
.node-title#node-title
| {{node.name}}
section.node-details-container.group
.node-details-meta.preview
ul.node-details-meta-list
li.node-details-meta-list-item.date(title="Created {{ node._created | pretty_date }}")
| {{ node._updated | pretty_date }}
li.node-details-meta-list-item.author
| {{ node.user.full_name }}
| {% if node.properties.status != 'published' %}
li.node-details-meta-list-item.status
| {{node.properties.status}}
| {% endif %}
.node-details-meta-actions
.btn-browsetoggle(
title="Toggle between list/grid view",
data-toggle="tooltip",
data-placement="top")
i.pi-list
| {% if node.description %}
.node-details-description
| {{node.description}}
| {% endif %}
section.node-children.group
| {% if children %}
| {% for child in children %}
| {# Browse type: List #}
a(
href="#",
data-node_id="{{ child._id }}",
class="item_icon list-node-children-item browse-list")
.list-node-children-item-thumbnail
| {% if child.picture %}
img(
src="{{ child.picture.thumbnail('t', api=api)}} ")
| {% endif %}
.list-node-children-item-thumbnail-icon
| {# If there's a type available, otherwise show a folder icon #}
| {% if child.properties.content_type %}
| {# Show an icon if there's no thumbnail #}
| {% if not child.picture %}
| {% if child.properties.content_type == 'image' %}
i.dark.pi-image
| {% elif child.properties.content_type == 'video' %}
i.dark.pi-film-thick
| {% elif child.properties.content_type == 'file' %}
i.dark.pi-document
| {% endif %}
| {% else %}
| {% if child.properties.content_type == 'video' %}
i.pi-play
| {% endif %}
| {% endif %}
| {% else %}
| {% if not child.picture %}
i.dark.pi-folder
| {% endif %}
| {% endif %}
| {% if child.permissions.world %}
.list-node-children-item-ribbon
span free
| {% endif %}
.list-node-children-item-name {{ child.name }}
.list-node-children-item-meta
| {% if child.properties.status != 'published' %}
span.status {{ child.properties.status }}
| {% endif %}
| {% if child.properties.content_type == 'video' %}
span Video ·
| {% elif child.properties.content_type == 'image' %}
span Image ·
| {% elif child.properties.content_type == 'file' %}
span File ·
| {% else %}
| {% if child.picture %}
span Folder ·
| {% endif %}
| {% endif %}
| {% if child._updated %}
span(title="Updated on {{ child._created }}") {{ child._updated | pretty_date }}
span.updated(title="Created on {{ child._updated }}") *
| {% else %}
span(title="Created on {{ child._created }}") {{ child._created | pretty_date }}
| {% endif %}
| {# Browse type: Icon #}
a(href="#", data-node_id="{{ child._id }}", title="{{ child.name }}", class="item_icon list-node-children-item browse-icon")
.list-node-children-item-thumbnail
| {% if child.picture %}
img(
src="{{ child.picture.thumbnail('b', api=api)}} ")
| {% endif %}
.list-node-children-item-thumbnail-icon
| {% if child.properties.content_type %}
| {% if child.properties.content_type == 'video' %}
i.pi-play
| {% endif %}
| {% else %}
i.pi-folder
| {% endif %}
| {% if child.properties.status != 'published' %}
.list-node-children-item-status {{ child.properties.status }}
| {% endif %}
| {% if child.permissions.world %}
.list-node-children-item-ribbon
span free
| {% endif %}
.list-node-children-item-name
| {% if child.properties.content_type == 'video' %}
i.pi-film-thick
| {% elif child.properties.content_type == 'image' %}
i.pi-image
| {% elif child.properties.content_type == 'file' %}
i.pi-document
| {% else %}
i.pi-folder
| {% endif %}
span {{ child.name }}
| {% endfor %}
| {% else %}
.list-node-children-container
.list-node-children-empty No items... yet!
| {% endif %}
script.
// Generate GA pageview
ga('send', 'pageview', location.pathname);
$('a.item_icon').unbind("click")
.click(function(e){
e.preventDefault();
var nodeId = $(this).data('node_id');
if (ProjectUtils.projectId()) {
// When clicking on a node preview, we load its content
// displayNode will run asynchronously and set the bcloud_current_node_id
// as well, but we set it manually in the next line as well, to make sure
// that select_node on jstree works as expected, preventing the node to be
// loaded twice.
Cookies.set('bcloud_current_node_id', nodeId);
displayNode(nodeId);
// Update tree with current selection
var jstree = $('#project_tree').jstree(true);
jstree.deselect_all();
jstree.open_node('n_' + ProjectUtils.nodeId(), function() {
jstree.select_node('n_' + nodeId);
});
} else {
// If there's project_id defined, we use the full link (for search)
window.location.replace('/nodes/' + nodeId + '/redir');
};
});
// Browse type: icon or list
function projectBrowseTypeIcon() {
$(".list-node-children-item.browse-list").hide();
$(".list-node-children-item.browse-icon").show();
$(".btn-browsetoggle").html('<i class="pi-list"></i>');
};
function projectBrowseTypeList() {
$(".list-node-children-item.browse-list").show();
$(".list-node-children-item.browse-icon").hide();
$(".btn-browsetoggle").html('<i class="pi-layout"></i>');
};
function projectBrowseTypeCheck(){
/* Only run if we're in a project, or search */
if(document.getElementById("project-container") !== null || document.getElementById("search-container") !== null) {
var browse_type = Cookies.getJSON('bcloud_ui');
if (browse_type && browse_type.group_browse_type) {
if (browse_type.group_browse_type == 'icon') {
projectBrowseTypeIcon();
} else if ( browse_type.group_browse_type == 'list' ) {
projectBrowseTypeList();
}
} else {
projectBrowseTypeIcon();
};
};
}
function projectBrowseToggle(){
var browse_type = Cookies.getJSON('bcloud_ui');
if (browse_type && browse_type.group_browse_type) {
if (browse_type.group_browse_type == 'icon') {
projectBrowseTypeList();
setJSONCookie('bcloud_ui', 'group_browse_type', 'list');
} else if ( browse_type.group_browse_type == 'list' ) {
projectBrowseTypeIcon();
setJSONCookie('bcloud_ui', 'group_browse_type', 'icon');
}
} else {
projectBrowseTypeList();
setJSONCookie('bcloud_ui', 'group_browse_type', 'list');
}
}
$('.btn-browsetoggle').on('click', function (e) {
e.preventDefault();
projectBrowseToggle();
});
projectBrowseTypeCheck();
include ../_scripts
| {% endblock %}

View File

@@ -0,0 +1,159 @@
| {% block body %}
#node-container.texture
.texture-title#node-title
| {{node.name}}
| {% if node.picture %}
.texture-backdrop(
style="background-image: url({{ node.picture.thumbnail('m', api=api) }})")
| {% endif %}
| {% if children %}
section.node-row.texture-info
span.texture-info-files {{ children|length }} item{% if children|length != 1 %}s{% endif %}
| {% endif %}
section.node-children.group.texture
| {% if children %}
| {% for child in children %}
| {% if child.properties.status == 'published' %}
a.list-node-children-container(
href="#",
data-node_id="{{ child._id }}",
class="item_icon {{child.node_type}} {% if child.picture %}thumbnail{% endif %}")
.list-node-children-item-preview
span.texture-name {{child.name}}
| {% if child.picture %}
img.texture-preview(
src="",
data-preview="{{ child.picture.thumbnail('m', api=api)}}",
alt='{{child.name}}')
| {% endif %}
.list-node-children-item(class="{{child.node_type}}")
.list-node-children-item-thumbnail
| {% if child.picture %}
img.texture-thumbnail(src="{{ child.picture.thumbnail('b', api=api)}}")
| {% else %}
.list-node-children-item-thumbnail-icon
| {% if child.node_type == 'group_hdri' %}
i.pi-folder-texture
| {% else %}
i.pi-texture
| {% endif %}
| {% endif %}
| {% if child.properties.status != 'published' %}
.list-node-children-item-status {{ child.properties.status }}
| {% endif %}
| {% if child.permissions.world %}
.list-node-children-item-ribbon
span free
| {% endif %}
| {% if child.node_type == 'hdri' %}
.list-node-children-item-name
span.sizes {{ child.name }}
| {% if child.properties.files %}
span.variations
| {% if child.properties.files|length > 6 %}
span {{ child.properties.files|length }} resolutions
span from {{ child.properties.files[0].resolution }}
span to {{ child.properties.files[-1].resolution }}
| {% else %}
| {% for f in child.properties.files %}
span {{ f.resolution }}
| {% endfor %}
| {% endif %}
| {% endif %}
| {% endif %}
| {% if child.node_type == 'group_hdri' %}
.list-node-children-item-name
i.pi-folder-texture
span {{ child.name }}
| {% endif %}
| {% endif %}
| {% endfor %}
| {% else %}
.list-node-children-container
.list-node-children-empty No textures... yet!
| {% endif %}
script.
// Generate GA pageview
ga('send', 'pageview', location.href);
// Display texture preview on mouse hover
$('a.list-node-children-container.texture.thumbnail').hover(
function(){
var thumbnail = $(this);
var src = thumbnail.find('.texture-thumbnail').attr('src');
var src_xl = thumbnail.find('.texture-preview').data('preview');
// Load the bigger preview
var preview_img = thumbnail.find('.texture-preview');
preview_img.attr('src', src_xl);
if (preview_img) {
preview_img.load(function() {
var preview = thumbnail.find('.list-node-children-item-preview');
// Positioning stuff
var offset = thumbnail.offset();
var offset_min_x = $('.node-children').width() - preview.width();
var offset_min_y = $('.node-children').height() - preview.height();
if (preview && offset.top > 300) {
$(preview).css({'top': (preview.height() * (-1))});
};
if (offset.left > offset_min_x) {
$(preview).css({'right': 0});
};
$(preview).addClass('active');
});
}
},
function(){
$('.list-node-children-item-preview').removeClass('active');
});
// hide preview on mouse hover itself
$('.list-node-children-item-preview').hover(function(){
$(this).removeClass('active');
});
$('a.item_icon')
.unbind("click")
.click(function(e){
e.preventDefault();
// When clicking on a node preview, we load its content
var nodeId = $(this).data('node_id');
// displayNode will run asynchronously and set the bcloud_current_node_id
// as well, but we set it manually in the next line as well, to make sure
// that select_node on jstree works as expected, preventing the node to be
// loaded twice.
Cookies.set('bcloud_current_node_id', nodeId);
displayNode(nodeId);
// Update tree with current selection
$('#project_tree').jstree('select_node', 'n_' + nodeId);
});
include ../_scripts
| {% endblock %}

View File

@@ -0,0 +1,190 @@
| {% block body %}
#node-container.texture
.texture-title#node-title
| {{node.name}}
| {% if node.picture %}
.texture-backdrop(
style="background-image: url({{ node.picture.thumbnail('m', api=api) }})")
| {% endif %}
| {% if children %}
section.node-row.texture-info
span.texture-info-files {{ children|length }} item{% if children|length != 1 %}s{% endif %}
| {% endif %}
section.node-children.group.texture
| {% if children %}
| {% for child in children %}
| {% if child.properties.status == 'published' %}
a.list-node-children-container(
href="#",
data-node_id="{{ child._id }}",
class="item_icon {{child.node_type}} {% if child.picture %}thumbnail{% endif %}")
.list-node-children-item-preview
span.texture-name {{child.name}}
| {% if child.picture %}
img.texture-preview(
src="",
data-preview="{{ child.picture.thumbnail('m', api=api)}}",
alt='{{child.name}}')
| {% endif %}
.list-node-children-item(class="{{child.node_type}}")
.list-node-children-item-thumbnail
| {% if child.picture %}
img.texture-thumbnail(src="{{ child.picture.thumbnail('b', api=api)}}")
| {% else %}
.list-node-children-item-thumbnail-icon
| {% if child.node_type == 'group_texture' %}
i.pi-folder-texture
| {% else %}
i.pi-texture
| {% endif %}
| {% endif %}
| {% if child.properties.status != 'published' %}
.list-node-children-item-status {{ child.properties.status }}
| {% endif %}
| {% if child.permissions.world %}
.list-node-children-item-ribbon
span free
| {% endif %}
| {% if child.node_type == 'texture' %}
.list-node-children-item-name
| {% if child.picture.width %}
span.sizes
| {{ child.picture.width }}
small x
| {{ child.picture.height }}
| {% else %}
span.sizes {{ child.name }}
| {% endif %}
span.icons
| {% if child.properties.is_tileable %}
i.pi-puzzle(title="Tileable", data-toggle="tooltip", data-placement="bottom", data-delay=0)
| {% endif %}
| {% if child.properties.files %}
span.variations
| {% for f in child.properties.files %}
| {% if loop.last and loop.index > 5 %}
span.more +{{ loop.length - 5 }} more
| {% elif loop.index <= 5 %}
| {% if f.map_type == 'color' %}
span Color
| {% elif f.map_type == 'bump' %}
span Bump
| {% elif f.map_type == 'specular' %}
span Specular
| {% elif f.map_type == 'normal' %}
span Normal Map
| {% elif f.map_type == 'translucency' %}
span Translucency
| {% elif f.map_type == 'emission' %}
span Emission
| {% elif f.map_type == 'alpha' %}
span Alpha
| {% elif f.map_type == 'id' %}
span ID Map
| {% else %}
span {{ f.map_type }}
| {% endif %}
| {% endif %}
| {% endfor %}
| {% endif %}
| {% endif %}
| {% if child.node_type == 'group_texture' %}
.list-node-children-item-name
i.pi-folder-texture
span {{ child.name }}
| {% endif %}
| {% endif %}
| {% endfor %}
| {% else %}
.list-node-children-container
.list-node-children-empty No textures... yet!
| {% endif %}
script.
// Generate GA pageview
ga('send', 'pageview', location.href);
// Display texture preview on mouse hover
$('a.list-node-children-container.texture.thumbnail').hover(
function(){
var thumbnail = $(this);
var src = thumbnail.find('.texture-thumbnail').attr('src');
var src_xl = thumbnail.find('.texture-preview').data('preview');
// Load the bigger preview
var preview_img = thumbnail.find('.texture-preview');
preview_img.attr('src', src_xl);
if (preview_img) {
preview_img.load(function() {
var preview = thumbnail.find('.list-node-children-item-preview');
// Positioning stuff
var offset = thumbnail.offset();
var offset_min_x = $('.node-children').width() - preview.width();
var offset_min_y = $('.node-children').height() - preview.height();
if (preview && offset.top > 300) {
$(preview).css({'top': (preview.height() * (-1))});
};
if (offset.left > offset_min_x) {
$(preview).css({'right': 0});
};
$(preview).addClass('active');
});
}
},
function(){
$('.list-node-children-item-preview').removeClass('active');
});
// hide preview on mouse hover itself
$('.list-node-children-item-preview').hover(function(){
$(this).removeClass('active');
});
$('a.item_icon')
.unbind("click")
.click(function(e){
e.preventDefault();
// When clicking on a node preview, we load its content
var nodeId = $(this).data('node_id');
// displayNode will run asynchronously and set the bcloud_current_node_id
// as well, but we set it manually in the next line as well, to make sure
// that select_node on jstree works as expected, preventing the node to be
// loaded twice.
Cookies.set('bcloud_current_node_id', nodeId);
displayNode(nodeId);
// Update tree with current selection
$('#project_tree').jstree('select_node', 'n_' + nodeId);
});
include ../_scripts
| {% endblock %}

View File

@@ -0,0 +1,133 @@
| {% block body %}
#node-container.texture
#node-overlay
section.node-preview
| {% if node.picture %}
iframe(
width='100%',
height='450px',
scrolling='no',
frameborder='0',
allowfullscreen='',
src="{{url_for('main.vrview', preview=node.picture.thumbnail('l', api=api), image=node.picture.thumbnail('h', api=api), is_stereo='false')}}")
| {% endif %}
section.node-details-container
.node-details-header
.node-title#node-title
| {{node.name}}
.node-details-meta.header
ul.node-details-meta-list
| {% if node.permissions.world %}
li.node-details-meta-list-item.access.public(
data-toggle="tooltip",
data-placement="bottom",
title="Anybody can download. Share it!")
i.pi-lock-open
span Public
| {% endif %}
| {% if node.properties.license_type %}
| {% if node.properties.license_notes %}
li.node-details-meta-list-item.video.license(
id="asset-license",
data-toggle="popover",
data-placement="left",
data-trigger="hover",
data-content="{{ node.properties.license_notes }}",
title="{{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% else %}
li.node-details-meta-list-item.video.license(
id="asset-license",
data-toggle="tooltip",
data-placement="bottom",
title="{{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% endif %}
| {% endif %}
| {% if node.properties.files %}
li.btn-group.node-details-meta-list-item.video.download(
title="Download HDRI")
button.btn.btn-default.dropdown-toggle(
type="button",
data-toggle="dropdown",
aria-haspopup="true",
aria-expanded="false")
i.pi-download
i.pi-angle-down.icon-dropdown-menu
ul.dropdown-menu
| {% for var in node.properties.files %}
li
a(href="{{ var.file.link }}",
title="Download this HDRi format",
download)
span.length {{ var.file.length | filesizeformat }}
span.format {{ var.file.format }}
span.size {{ var.resolution }}
| {% endfor %}
| {% else %}
li.btn-group.node-details-meta-list-item.video.download.disabled(
title="Download HDRi")
button.btn.btn-default.sorry(type="button")
i.pi-download
i.pi-angle-down.icon-dropdown-menu
| {% endif %}
| {% if node.description %}
.node-details-description#node-description
| {{node.description}}
| {% endif %}
| {% if node.properties.license_notes %}
.node-details-meta.license
| {{ node.properties.license_notes }}
| {% endif %}
.node-details-meta.footer
ul.node-details-meta-list
li.node-details-meta-list-item.status
| {{node.properties.status}}
li.node-details-meta-list-item.author
| {{ node.user.full_name }}
li.node-details-meta-list-item.date(title="Created {{ node._created }}")
| {{ node._created | pretty_date }}
| {% if (node._created | pretty_date) != (node._updated | pretty_date) %}
span(title="Updated {{ node._updated }}") (updated {{ node._updated | pretty_date }})
| {% endif %}
include ../_scripts
| {% endblock %}
| {% block footer_scripts %}
script.
$('#asset-license').popover();
// Generate GA pageview
ga('send', 'pageview', location.pathname);
$('.sorry').click(function() {
$.get('/403', function(data) {
$('#node-overlay').html(data).show().addClass('active');
})
});
$('#node-overlay').click(function(){
$(this).removeClass('active').hide().html();
});
| {% endblock %}

View File

@@ -0,0 +1,175 @@
| {% extends 'layout.html' %}
| {% set title = 'blog' %}
| {% block page_title %}New {{ node_type.name }}{% endblock %}
| {% block body %}
.container.box
form(
method='POST',
action="{{url_for('nodes.posts_create', project_id=project._id)}}")
#blog_container.post-create
| {% with errors = errors %}
| {% if errors %}
| {% for field in errors %}
.alert.alert-danger(role='alert')
strong {{field}}
| {% for message in errors[field] %}
| {{message}}|
| {% endfor %}
| {% endfor %}
| {% endif %}
| {% endwith %}
#blog_index-sidebar
| {% if project._id != config.MAIN_PROJECT_ID %}
.blog_project-card
a.item-header(
href="{{ url_for('projects.view', project_url=project.url) }}")
.overlay
| {% if project.picture_header %}
img.background(src="{{ project.picture_header.thumbnail('m', api=api) }}")
| {% endif %}
a.card-thumbnail(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {% if project.picture_square %}
img.thumb(src="{{ project.picture_square.thumbnail('m', api=api) }}")
| {% endif %}
.item-info
a.item-title(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {{ project.name }}
| {% endif %}
.blog_project-sidebar
#blog_post-edit-form
| {% for field in form %}
| {% if field.name in ['picture', 'status'] %}
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
| {{ field.label }}
| {{ field(class='form-control') }}
| {% if field.errors %}
ul.error
| {% for error in field.errors %}
li {{ error }}
| {% endfor %}
| {% endif %}
| {% endif %}
| {% endfor %}
input.btn.btn-default.button-create(type='submit', value='Create {{ node_type.name }}')
a.btn.btn-default.button-back(href="{{ url_for('projects.view', project_url=project.url) }}blog")
| Back to Blog
#blog_post-create-container
#blog_post-edit-title
| Create {{ node_type.name }} on {{ project.name }}
#blog_post-edit-form
| {% for field in form %}
| {% if field.name == 'csrf_token' %}
| {{ field }}
| {% else %}
| {% if field.type == 'HiddenField' %}
| {{ field }}
| {% else %}
| {% if field.name not in ['description', 'picture', 'category', 'status'] %}
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
| {{ field.label }}
| {{ field(class='form-control') }}
| {% if field.errors %}
ul.error
| {% for error in field.errors %}
li {{ error }}
| {% endfor %}
| {% endif %}
| {% endif %}
| {% endif %}
| {% endif %}
| {% endfor %}
| {% endblock %}
| {% block footer_scripts %}
script(type="text/javascript").
function FormatForUrl(str) {
return str.replace(/_/g, '-')
.replace(/ /g, '-')
.replace(/:/g, '-')
.replace(/\\/g, '-')
.replace(/\//g, '-')
.replace(/[^a-zA-Z0-9\-]+/g, '')
.replace(/-{2,}/g, '-')
.toLowerCase();
};
var convert = new Markdown.getSanitizingConverter().makeHtml;
/* Build the markdown preview when typing in textarea */
$(function() {
var $textarea = $('.form-group.content textarea'),
$loader = $('<div class="md-preview-loading"><i class="pi-spin spin"></i></div>').insertAfter($textarea),
$preview = $('<div class="node-edit-form-md-preview" />').insertAfter($loader);
$loader.hide();
// Delay function to not start converting heavy posts immediately
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
$textarea.keyup(function() {
/* If there's an iframe (YouTube embed), delay markdown convert 1.5s */
if (/iframe/i.test($textarea.val())) {
$loader.show();
delay(function(){
// Convert markdown
$preview.html(convert($textarea.val()));
$loader.hide();
}, 1500 );
} else {
// Convert markdown
$preview.html(convert($textarea.val()));
};
}).trigger('keyup');
});
$(function() {
var $name_input = $('.form-group.name input');
$name_input.keyup(function() {
$('#url').val(FormatForUrl($name_input.val()));
}).trigger('keyup');
});
| {% endblock %}
| {% block footer_navigation %}
| {% endblock %}
| {% block footer %}
| {% endblock %}

View File

@@ -0,0 +1,168 @@
| {% extends 'layout.html' %}
| {% set title = 'blog' %}
| {% block page_title %}New {{ node_type.name }}{% endblock %}
| {% block body %}
.container.box
form(
method='POST',
action="{{url_for('nodes.posts_edit', post_id=post._id)}}")
#blog_container.post-create
| {% with errors = errors %}
| {% if errors %}
| {% for field in errors %}
.alert.alert-danger(role='alert')
strong {{field}}
| {% for message in errors[field] %}
| {{message}}|
| {% endfor %}
| {% endfor %}
| {% endif %}
| {% endwith %}
#blog_index-sidebar
| {% if project._id != config.MAIN_PROJECT_ID %}
.blog_project-card
a.item-header(
href="{{ url_for('projects.view', project_url=project.url) }}")
.overlay
| {% if project.picture_header %}
img.background(src="{{ project.picture_header.thumbnail('m', api=api) }}")
| {% endif %}
a.card-thumbnail(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {% if project.picture_square %}
img.thumb(src="{{ project.picture_square.thumbnail('m', api=api) }}")
| {% endif %}
.item-info
a.item-title(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {{ project.name }}
| {% endif %}
.blog_project-sidebar
#blog_post-edit-form
| {% for field in form %}
| {% if field.name in ['picture', 'status'] %}
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
| {{ field.label }}
| {{ field(class='form-control') }}
| {% if field.errors %}
ul.error
| {% for error in field.errors %}
li {{ error }}
| {% endfor %}
| {% endif %}
| {% endif %}
| {% endfor %}
button.btn.btn-default.button-create(type='submit')
i.pi-check
| Update {{ node_type.name }}
a.btn.btn-default.button-back(href="{{ url_for_node(node=post) }}")
i.pi-angle-left
| Back to Post
a.btn.btn-default.button-back(href="{{ url_for('projects.view', project_url=project.url) }}blog")
| Go to Blog
#blog_post-edit-container
#blog_post-edit-title
| Edit {{ node_type.name }}
#blog_post-edit-form
| {% for field in form %}
| {% if field.name == 'csrf_token' %}
| {{ field }}
| {% else %}
| {% if field.type == 'HiddenField' %}
| {{ field }}
| {% else %}
| {% if field.name not in ['description', 'picture', 'category', 'status'] %}
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
| {{ field.label }}
| {{ field(class='form-control') }}
| {% if field.errors %}
ul.error
| {% for error in field.errors %}
li {{ error }}
| {% endfor %}
| {% endif %}
| {% endif %}
| {% endif %}
| {% endif %}
| {% endfor %}
| {% endblock %}
| {% block footer_scripts %}
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.iframe-transport.min.js') }}")
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.iframe-transport.min.js') }}")
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.fileupload.min.js') }}")
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/file_upload.min.js') }}")
script(type="text/javascript").
var convert = new Markdown.getSanitizingConverter().makeHtml;
ProjectUtils.setProjectAttributes({projectId: "{{project._id}}"});
/* Build the markdown preview when typing in textarea */
$(function() {
var $textarea = $('.form-group.content textarea'),
$loader = $('<div class="md-preview-loading"><i class="pi-spin spin"></i></div>').insertAfter($textarea),
$preview = $('<div class="node-edit-form-md-preview" />').insertAfter($loader);
$loader.hide();
// Delay function to not start converting heavy posts immediately
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
$textarea.keyup(function() {
/* If there's an iframe (YouTube embed), delay markdown convert 1.5s */
if (/iframe/i.test($textarea.val())) {
$loader.show();
delay(function(){
// Convert markdown
$preview.html(convert($textarea.val()));
$loader.hide();
}, 1500 );
} else {
// Convert markdown
$preview.html(convert($textarea.val()));
};
}).trigger('keyup');
});
| {% endblock %}
| {% block footer_navigation %}
| {% endblock %}
| {% block footer %}
| {% endblock %}

View File

@@ -0,0 +1,4 @@
| {% extends 'layout.html' %}
| {% block page_title %}{{node.name}} - Blog{% endblock%}
include view_embed

View File

@@ -0,0 +1,128 @@
| {% block og %}
| {% set title = 'blog' %}
| {% if project %}
meta(property="og:title", content="{{ node.name }}{% if project._id == config.MAIN_PROJECT_ID %} — Blender Cloud Blog{% else%} - {{ project.name }} — Blender Cloud{% endif %}")
| {% endif %}
| {% if project and project.properties.picture_header %}
meta(property="og:image", content="{{ project.properties.picture_header.thumbnail('l', api=api) }}")
| {% elif node and node.picture %}
meta(property="og:image", content="{{ node.picture.thumbnail('l', api=api) }}")
| {% endif %}
meta(property="og:description", content="{{ node.properties.content }}")
meta(property="og:type", content="article")
meta(property="article:type", content="{{node.user.full_name}}")
meta(property="article:published_time", content="{{node._created | pretty_date }}")
meta(property="og:see_also", content="https://cloud.blender.org/blog")
meta(property="og:url", content="https://cloud.blender.org{{ url_for_node(node=node) }}")
| {% endblock %}
| {% block tw %}
meta(name="twitter:card", content="summary_large_image")
| {% if project._id == config.MAIN_PROJECT_ID %}
meta(property="twitter:title", content="{{ node.name }} — Blender Cloud Blog")
| {% else %}
meta(property="twitter:title", content="{{ node.name }} - {{ project.name }} Blog — Blender Cloud")
| {% endif %}
| {% if project and project.properties.picture_header %}
meta(name="twitter:image", content="{{ project.properties.picture_header.thumbnail('l', api=api) }}")
| {% elif node and node.picture %}
meta(name="twitter:image", content="{{ node.picture.thumbnail('l', api=api) }}")
| {% else %}
meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_caminandes_3_02.jpg')}}")
| {% endif %}
meta(name="twitter:description", content="{{ node.properties.content }}")
| {% endblock %}
| {% block body %}
.container.box
#blog_container(class="{% if project._id == config.MAIN_PROJECT_ID %}cloud-blog{% endif %}")
#blog_post-container
| {% if project._id == config.MAIN_PROJECT_ID %}
a.btn.btn-default.button-back(href="{{ url_for('projects.view', project_url=project.url) }}blog")
| Back to Blog
| {% if node.has_method('PUT') %}
a.btn.btn-default.button-edit(href="{{url_for('nodes.posts_edit', post_id=node._id)}}")
i.pi-edit
| Edit Post
| {% endif %}
.clearfix
| {% endif %}
| {% if node.picture %}
.blog_index-header
img(src="{{ node.picture.thumbnail('l', api=api) }}")
| {% endif %}
.blog_index-item
.item-title
| {{node.name}}
.item-info.
<span title="{{node._created}}">{{node._created | pretty_date }}</span>
{% if node._created != node._updated %}
<span title="{{node._updated}}">(updated {{node._updated | pretty_date }})</span>
{% endif %}
{% if node.properties.category %}| {{node.properties.category}}{% endif %}
| by {{node.user.full_name}}
.item-content
| {{ node.properties.content }}
#comments-container
#comments-list-items-loading
i.pi-spin
| {% if project._id != config.MAIN_PROJECT_ID %}
#blog_index-sidebar
.blog_project-card
a.item-header(
href="{{ url_for('projects.view', project_url=project.url) }}")
.overlay
| {% if project.picture_header %}
img.background(src="{{ project.picture_header.thumbnail('m', api=api) }}")
| {% endif %}
a.card-thumbnail(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {% if project.picture_square %}
img.thumb(src="{{ project.picture_square.thumbnail('m', api=api) }}")
| {% endif %}
.item-info
a.item-title(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {{ project.name }}
| {% if project.summary %}
p.item-description
| {{project.summary|safe}}
| {% endif %}
.blog_project-sidebar
| {% if node.has_method('PUT') %}
a.btn.btn-default.button-create(href="{{url_for('nodes.posts_edit', post_id=node._id)}}")
| Edit Post
| {% endif %}
a.btn.btn-default.button-back(href="{{ url_for('projects.view', project_url=project.url) }}blog")
| Back to Blog
| {% endif %}
| {% endblock %}
| {% block footer_scripts %}
include ../_scripts
script hopToTop(); // Display jump to top button
| {% endblock %}

View File

@@ -0,0 +1,53 @@
| {% block body %}
#node-container
section.node-details-container.storage
.node-details-header
.node-title
| {{node.name}}
section.node-children.storage
| {% if node.children %}
| {% for child in node.children %}
a(href="#", data-node_id="{{ node._id }}" data-path="{{ child['path'] }}", title="{{ child['name'] }}", class="item_icon")
.list-node-children-item
.list-node-children-item-thumbnail
.list-node-children-item-thumbnail-icon
| {% if child['content_type'] == 'video' %}
i.pi-film
| {% elif child['content_type'] == 'image' %}
i.pi-image
| {% elif child['content_type'] == 'file' %}
i.pi-document
| {% elif child['content_type'] == 'binary' %}
i.pi-file-archive
| {% else %}
i.pi-folder
| {% endif %}
.list-node-children-item-name
span {{ child['name'] }}
| {% endfor %}
| {% endif %}
script.
$('a.item_icon').click(function(e){
// When clicking on a node preview, we load its content
e.preventDefault;
var nodeId = $(this).data('node_id');
var path = $(this).data('path');
displayStorage(nodeId, path);
// Update tree with current selection
//$('#project_tree').jstree('select_node', 'n_' + nodeId);
});
| {% endblock %}

View File

@@ -0,0 +1,33 @@
| {% block body %}
#node-container
section.node-details-container.storage
.node-details-header
.node-title
| {{node.name}}
//- .node-details-description
//- | {{node.description}}
.node-details-meta
ul.node-details-meta-list
li.node-details-meta-list-item.status
| {{node.status}}
li.node-details-meta-list-item.date(title="Created {{ node._created | pretty_date }}")
| {{ node._updated | pretty_date }}
li.node-details-meta-list-item.file.length
| {{ node.length | filesizeformat }}
li.node-details-meta-list-item.file.download
a(href="{% if node.has_method('GET') %}{{ node.download_link }}{% else %}{{ url_for('users.login') }}{% endif %}")
button.btn.btn-default(type="button")
| Download
| {% endblock %}

View File

@@ -0,0 +1,195 @@
| {% block body %}
#node-container.texture
#node-overlay
.texture-title#node-title
| {{node.name}}
| {% if node.properties.license_type %}
| {% if node.properties.license_notes %}
.texture-license(
id="asset-license",
data-toggle="popover",
data-placement="left",
data-trigger="hover",
data-content="{{ node.properties.license_notes }}",
title="{{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% else %}
.texture-license(
id="asset-license",
data-toggle="tooltip",
data-placement="bottom",
title="{{ node.properties.license_type }}")
i(class="pi-license-{{ node.properties.license_type }}")
| {% endif %}
| {% endif %}
section.node-row.texture-info
| {% if node.properties.files %}
span.texture-info-files
i.pi-texture
| {{ node.properties.files|length }} map{% if node.properties.files|length != 1 %}s{% endif %}
| {% endif %}
span.texture-info-seamless
i.pi-puzzle
| {% if not node.properties.is_tileable %}Not {% endif %}Seamless
| {% if node.properties.files %}
| {% for f in node.properties.files %}
section.node-row.texture-map
section.node-preview.texture
img.node-preview-thumbnail(
src="{{ f.file.thumbnail('m', api=api) }}",
data-preview="{{ f.file.thumbnail('l', api=api) }}",
data-aspect_ratio="{{ node.properties.aspect_ratio }}")
| {% if f.map_type == 'color' %}
| {% set map_type = 'Color Map' %}
| {% elif f.map_type == 'bump' %}
| {% set map_type = 'Bump Map' %}
| {% elif f.map_type == 'specular' %}
| {% set map_type = 'Specular Map' %}
| {% elif f.map_type == 'normal' %}
| {% set map_type = 'Normal Map' %}
| {% elif f.map_type == 'translucency' %}
| {% set map_type = 'Translucency' %}
| {% elif f.map_type == 'emission' %}
| {% set map_type = 'Emission' %}
| {% elif f.map_type == 'alpha' %}
| {% set map_type = 'Alpha' %}
| {% elif f.map_type == 'id' %}
| {% set map_type = 'ID Map' %}
| {% else %}
| {% set map_type = f.map_type %}
| {% endif %}
section.node-details-container.texture
.node-details-header
.node-title {{map_type}}
.node-details-attributes
span.sizes
span.x
| Width:
strong {{ f.file.width }}
span.y
| Height:
strong {{ f.file.height }}
span.length
| {{ f.file.length | filesizeformat }}
span.content_type
| {{ f.file.content_type }}
.node-details-meta
ul.node-details-meta-list
li.node-details-meta-list-item.texture.download
| {% if f.file.link %}
a(href="{{ f.file.link }}",,
title="Download texture",
download="{{ f.file.filename }}")
button.btn.btn-default(type="button")
i.pi-download
| Download
| {% else %}
button.btn.btn-default.disabled.sorry(type="button")
| Download
| {% endif %}
| {% endfor %}
| {% else %}
section.node-row
section.node-details-container.texture
.node-details-header.nofiles
.node-title No texture maps... yet!
| {% endif %}
include ../_scripts
| {% endblock %}
| {% block footer_scripts %}
script.
$('#asset-license').popover();
// Generate GA pageview
ga('send', 'pageview', location.pathname);
var str = $('.texture-title').text();
var to_replace = /_color|_bump|_specular|_normal|_translucency|_emission|_alpha|_tileable|.jpg|.png/g;
$('.texture-title').text(str.replace(to_replace,'').replace(/_/g,' '));
$('.node-preview-thumbnail').each(function(i){
$(this).closest('.node-preview').css({'height' : $(this).width() / $(this).data('aspect_ratio')});
var thumbnail = $(this);
var src = $(this).attr('src');
var src_xl = $(thumbnail).data('preview');
var src_xl_width, src_xl_height;
/* Make dummy img in memory otherwise we have css issues */
$("<img/>")
.attr('src', src_xl)
.load(function(){
src_xl_width = this.width;
src_xl_height = this.height;
});
$(this).on('click', function(e){
e.preventDefault();
});
$(this).hover(
function(){
var preview = $(this);
/* Replace image src with larger one */
if (src_xl_width > 350 || src_xl_height > 250) {
$(thumbnail).attr('src', src_xl);
$(preview).css({width: src_xl_width + 'px', height: src_xl_height + 'px'});
}
var parent = $(preview).parent();
var parentOffset = parent.offset();
if (src_xl_width > 600 || src_xl_height > 300) {
$(document).on('mousemove', function(e){
$(preview).css({
left: e.pageX - parentOffset.left - (src_xl_width / 2),
top: e.pageY - parentOffset.top - (src_xl_height / 2),
transform: 'initial',
cursor: 'grabbing',
});
});
};
},
function(){
$(document).off('mousemove');
$(this).attr('src', src);
$(this).css({left: '50%', top: "50%", width: '100%', height: 'auto', transform: 'translate(-50%, -50%)'});
}
);
});
$('.sorry').click(function() {
$.get('/403', function(data) {
$('#node-overlay').html(data).show().addClass('active');
})
});
$('#node-overlay').click(function(){
$(this).removeClass('active').hide().html();
});
| {% endblock %}

View File

@@ -0,0 +1,11 @@
| {% extends 'layout.html' %}
| {% block body %}
div.container
div.page-content
| {% include 'nodes/edit_embed.html' %}
| {% endblock %}
| {% block footer_scripts %}
| {% include '_macros/_file_uploader_javascript.html' %}
| {% endblock %}

View File

@@ -0,0 +1,347 @@
| {% from '_macros/_node_edit_form.html' import render_field %}
| {% block body %}
| {% with errors = errors %}
| {% if errors %}
| {% for field in errors %}
.alert.alert-danger(role="alert")
strong {{field}}
| {% for message in errors[field] %}
| {{message}}|
| {% endfor %}
| {% endfor %}
| {% endif %}
| {% endwith %}
| {% if error!="" %}
.alert.alert-danger(role="alert")
| {{error}}
| {% endif %}
#node-edit-container
form(
id="node-edit-form",
class="{{ node.node_type }}",
method="POST",
enctype="multipart/form-data",
action="{{url_for('nodes.edit', node_id=node._id)}}")
| {% for field in form %}
| {% if field.name == 'csrf_token' %}
| {{ field }}
| {% elif field.type == 'HiddenField' %}
| {{ field }}
| {% elif field.name == 'attachments' %}
#attachments-actions
.btn.btn-info#attachments-action-add
i.pi-plus
| Add New Attachment
| {{ render_field(field) }}
| {% elif field.name == 'files' %}
.files-header
#files-actions
#files-action-add
i.pi-plus
| Add New File
| {{ render_field(field) }}
| {% else %}
| {{ render_field(field) }}
| {% endif %}
| {% endfor %}
ul.project-edit-tools.bottom
li.button-cancel
a#item_cancel.item-cancel.project-mode-edit(
href="javascript:void(0);",
title="Cancel changes")
i.button-cancel-icon.pi-cancel
| Cancel
li.button-save
a#item_save.item-save.project-mode-edit(
href="javascript:void(0);",
title="Save changes")
i.button-save-icon.pi-check
| Save Changes
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.ui.widget.min.js') }}")
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.iframe-transport.min.js') }}")
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.fileupload.min.js') }}")
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.select2.min.js') }}")
script(src="{{ url_for('static_pillar', filename='assets/js/file_upload.min.js') }}")
script(type="text/javascript").
$(function () {
$('#tags').select2();
});
var convert = new Markdown.getSanitizingConverter();
Markdown.Extra.init(convert);
convert = convert.makeHtml;
/* Build the markdown preview when typing in textarea */
$(function() {
var $textarea = $('.form-group.description textarea'),
$loader = $('<div class="md-preview-loading"><i class="pi-spin spin"></i></div>').insertAfter($textarea),
$preview = $('<div class="node-edit-form-md-preview" />').insertAfter($loader);
$loader.hide();
// Delay function to not start converting heavy posts immediately
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
$textarea.keyup(function() {
/* If there's an iframe (YouTube embed), delay markdown convert 1.5s */
if (/iframe/i.test($textarea.val())) {
$loader.show();
delay(function(){
// Convert markdown
$preview.html(convert($textarea.val()));
$loader.hide();
}, 1500 );
} else {
// Convert markdown
$preview.html(convert($textarea.val()));
}
}).trigger('keyup');
});
$(function() {
$('input, textarea').keypress(function () {
// Unused: save status of the page as 'edited'
ProjectUtils.setProjectAttributes({isModified: true});
// Set the beforeunload to warn the user of unsaved changes
$(window).on('beforeunload', function () {
return 'You have unsaved changes in your asset.';
});
});
});
$("#item_save").unbind( "click" );
$("#item_cancel").unbind( "click" );
$(".file_delete").unbind( "click" );
/* Reset Save Changes button status */
$("li.button-save").removeClass('field-error saving');
$("li.button-save a#item_save").html('<i class="pi-check"></i> Save Changes');
/* Submit changes */
$("#node-edit-form").unbind( "submit" )
.submit(function(e) {
e.preventDefault();
/* Let us know started saving */
$("li.button-save").addClass('saving');
$("li.button-save a#item_save").html('<i class="pi-spin spin"></i> Saving...');
$.ajax({
url: "{{url_for('nodes.edit', node_id=node._id)}}",
data: $(this).serialize(),
type: 'POST'
})
.fail(function(data){
/* Something went wrong, print it */
if (data.status == 422) {
statusBarSet('error', 'The submitted data could not be validated.', 8000);
} else {
statusBarSet('error', 'Error! We\'ve been notified and are working on it - '
+ data.status + ' ' + data.statusText, 8000);
}
$("li.button-save").addClass('field-error');
$("li.button-save a#item_save").html('<i class="pi-warning"></i> Houston!');
/* Back to normal */
setTimeout(function(){
$("li.button-save").removeClass('saving field-error');
$("li.button-save a#item_save").html('<i class="pi-check"></i> Save Changes');
}, 8000);
})
.done(function(dataHtml){
/* Success! */
/* Load content*/
$('#project_context').html(dataHtml);
statusBarSet('success', 'Saved Successfully', 'pi-check');
/* Style button */
$("li.button-save").removeClass('saving field-error');
$("li.button-save a#item_save").html('<i class="pi-check"></i> Save Changes');
// XXX TODO - Keeps displaying 'loading', needs further investigation
//- $('#project_tree').jstree("refresh");
updateUi(ProjectUtils.nodeId(), 'view');
});
});
$('#item_save, .item-save').click(function(e){
e.preventDefault();
// Assets always need a file
if ($('.form-group.file #file').val() == ''){
$('.form-group.file').addClass('error');
statusBarSet('error', 'No File Selected', 'pi-warning', 5000);
} else {
$('.form-group.file').removeClass('error');
$("#node-edit-form").submit();
// Disable beforeunolad when submitting a form
$(window).off('beforeunload');
}
});
$('#item_cancel, .item-cancel').click(function(e){
displayNode('{{node._id}}');
});
var attrs = ['for', 'id', 'name', 'data-field-name'];
function resetAttributeNames(section) {
var tags = section.find('input, select, label, div, a');
var idx = section.index();
tags.each(function () {
var $this = $(this);
// Renumber certain attributes.
$.each(attrs, function (i, attr) {
var attr_val = $this.attr(attr);
if (attr_val) {
$this.attr(attr, attr_val.replace(/-\d+/, '-' + idx))
}
});
// Clear input field values
var tagname = $this.prop('tagName');
if (tagname == 'INPUT') {
if ($this.attr('type') == 'checkbox') {
$this.prop('checked', false);
} else {
$this.val('');
}
} else if (tagname == 'SELECT') {
$this.find(':nth-child(1)').prop('selected', true);
}
});
// Click on all file delete buttons to clear all file widgets.
section.find('a.file_delete').click();
section.find('div.form-upload-progress-bar').hide();
}
var initUploadFields = function(selector_string) {
// console.log($(selector_string));
$(selector_string).fileupload({
dataType: 'json',
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
replaceFileInput: false,
dropZone: $(this),
formData: {},
progressall: function (e, data) {
// Update progressbar during upload
var progress = parseInt(data.loaded / data.total * 100, 10);
$(this).next().find('.form-upload-progress-bar').css(
{'width': progress + '%', 'display': 'block'}
).removeClass('progress-error').addClass('progress-active');
fieldUpload = $(this);
},
done: function (e, data) {
// Get the first file upload result (we only need one)
var fileData = data.result.files[0];
// Create a file object on the server and retrieve its id
statusBarSet('info', 'Uploading File...', 'pi-upload-cloud');
$('.button-save').addClass('disabled');
$('li.button-save a#item_save').html('<i class="pi-spin spin"></i> Uploading Preview...');
var payload = {
name: fileData.name,
size: fileData.size,
type: fileData.type,
field_name: $(this).data('field-name'),
project_id: ProjectUtils.projectId()
}
$.post("/files/create", payload)
.done(function (data) {
if (data.status === 'success') {
// If successful, add id to the picture hidden field
var field_name = '#' + data.data.field_name;
if ($(field_name).val()) {
$('.node-preview-thumbnail').hide();
deleteFile($(field_name), data.data.id);
} else {
$(field_name).val(data.data.id);
}
var previewThumbnail = fieldUpload.prev().prev();
$(previewThumbnail).attr('src', data.data.link);
$('.node-preview-thumbnail').show();
statusBarSet('success', 'File Uploaded Successfully', 'pi-check');
$('.button-save').removeClass('disabled');
$('li.button-save a#item_save').html('<i class="pi-check"></i> Save Changes');
$('.progress-active').removeClass('progress-active progress-error');
}
})
.fail(function(data) {
$('.button-save').removeClass('disabled');
$('li.button-save a#item_save').html('<i class="pi-check"></i> Save Changes');
statusBarSet(data.textStatus, 'Upload error: ' + data.errorThrown, 'pi-attention', 8000);
});
},
fail: function (e, data) {
$('.button-save').removeClass('disabled');
$('li.button-save a#item_save').html('<i class="pi-check"></i> Save Changes');
statusBarSet(data.textStatus, 'Upload error: ' + data.errorThrown, 'pi-attention', 8000);
$('.progress-active').addClass('progress-error').removeClass('progress-active');
}
});
}
if (document.getElementById("attachments") !== null) {
$("#attachments-action-add").on('click', function(){
var lastRepeatingGroup = $('#attachments > li').last();
var cloned = lastRepeatingGroup.clone(true);
cloned.insertAfter(lastRepeatingGroup);
resetAttributeNames(cloned);
});
}
if (document.getElementById("files") !== null) {
$("#files-action-add").on('click', function () {
var lastRepeatingGroup = $('#files > li').last();
var cloned = lastRepeatingGroup.clone(false);
cloned.insertAfter(lastRepeatingGroup);
resetAttributeNames(cloned);
cloned.find('.fileupload').each(setup_file_uploader)
});
}
//- console.log($._data($(elementSelector)[0], "events"));
| {% endblock %}

View File

@@ -0,0 +1,301 @@
| {% extends 'layout.html' %}
| {% block page_title %}Search{% if project %} {{ project.name }}{% endif %}{% endblock %}
| {% block og %}
meta(property="og:type", content="website")
| {% if og_picture %}
meta(property="og:image", content="{{ og_picture.thumbnail('l', api=api) }}")
| {% endif %}
| {% if project %}
meta(property="og:title", content="{{project.name}} - Blender Cloud")
meta(property="og:url", content="{{url_for('projects.view', project_url=project.url, _external=True)}}")
meta(property="og:description", content="{{project.summary}}")
| {% endif %}
| {% endblock %}
| {% block tw %}
| {% if og_picture %}
meta(property="twitter:image", content="{{ og_picture.thumbnail('l', api=api) }}")
| {% endif %}
| {% if project %}
meta(name="twitter:title", content="{{project.name}} on Blender Cloud")
meta(name="twitter:description", content="{{project.summary}}")
| {% endif %}
| {% endblock %}
| {% block body %}
#search-container
| {% if project %}
#project_sidebar
ul.project-tabs
li.tabs-thumbnail(
title="About",
data-toggle="tooltip",
data-placement="left",
class="{% if title == 'about' %}active {% endif %}{% if project.picture_square %}image{% endif %}")
a(href="{{url_for('projects.about', project_url=project.url, _external=True)}}")
#project-loading
i.pi-spin
| {% if project.picture_square %}
img(src="{{ project.picture_square.thumbnail('b', api=api) }}")
| {% else %}
i.pi-home
| {% endif %}
li.tabs-browse(
title="Browse",
data-toggle="tooltip",
data-placement="left")
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
i.pi-tree-flow
li.tabs-search.active(
title="Search",
data-toggle="tooltip",
data-placement="left")
a(href="{{url_for('projects.search', project_url=project.url, _external=True)}}")
i.pi-search
| {% endif %}
#search-sidebar
input.search-field(
type="text",
name="q",
id="q",
autocomplete="off",
spellcheck="false",
autocorrect="false",
placeholder="Search by Title, Type...")
.search-list-filters
.filter-list
| View as:
ul.filter-list
li.filter-list-type.grid(
title="Browse as grid",
data-list-type="grid")
i.pi-layout
li.filter-list-type.list(
title="Browse as list",
data-list-type="list")
i.pi-list
#accordion.panel-group.accordion(role="tablist", aria-multiselectable="true")
#facets
#pagination
.search-list-stats
#stats
#search-list
#hits
#search-details
#search-error
#search-hit-container
| {% raw %}
// Facet template
script(type="text/template", id="facet-template")
.panel.panel-default
a(data-toggle='collapse', data-parent='#accordion', href='#filter_{{ facet }}', aria-expanded='true', aria-controls='filter_{{ facet }}')
.panel-heading(role='tab')
.panel-title {{ title }}
.panel-collapse.collapse.in(id='filter_{{ facet }}', role='tabpanel', aria-labelledby='headingOne')
.panel-body
| {{#values}}
a.facet_link.toggleRefine(
class='{{#refined}}refined{{/refined}}',
data-facet='{{ facet }}',
data-value='{{ value }}',
href='#')
span
| {{ label }}
small.facet_count.pull-right {{ count }}
| {{/values}}
// Hit template
script(type="text/template", id="hit-template")
.search-hit(data-hit-id='{{ objectID }}')
#search-loading.search-loading
.spinner
span.spin ·
.search-hit-thumbnail
| {{#picture}}
img(src="{{{ picture }}}")
| {{/picture}}
| {{^picture}}
.search-hit-thumbnail-icon
| {{#media}}
i(class="pi-{{{ media }}}")
| {{/media}}
| {{^media}}
i.dark(class="pi-{{{ node_type }}}")
| {{/media}}
| {{/picture}}
| {{#is_free}}
.search-hit-ribbon
span free
| {{/is_free}}
.search-hit-name
| {{{ _highlightResult.name.value }}}
.search-hit-meta
span.project {{{ project.name }}} ·
span.node_type {{{ node_type }}}
| {{#media}}
span.media · {{{ media }}}
| {{/media}}
span.when {{{ created }}}
span.context
a(href="/nodes/{{ objectID }}/redir") view in context
// Pagination template
script(type="text/template", id="pagination-template")
ul.search-pagination.
<li {{^prev_page}}class="disabled"{{/prev_page}}><a href="#" {{#prev_page}} class="gotoPage" data-page="{{ prev_page }}" {{/prev_page}}><i class="pi-angle-left"></i></a></li>
{{#pages}}
<li class="{{#current}}active{{/current}}{{#disabled}}disabled{{/disabled}}"><a href="#" {{^disabled}} class="gotoPage" data-page="{{ number }}" {{/disabled}}>{{ number }}</a></li>
{{/pages}}
<li {{^next_page}}class="disabled"{{/next_page}}><a href="#" {{#next_page}} class="gotoPage" data-page="{{ next_page }}" {{/next_page}}><i class="pi-angle-right"></i></a></li>
// Stats template
script(type="text/template", id="stats-template")
span {{ nbHits }} result{{#nbHits_plural}}s{{/nbHits_plural}}
small ({{ processingTimeMS }}ms)
| {% endraw %}
| {% endblock %}
| {% block footer_scripts %}
script(src="//releases.flowplayer.org/6.0.5/flowplayer.min.js", async)
script().
var APPLICATION_ID = '{{config.ALGOLIA_USER}}';
var SEARCH_ONLY_API_KEY = '{{config.ALGOLIA_PUBLIC_KEY}}';
var INDEX_NAME = '{{config.ALGOLIA_INDEX_NODES}}';
var sortByCountDesc = null;
var FACET_CONFIG = [
{ name: 'node_type', title: 'Type', disjunctive: false, sortFunction: sortByCountDesc },
{ name: 'media', title: 'Media', disjunctive: false, sortFunction: sortByCountDesc },
{ name: 'tags', title: 'Tags', disjunctive: false, sortFunction: sortByCountDesc },
{ name: 'is_free', title: 'Free Access', disjunctive: false, sortFunction: sortByCountDesc },
];
{% if project %}
FACET_CONFIG.push({ name: 'project._id', title: 'Project', disjunctive: false, hidden: true, value: '{{project._id}}' })
{% endif %}
script(src="//cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js")
script(src="//cdn.jsdelivr.net/hogan.js/3.0.0/hogan.common.js")
script(src="{{ url_for('static_pillar', filename='assets/js/algolia_search.min.js') }}")
script(type="text/javascript").
function displayUser(userId) {
var url = '/nodes/' + userId + '/view';
$.get(url, function(dataHtml){
$('#search-hit-container').html(dataHtml);
})
.done(function(){
$('.search-loading').removeClass('active');
$('#search-error').hide();
$('#search-hit-container').show();
})
.fail(function(data){
$('.search-loading').removeClass('active');
$('#search-hit-container').hide();
$('#search-error').show().html('Houston!\n\n' + data.status + ' ' + data.statusText);
});
}
$('body').on('click', '.search-hit', function(){
if ($('.search-loading').hasClass('active')){
$(this).removeClass('active');
}
$(this).find('#search-loading').addClass('active');
displayUser($(this).data('hit-id'));
$('.search-hit').removeClass('active');
$(this).addClass('active');
});
// Remove focus from search input so that the click event bound to .search-hit
// can be fired on the first click.
$(searchList).hover(function(){
$('#q').blur();
});
$('#search-sidebar').hover(function(){
$('#q').focus();
});
/* UI Stuff */
/* List types, grid or list (default)*/
var uiListType = Cookies.getJSON('bcloud_ui');
var searchList = document.getElementById('search-list');
function uiSetListType(type){
$('.filter-list-type').removeClass('active');
if (type == 'grid'){
$(searchList).addClass('view-grid');
$('.filter-list-type.grid').addClass('active');
} else {
$(searchList).removeClass('view-grid');
$('.filter-list-type.list').addClass('active');
}
}
if (uiListType && uiListType.search_browse_type == 'grid'){
uiSetListType('grid');
} else {
uiSetListType('list');
}
$('.filter-list-type').on('click', function(){
if ($(this).attr('data-list-type') == 'grid'){
uiSetListType('grid');
setJSONCookie('bcloud_ui', 'search_browse_type', 'grid');
} else {
uiSetListType('list');
setJSONCookie('bcloud_ui', 'search_browse_type', 'list');
}
});
/* Scrollbars */
if (typeof Ps !== 'undefined'){
Ps.initialize(searchList, {suppressScrollX: true});
Ps.initialize(document.getElementById('search-hit-container'), {suppressScrollX: true});
}
/* Hide site-wide search, kinda confusing */
$('.search-input').hide();
/* Resize container so we can have custom scrollbars */
container_offset = $('#search-container').offset();
function containerResizeY(window_height){
var container_height = window_height - container_offset.top;
if (container_height > parseInt($('#search-container').css("min-height"))) {
$('#search-container').css(
{'max-height': container_height + 'px', 'height': container_height + 'px'}
);
$('#search-list, #search-hit-container').css(
{'max-height': container_height + 'px', 'height': container_height + 'px'}
);
};
};
$(window).on("load resize",function(){
containerResizeY($(window).height());
});
| {% endblock %}
| {% block footer_navigation %}{% endblock %}
| {% block footer %}{% endblock %}

View File

@@ -0,0 +1,22 @@
script(type="text/javascript").
/* Convert Markdown */
var convert = new Markdown.getSanitizingConverter().makeHtml;
var convert_fields = '.node-details-description, .blog_index-item .item-content';
/* Parse description/content fields to convert markdown */
$(convert_fields).each(function(i){
$(convert_fields).eq(i).html(convert($(convert_fields).eq(i).text()));
});
ProjectUtils.setProjectAttributes({isProject: true, nodeId: '', parentNodeId: ''});
var movingMode = Cookies.getJSON('bcloud_moving_node');
if (movingMode){
$('#item_move_accept').removeClass('disabled').html('<i class="pi-check"></i> Move to Root');
if (movingMode.node_type === 'texture'){
$('#item_move_accept').addClass('disabled').html('Select a Texture Folder');
}
};

View File

@@ -0,0 +1,250 @@
| {% extends 'layout.html' %}
| {% set title = 'edit' %}
| {% block page_title %}Edit {{ project.name }}{% endblock %}
| {% block body %}
#project-container
#project-side-container
#project_sidebar
ul.project-tabs
li.tabs-thumbnail(
title="About",
data-toggle="tooltip",
data-placement="left",
class="{% if title == 'about' %}active {% endif %}{% if project.picture_square %}image{% endif %}")
a(href="{{url_for('projects.about', project_url=project.url, _external=True)}}")
#project-loading
i.pi-spin
| {% if project.picture_square %}
img(src="{{ project.picture_square.thumbnail('b', api=api) }}")
| {% else %}
i.pi-home
| {% endif %}
li.tabs-browse(
title="Browse",
data-toggle="tooltip",
data-placement="left")
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
i.pi-tree-flow
| {% if not project.is_private %}
li.tabs-search(
title="Search",
data-toggle="tooltip",
data-placement="left")
a(href="{{url_for('projects.search', project_url=project.url, _external=True)}}")
i.pi-search
| {% endif %}
.project_nav-toggle-btn(
title="Expand Navigation [T]",
data-toggle="tooltip",
data-placement="right")
i.pi-angle-double-left
#project_nav
#project_nav-container
#project_nav-header
.project-title
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
| {{ project.name }}
// TODO - make list a macro
#project_tree
ul.project_nav-edit-list
li(class="{% if title == 'edit' %}active{% endif %}")
a(href="{{ url_for('projects.edit', project_url=project.url) }}")
i.pi-list
| Overview
li(class="{% if title == 'sharing' %}active{% endif %}")
a(href="{{ url_for('projects.sharing', project_url=project.url) }}")
i.pi-share
| Sharing
li(class="{% if title == 'edit_node_types' %}active{% endif %}")
a(href="{{ url_for('projects.edit_node_types', project_url=project.url) }}")
i.pi-puzzle
| Node Types
.project_split(title="Toggle Navigation [T]")
#project_context-container
#project_context-header
span#project-statusbar
span#project-edit-title
| Edit Project
ul.project-edit-tools
// Edit Mode
li.button-cancel
a#item_cancel.project-mode-edit(
href="{{url_for('projects.view', project_url=project.url, _external=True)}}",
title="Cancel changes")
i.button-cancel-icon.pi-back
| Go to Project
li.button-save
a#item_save.project-mode-edit(
href="#",
title="Save changes")
i.button-save-icon.pi-check
| Save Changes
#project_context
#node-edit-container
form(
id="node-edit-form"
method='POST',
action="{{url_for('projects.edit', project_url=project.url)}}")
| {% with errors = errors %}
| {% if errors %}
| {% for field in errors %}
.alert.alert-danger(role='alert')
strong {{field}}
| {% for message in errors[field] %}
| {{message}}|
| {% endfor %}
| {% endfor %}
| {% endif %}
| {% endwith %}
| {% for field in form %}
| {% if field.name == 'csrf_token' %}
| {{ field }}
| {% else %}
| {% if field.type == 'HiddenField' %}
| {{ field }}
| {% else %}
| {% if field.name not in hidden_fields %}
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
| {{ field.label }}
| {% if field.name == 'picture' %}
| {% if post.picture %}
img.node-preview-thumbnail(src="{{ post.picture.thumbnail('m', api=api) }}")
a(href="#", class="file_delete", data-field-name="picture", data-file_id="{{post.picture._id}}") Delete
| {% endif %}
| {% endif %}
| {{ field(class='form-control') }}
| {% if field.errors %}
ul.error
| {% for error in field.errors %}
li {{ error }}
| {% endfor %}
| {% endif %}
| {% else %}
| {{ field(class='hidden') }}
| {% endif %}
| {% endif %}
| {% endif %}
| {% endfor %}
ul.project-edit-tools.bottom
li.button-cancel
a#item_cancel.project-mode-edit(
href="{{url_for('projects.view', project_url=project.url, _external=True)}}",
title="Cancel changes")
i.button-cancel-icon.pi-back
| Go to Project
li.button-save
a#item_save.project-mode-edit(
href="#",
title="Save changes")
i.button-save-icon.pi-check
| Save Changes
| {% endblock %}
| {% block footer_scripts %}
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.ui.widget.min.js') }}")
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.iframe-transport.min.js') }}")
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.fileupload.min.js') }}")
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/file_upload.min.js') }}")
script(type="text/javascript").
/* UI Stuff */
$(window).on("load resize",function(){
containerResizeY($(window).height());
});
/* Initialize scrollbars */
if ((typeof Ps !== 'undefined') && window.innerWidth > 768){
Ps.initialize(document.getElementById('project_tree'), {suppressScrollX: true});
}
$('.project-mode-edit').show();
ProjectUtils.setProjectAttributes({projectId: "{{project._id}}", isProject: true, nodeId: ''});
var convert = new Markdown.getSanitizingConverter().makeHtml;
$('.button-save').on('click', function(e){
e.preventDefault();
// Disable beforeunolad when submitting a form
$(window).off('beforeunload');
$(this).children('a').html('<i class="pi-spin spin"></i> Saving');
$('#node-edit-form').submit();
});
/* Build the markdown preview when typing in textarea */
$(function() {
var $textarea = $('.form-group.description textarea'),
$loader = $('<div class="md-preview-loading"><i class="pi-spin spin"></i></div>').insertAfter($textarea),
$preview = $('<div class="node-edit-form-md-preview" />').insertAfter($loader);
$loader.hide();
// Delay function to not start converting heavy posts immediately
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
$textarea.keyup(function() {
/* If there's an iframe (YouTube embed), delay markdown convert 1.5s */
if (/iframe/i.test($textarea.val())) {
$loader.show();
delay(function(){
// Convert markdown
$preview.html(convert($textarea.val()));
$loader.hide();
}, 1500 );
} else {
// Convert markdown
$preview.html(convert($textarea.val()));
};
}).trigger('keyup');
$('input, textarea').keypress(function () {
// Unused: save status of the page as 'edited'
ProjectUtils.setProjectAttributes({isModified: true});
// Set the beforeunload to warn the user of unsaved changes
$(window).on('beforeunload', function () {
return 'You have unsaved changes in your project.';
});
});
});
| {% endblock %}
| {% block footer_navigation %}
| {% endblock %}
| {% block footer %}
| {% endblock %}

View File

@@ -0,0 +1,88 @@
| {% extends 'layout.html' %}
| {% set title = 'edit_node_types' %}
| {% block page_title %}Project {{ project.name }}{% endblock %}
| {% block body %}
.container.box
form(
method='POST',
action="{{url_for('projects.edit_node_type', project_url=project.url, node_type_name=node_type['name'])}}")
#blog_container.post-create
| {% with errors = errors %}
| {% if errors %}
| {% for field in errors %}
.alert.alert-danger(role='alert')
strong {{field}}
| {% for message in errors[field] %}
| {{message}}|
| {% endfor %}
| {% endfor %}
| {% endif %}
| {% endwith %}
#blog_index-sidebar
.blog_project-sidebar
input.btn.btn-default.button-create(type='submit', value="Update {{ node_type['name'] }}")
a.btn.btn-default.button-back(href="{{ url_for('projects.view', project_url=project.url) }}")
| Back to Project
#blog_post-edit-container
#blog_post-edit-title
| Edit {{ node_type['name'] }}
#blog_post-edit-form
| {% for field in form %}
| {% if field.name == 'csrf_token' %}
| {{ field }}
| {% else %}
| {% if field.type == 'HiddenField' %}
| {{ field }}
| {% else %}
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
| {{ field.label }}
| {{ field(class='form-control') }}
| {% if field.errors %}
ul.error
| {% for error in field.errors %}
li {{ error }}
| {% endfor %}
| {% endif %}
| {% endif %}
| {% endif %}
| {% endfor %}
| {% endblock %}
| {% block footer_scripts%}
script(src="https://cdn.jsdelivr.net/g/ace@1.2.3(noconflict/ace.js+noconflict/mode-json.js)")
script.
var dynSchemaEditorContainer = $("<div>", {id: "dyn_schema_editor"});
$(".form-group.dyn_schema").before(dynSchemaEditorContainer);
var dynSchemaEditor = ace.edit("dyn_schema_editor");
dynSchemaEditor.getSession().setValue($("#dyn_schema").val());
var formSchemaEditorContainer = $("<div>", {id: "form_schema_editor"});
$(".form-group.form_schema").before(formSchemaEditorContainer);
var formSchemaEditor = ace.edit("form_schema_editor");
formSchemaEditor.getSession().setValue($("#form_schema").val());
var permissionsEditorContainer = $("<div>", {id: "permissions_editor"});
$(".form-group.permissions").before(permissionsEditorContainer);
var permissionsEditor = ace.edit("permissions_editor");
permissionsEditor.getSession().setValue($("#permissions").val());
$("form").submit(function(e) {
$("#dyn_schema").val(dynSchemaEditor.getSession().getValue());
$("#form_schema").val(formSchemaEditor.getSession().getValue());
$("#permissions").val(permissionsEditor.getSession().getValue());
});
| {% endblock %}

View File

@@ -0,0 +1,110 @@
| {% extends 'layout.html' %}
| {% set title = 'edit_node_types' %}
| {% block page_title %}Node Types: {{ project.name }}{% endblock %}
| {% block body %}
#project-container
#project-side-container
#project_sidebar
ul.project-tabs
li.tabs-thumbnail(
title="About",
data-toggle="tooltip",
data-placement="left",
class="{% if title == 'about' %}active {% endif %}{% if project.picture_square %}image{% endif %}")
a(href="{{url_for('projects.about', project_url=project.url, _external=True)}}")
#project-loading
i.pi-spin
| {% if project.picture_square %}
img(src="{{ project.picture_square.thumbnail('b', api=api) }}")
| {% else %}
i.pi-home
| {% endif %}
li.tabs-browse(
title="Browse",
data-toggle="tooltip",
data-placement="left")
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
i.pi-tree-flow
| {% if not project.is_private %}
li.tabs-search(
title="Search",
data-toggle="tooltip",
data-placement="left")
a(href="{{url_for('projects.search', project_url=project.url, _external=True)}}")
i.pi-search
| {% endif %}
.project_nav-toggle-btn(
title="Expand Navigation [T]",
data-toggle="tooltip",
data-placement="right")
i.pi-angle-double-left
#project_nav
#project_nav-container
#project_nav-header
.project-title
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
| {{ project.name }}
// TODO - make list a macro
#project_tree
ul.project_nav-edit-list
li(class="{% if title == 'edit' %}active{% endif %}")
a(href="{{ url_for('projects.edit', project_url=project.url) }}")
i.pi-list
| Overview
li(class="{% if title == 'sharing' %}active{% endif %}")
a(href="{{ url_for('projects.sharing', project_url=project.url) }}")
i.pi-share
| Sharing
li(class="{% if title == 'edit_node_types' %}active{% endif %}")
a(href="{{ url_for('projects.edit_node_types', project_url=project.url) }}")
i.pi-puzzle
| Node Types
.project_split(title="Toggle Navigation [T]")
#project_context-container
#project_context-header
span#project-statusbar
span#project-edit-title
| Edit Project
#project_context
#node-edit-container
div(id="node-edit-form")
h3 Node Types (coming soon)
p.
Nodes are all the items that can be found in a project.
Everything is a node: a file, a folder, a comment. They are
defined with custom properties and properly presented to you.
When we add support for new node types in the future, it means we
allow the creation of new items (such as textures).
| {% if current_user.has_role('admin') %}
ul
| {% for node_type in project.node_types %}
li
a(href="{{ url_for('projects.edit_node_type', project_url=project.url, node_type_name=node_type.name) }}")
| {{node_type.name}}
| {% endfor %}
| {% endif %}
| {% endblock %}
| {% block footer_scripts %}
script(type="text/javascript").
$(window).on("load resize",function(){
containerResizeY($(window).height());
});
| {% endblock %}
| {% block footer_navigation %}
| {% endblock %}
| {% block footer %}
| {% endblock %}

View File

@@ -0,0 +1,139 @@
| {% extends 'projects/home_layout.html' %}
| {% set subtab = 'images' %}
| {% set learn_more_btn_url = '/blog/introducing-image-sharing' %}
| {% block currenttab %}
section.nav-tabs__tab.active#tab-images
.tab_header-container
| {% if not shared_images %}
.tab_header-intro(
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/pattern_01.jpg')}})")
.tab_header-intro_text
h2 Share what you see.
p.
Got a nice render, a Blender oddity, or a cool screenshot?
<br/>
Share it instantly from within Blender to the world!
.tab_header-intro_icons
i.pi-blender
i.pi-heart-filled
i.pi-picture-album
| {% endif %}
| {% if shared_images %}
div#home-images__list
| {% for node in shared_images %}
div.home-images__list-item
.home-images__list-details
a.title(href="{{ url_for_node(node=node) }}?t")
| {{ node.name }}
| {% if node.picture %}
a.home-images__list-thumbnail(
href="{{ url_for_node(node=node) }}?t")
img(src="{{ node.picture.thumbnail('l', api=api) }}")
| {% endif %}
.home-images__list-details
ul.meta
li.when(title="{{ node._created }}") {{ node._created | pretty_date_time }}
li.delete-image
a.delete-prompt(href='javascript:void(0);')
| Delete
span.delete-confirm
| Are you sure?
a.delete-confirm(href='javascript:void(0);',
data-image-id="{{ node._id }}")
i.pi-check
| Yes, delete
a.delete-cancel(href='javascript:void(0);')
i.pi-cancel
| No, cancel
| {% if node.short_link %}
li
a(href="{{ node.short_link }}") {{ node.short_link }}
| {% endif %}
| {% endfor %}
| {% else %}
.blender_sync-main.empty
.blender_sync-main-header
span.blender_sync-main-title
| Share some images using the
a(
href="https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip")
| Blender Cloud add-on.
| {% endif %}
| {% endblock %}
| {% block side_announcement %}
.title
a(href="https://cloud.blender.org/blog/introducing-image-sharing") Image Sharing
.lead
p.
Share your renders, painted textures, and other images, straight from Blender
to the cloud.
hr
| {% if show_addon_download_buttons %}
p.
Image Sharing requires a Blender Cloud subscription, which you have!
| {% else %}
p.
Image Sharing requires a Blender Cloud subscription.
.buttons
a.btn.btn-default.btn-outline.green(href="https://store.blender.org/product/membership/")
| Join Now
| {% endif %}
| {% endblock %}
| {% block footer_scripts %}
| {{ super() }}
script.
var urlNodeDelete = "{{url_for('projects.delete_node')}}";
$(document).ready(function() {
// 'Delete' link on images
var $home_image_list = $('#home-images__list');
$home_image_list.find('a.delete-prompt').on('click', function(e){
$(this)
.hide()
.next().show();
});
// 'Cancel delete' link on images
$home_image_list.find('a.delete-cancel').on('click', function(e){
$(this).parent()
.hide()
.prev().show();
});
// 'Confirm delete' link on images
$home_image_list.find('a.delete-confirm').on('click', function (e) {
var image_id = this.dataset.imageId;
var $this = $(this);
var parent = $this.closest('.home-images__list-item');
console.log('My parent is', parent);
var error_elt = $this.parent();
$.ajax({
type: 'POST',
url: urlNodeDelete,
data: {node_id: image_id},
success: function () {
if (parent.siblings().length == 0) {
// This was the last shared image. Reload the page,
// so that we can show the correct "no images shared"
// content with Jinja2.
window.location = window.location;
}
parent.hide('slow', function() { parent.remove(); });
},
error: function (jqxhr, textStatus, errorThrown) {
error_elt.text('Unable to delete image; ' + textStatus + ': ' + errorThrown);
}
});
});
hopToTop(); // Display jump to top button
});
| {% endblock %}

View File

@@ -0,0 +1,43 @@
| {% extends 'projects/home_layout.html' %}
| {% set subtab = 'blender_sync' %}
| {% set learn_more_btn_url = '/blog/introducing-blender-sync' %}
| {% block currenttab %}
section.nav-tabs__tab.active#tab-blender_sync
.tab_header-container
.tab_header-intro(
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/pattern_01.jpg')}})")
.tab_header-intro_text
h2 Connect Blender with the Cloud
p
| Save your Blender preferences once, load them anywhere.
<br/>
| Use the
=' '
a(href='https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip') Blender Cloud add-on
=' '
| to synchronise your settings from within Blender.
.tab_header-intro_icons
i.pi-blender
i.pi-heart-filled
i.pi-blender-cloud
| {% for version in synced_versions %}
.blender_sync-main
.blender_sync-main-header
h2.blender_sync-main-title
i.pi-blender
| Blender {{ version.version }}
.blender_sync-main-last
| Last synced on: {{ version.date|pretty_date }}
| {% else %}
.blender_sync-main.empty
.blender_sync-main-header
span.blender_sync-main-title
| No settings synced yet
<hr/>
a.download(
href='https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip')
| Download add-on
| {% endfor %}
| {% endblock %}

View File

@@ -0,0 +1,79 @@
| {% extends 'layout.html' %}
| {% from '_macros/_navigation.html' import navigation_tabs %}
| {% set title = 'home' %}
| {% block og %}
meta(property="og:title", content="Blender Cloud - Home")
meta(property="og:url", content="https://cloud.blender.org{{ request.path }}")
meta(property="og:type", content="website")
| {% endblock %}
| {% block tw %}
meta(name="twitter:card", content="summary_large_image")
meta(name="twitter:site", content="@Blender_Cloud")
meta(name="twitter:title", content="Blender Cloud")
meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/backgrounds/cloud_services_oti.jpg')}}")
| {% endblock %}
| {% block page_title %}
| {{current_user.full_name}}
| {% endblock %}
| {% block body %}
.dashboard-container
section#main
| {{ navigation_tabs(title) }}
section#projects
section#sub-nav-tabs.home
ul#sub-nav-tabs__list
li.nav-tabs__list-tab#subtab-blender_sync(data-tab-url='.')
i.pi-blender
| Blender Sync
li.nav-tabs__list-tab#subtab-images(data-tab-url='images')
i.pi-picture
| Images
| {% block currenttab %}{% endblock %}
section#side
section#announcement
img.header(
src="{{ url_for('static', filename='assets/img/blender_sync_header.jpg') }}")
.text
| {% block side_announcement %}
.title
a(href="https://cloud.blender.org/blog/introducing-blender-sync") Blender Sync
.lead
span.
Save your settings once. Use them anywhere.
Carry your Blender configuration with you, use our free add-on to sync your keymaps and preferences.
<hr/>
Syncing is free for everyone. No subscription required.
| {% endblock %}
| {% if show_addon_download_buttons %}
.buttons
a.btn.btn-default.btn-outline.orange(
href="https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip")
i.pi-download
| Download <small>v</small>{{ config.BLENDER_CLOUD_ADDON_VERSION }}
a.btn.btn-default.btn-outline.blue(
href="{{ learn_more_btn_url }}")
| Learn More
| {% endif %}
| {% endblock %}
| {% block footer_scripts %}
script.
$(document).ready(function () {
$('#subtab-{{ subtab }}').addClass('active');
var $nav_tabs = $('#sub-nav-tabs__list').find('li.nav-tabs__list-tab');
$nav_tabs.on('click', function (e) {
window.location = $(this).attr('data-tab-url');
});
});
| {% endblock %}

View File

@@ -0,0 +1,87 @@
| {% extends 'layout.html' %}
| {% block og %}
meta(property="og:title", content="{% if title == 'open-projects' %}Open Projects{% elif title == 'training' %}Training{% endif %}")
// XXX - Replace with actual url
meta(property="og:url", content="https://cloud.blender.org")
meta(property="og:type", content="website")
| {% endblock %}
| {% block tw %}
meta(name="twitter:card", content="summary_large_image")
meta(name="twitter:site", content="@Blender_Cloud")
meta(name="twitter:title", content="{% if title == 'open-projects' %}Open Projects{% elif title == 'training' %}Training{% endif %} on Blender Cloud")
meta(name="twitter:description", content="{% if title == 'open-projects' %}Full production data and tutorials from all open movies, for you to use freely{% elif title == 'training' %}Production quality training by 3D professionals{% endif %}")
meta(name="twitter:image", content="{% if title == 'training' %}{{ url_for('static', filename='assets/img/backgrounds/background_caminandes_3_03.jpg')}}{% else %}{{ url_for('static', filename='assets/img/backgrounds/background_agent327_01.jpg')}}{% endif %}")
| {% endblock %}
| {% block page_title %}
| {% if title == 'open-projects' %}Open Projects{% elif title == 'training' %}Training{% else %}Projects{% endif %}
| {% endblock %}
| {% block body %}
#project-container
#node_index-container
#node_index-header.collection
img.background-header(src="{% if title == 'training' %}{{ url_for('static', filename='assets/img/backgrounds/background_caminandes_3_03.jpg')}}{% else %}{{ url_for('static', filename='assets/img/backgrounds/background_agent327_01.jpg')}}{% endif %}")
#node_index-collection-info
| {% if title == 'open-projects' %}
.node_index-collection-name
span Open Projects
.node_index-collection-description
span.
The iconic Blender Institute Open Movies.
Featuring all the production files, assets, artwork, and never-seen-before content.
| {% elif title == 'training' %}
.node_index-collection-name
span Training
.node_index-collection-description
span.
Character modeling, 3D printing, VFX, rigging and more.
| {% endif %}
.node_index-collection
| {% for project in projects %}
| {% if (project.status == 'published') or (project.status == 'pending' and current_user.is_authenticated) and project._id != config.MAIN_PROJECT_ID %}
.node_index-collection-card.project(
data-url="{{ url_for('projects.view', project_url=project.url) }}",
tabindex="{{ loop.index }}")
| {% if project.picture_header %}
a.item-header(
href="{{ url_for('projects.view', project_url=project.url) }}")
img(src="{{ project.picture_header.thumbnail('m', api=api) }}")
| {% endif %}
.item-info
a.item-title(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {{project.name}}
| {% if project.status == 'pending' and current_user.is_authenticated and current_user.has_role('admin') %}
small (pending)
| {% endif %}
| {% if project.summary %}
p.item-description
| {{project.summary|safe}}
| {% endif %}
a.learn-more LEARN MORE
| {% endif %}
| {% endfor %}
| {% endblock %}
| {% block footer_scripts %}
script.
$('.node_index-collection-card.project').on('click', function(e){
e.preventDefault();
window.location.href = $(this).data('url');
});
| {% endblock %}

View File

@@ -0,0 +1,232 @@
| {% extends 'layout.html' %}
| {% from '_macros/_navigation.html' import navigation_tabs %}
| {% set title = 'dashboard' %}
| {% block og %}
meta(property="og:title", content="Dashboard")
meta(property="og:url", content="https://cloud.blender.org/{{ request.path }}")
meta(property="og:type", content="website")
| {% endblock %}
| {% block tw %}
meta(name="twitter:card", content="summary_large_image")
meta(name="twitter:site", content="@Blender_Cloud")
meta(name="twitter:title", content="Blender Cloud")
meta(name="twitter:image", content="{{ url_for('static', filename='assets/img/backgrounds/cloud_services_oti.jpg')}}")
| {% endblock %}
| {% block page_title %}
| {{current_user.full_name}}
| {% endblock %}
| {% block body %}
.dashboard-container
section#main
| {{ navigation_tabs(title) }}
section#projects
section#sub-nav-tabs.projects
ul#sub-nav-tabs__list
li.nav-tabs__list-tab.active(data-tab-toggle='own_projects')
| Own Projects
| {% if projects_user|length != 0 %}
span ({{ projects_user|length }})
| {% endif %}
li.nav-tabs__list-tab(data-tab-toggle='shared')
| Shared with me
| {% if projects_shared|length != 0 %}
span ({{ projects_shared|length }})
| {% endif %}
| {% if (current_user.has_role('subscriber') or current_user.has_role('admin')) %}
li.create(
data-url="{{ url_for('projects.create') }}")
a#project-create(
href="{{ url_for('projects.create') }}")
i.pi-plus
| Create Project
| {% endif %}
section.nav-tabs__tab.active#own_projects
ul.projects__list
| {% if projects_user %}
| {% for project in projects_user %}
li.projects__list-item(
data-url="{{ url_for('projects.view', project_url=project.url) }}")
a.projects__list-thumbnail(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {% if project.picture_square %}
img(src="{{ project.picture_square.thumbnail('s', api=api) }}")
| {% else %}
i.pi-blender-cloud
| {% endif %}
.projects__list-details
a.title(href="{{ url_for('projects.view', project_url=project.url) }}")
| {{ project.name }}
ul.meta
li.when(title="{{ project._created }}") {{ project._created | pretty_date }}
li.edit
a(href="{{ url_for('projects.edit', project_url=project.url) }}") Edit
| {% if project.status == 'pending' and current_user.is_authenticated and current_user.has_role('admin') %}
li.pending Not Published
| {% endif %}
| {% endfor %}
| {% else %}
li.projects__list-item
a.projects__list-thumbnail
i.pi-plus
.projects__list-details
a.title(href="{{ url_for('projects.create') }}")
| Create a project to get started!
| {% endif %}
section.nav-tabs__tab#shared
ul.projects__list
| {% if projects_shared %}
| {% for project in projects_shared %}
li.projects__list-item(
data-url="{{ url_for('projects.view', project_url=project.url) }}")
a.projects__list-thumbnail(
href="{{ url_for('projects.view', project_url=project.url) }}")
| {% if project.picture_square %}
img(src="{{ project.picture_square.thumbnail('s', api=api) }}")
| {% else %}
i.pi-blender-cloud
| {% endif %}
.projects__list-details
a.title(href="{{ url_for('projects.view', project_url=project.url) }}")
| {{ project.name }}
ul.meta
li.when {{ project._created | pretty_date }}
li.who by {{ project.user.full_name }}
li.edit
a(href="{{ url_for('projects.edit', project_url=project.url) }}") Edit
| {% if project.status == 'pending' and current_user.is_authenticated and current_user.has_role('admin') %}
li.pending Not Published
| {% endif %}
li.leave
span.user-remove-prompt
| Leave Project
span.user-remove
| Are you sure?
span.user-remove-confirm(
user-id="{{ current_user.objectid }}",
project-url="{{url_for('projects.sharing', project_url=project.url)}}")
i.pi-check
| Yes, leave
span.user-remove-cancel
i.pi-cancel
| No, cancel
| {% endfor %}
| {% else %}
li.projects__list-item
a.projects__list-thumbnail
i.pi-heart
.projects__list-details
.title
| No projects shared with you... yet!
| {% endif %}
section#side
section#announcement
img.header(
src="{{ url_for('static', filename='assets/img/backgrounds/services_projects.jpg')}}")
.text
.title Projects
.lead
span.
Create and manage your own personal projects.
Upload assets and collaborate with other Blender Cloud members.
.buttons
a.btn.btn-default.btn-outline.blue(
href="https://cloud.blender.org/blog/introducing-private-projects")
| Learn More
| {% endblock %}
| {% block footer_scripts %}
script.
$(document).ready(function() {
$('li.projects__list-item').click(function(e){
url = $(this).data('url');
if (typeof url === 'undefined') return;
window.location.href = url;
if (console) console.log(url);
$(this).addClass('active');
$(this).find('.projects__list-thumbnail i')
.removeAttr('class')
.addClass('pi-spin spin');
});
// Tabs behavior
var $nav_tabs_list = $('#sub-nav-tabs__list');
var $nav_tabs = $nav_tabs_list.find('li.nav-tabs__list-tab');
$nav_tabs.on('click', function(e){
e.preventDefault();
$nav_tabs.removeClass('active');
$(this).addClass('active');
$('.nav-tabs__tab').hide();
$('#' + $(this).attr('data-tab-toggle')).show();
});
// Create project
$nav_tabs_list.find('li.create').on('click', function(e){
e.preventDefault();
$(this).addClass('disabled');
$('a', this).html('<i class="pi-spin spin"></i> Creating project...');
window.location.href = $(this).data('url');
});
// Leave project
var $projects_list = $('ul.projects__list');
$projects_list.find('span.user-remove-prompt').on('click', function(e){
e.stopPropagation();
e.preventDefault();
$(this).next().show();
$(this).hide();
});
$projects_list.find('span.user-remove-cancel').on('click', function(e){
e.stopPropagation();
e.preventDefault();
$(this).parent().prev().show();
$(this).parent().hide();
});
$projects_list.find('span.user-remove-confirm').on('click', function(e){
e.stopPropagation();
e.preventDefault();
var parent = $(this).closest('projects__list-item');
function removeUser(userId, projectUrl){
$.post(projectUrl, {user_id: userId, action: 'remove'})
.done(function (data) {
parent.remove();
});
}
removeUser($(this).attr('user-id'), $(this).attr('project-url'));
});
hopToTop(); // Display jump to top button
});
| {% endblock %}

View File

@@ -0,0 +1,266 @@
| {% extends 'layout.html' %}
| {% set title = 'sharing' %}
| {% block page_title %}Sharing: {{ project.name }}{% endblock %}
| {% block body %}
#project-container
#project-side-container
#project_sidebar
ul.project-tabs
li.tabs-thumbnail(
title="About",
data-toggle="tooltip",
data-placement="left",
class="{% if title == 'about' %}active {% endif %}{% if project.picture_square %}image{% endif %}")
a(href="{{url_for('projects.about', project_url=project.url, _external=True)}}")
#project-loading
i.pi-spin
| {% if project.picture_square %}
img(src="{{ project.picture_square.thumbnail('b', api=api) }}")
| {% else %}
i.pi-home
| {% endif %}
li.tabs-browse(
title="Browse",
data-toggle="tooltip",
data-placement="left")
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
i.pi-tree-flow
| {% if not project.is_private %}
li.tabs-search(
title="Search",
data-toggle="tooltip",
data-placement="left")
a(href="{{url_for('projects.search', project_url=project.url, _external=True)}}")
i.pi-search
| {% endif %}
.project_nav-toggle-btn(
title="Expand Navigation [T]",
data-toggle="tooltip",
data-placement="right")
i.pi-angle-double-left
#project_nav
#project_nav-container
#project_nav-header
.project-title
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
| {{ project.name }}
// TODO - make list a macro
#project_tree
ul.project_nav-edit-list
li(class="{% if title == 'edit' %}active{% endif %}")
a(href="{{ url_for('projects.edit', project_url=project.url) }}")
i.pi-list
| Overview
li(class="{% if title == 'sharing' %}active{% endif %}")
a(href="{{ url_for('projects.sharing', project_url=project.url) }}")
i.pi-share
| Sharing
li(class="{% if title == 'edit_node_types' %}active{% endif %}")
a(href="{{ url_for('projects.edit_node_types', project_url=project.url) }}")
i.pi-puzzle
| Node Types
.project_split(title="Toggle Navigation [T]")
#project_context-container
#project_context-header
span#project-statusbar
span#project-edit-title
| Manage users for this project
#project_context
#node-edit-container
#node-edit-form
.col-md-6
| {% if (project.user == current_user.objectid or current_user.has_role('admin')) %}
.sharing-users-search
.form-group
input#user-select.form-control(
name='contacts',
type='text',
placeholder='Add users by name')
| {% else %}
.sharing-users-search
.disabled Only project owners can manage users
| {% endif %}
ul.sharing-users-list
| {% for user in users %}
li.sharing-users-item(
user-id="{{ user['_id'] }}",
class="{% if current_user.objectid == user['_id'] %}self{% endif %}")
.sharing-users-avatar
img(src="{{ user['avatar'] }}")
.sharing-users-details
span.sharing-users-name
| {{user['full_name']}}
| {% if project.user == user['_id'] and current_user.objectid == user['_id'] %}
small (You, owner)
| {% elif project.user == user['_id'] %}
small (Owner)
| {% elif current_user.objectid == user['_id'] %}
small (You)
| {% endif %}
span.sharing-users-extra {{user['username']}}
.sharing-users-action
| {# Only allow deletion if we are: admin, project owners, or current_user in the team #}
| {% if current_user.has_role('admin') or (project.user == current_user.objectid) or (current_user.objectid == user['_id']) %}
| {% if project.user == user['_id'] %}
span
i.pi-happy(title="Hi boss!")
| {% elif current_user.objectid == user['_id'] %}
button.user-remove(title="Leave this project") Leave
| {% else %}
button.user-remove(title="Remove this user from your project")
i.pi-trash
| {% endif %}
| {% endif %}
| {% endfor %}
.col-md-6
.sharing-users-info
h4 What can team members do?
p.
Team members are able to upload new content to the
project; as well as view, edit, and comment on the content previously created.
| {% endblock %}
| {% block footer_navigation %}
| {% endblock %}
| {% block footer_scripts %}
script(type="text/javascript").
$(window).on("load resize",function(){
containerResizeY($(window).height());
});
| {% if (project.user == current_user.objectid or current_user.has_role('admin')) %}
script(src='//cdn.jsdelivr.net/autocomplete.js/0/autocomplete.jquery.min.js')
script.
$(document).ready(function() {
var APPLICATION_ID = '{{config.ALGOLIA_USER}}'
var SEARCH_ONLY_API_KEY = '{{config.ALGOLIA_PUBLIC_KEY}}';
var INDEX_NAME = '{{config.ALGOLIA_INDEX_USERS}}';
var client = algoliasearch(APPLICATION_ID, SEARCH_ONLY_API_KEY);
var index = client.initIndex(INDEX_NAME);
$('#user-select').autocomplete({hint: false}, [
{
source: function (q, cb) {
index.search(q, {hitsPerPage: 5}, function (error, content) {
if (error) {
cb([]);
return;
}
cb(content.hits, content);
});
},
displayKey: 'full_name',
minLength: 2,
limit: 10,
templates: {
suggestion: function (hit) {
return hit._highlightResult.full_name.value + ' (' + hit._highlightResult.username.value + ')';
}
}
}
]).on('autocomplete:selected', function (event, hit, dataset) {
var lis = document.getElementsByClassName('sharing-users-item');
var has_match = false;
for (var i = 0; i < lis.length; ++i) {
// Check if the user already is in the list
if ($(lis[i]).attr('user-id') == hit.objectID){
$(lis[i]).addClass('active');
setTimeout(function(){ $('.sharing-users-item').removeClass('active');}, 350);
statusBarSet('info', 'User is already part of the project', 'pi-info');
has_match = false;
break;
} else {
has_match = true;
continue;
}
};
if (has_match){
addUser(hit.objectID);
}
});
function addUser(userId){
if (userId && userId.length > 0) {
$.post("{{url_for('projects.sharing', project_url=project.url)}}",
{user_id: userId, action: 'add'})
.done(function (data) {
$("ul.sharing-users-list").prepend('' +
'<li class="sharing-users-item" user-id="' + data._id + '">' +
'<div class="sharing-users-avatar">' +
'<img src="' + data.avatar + '">'+
'</div>' +
'<div class="sharing-users-details">' +
'<span class="sharing-users-name">' + data.full_name + '</span>' +
'<span class="sharing-users-extra">' + data.username + '</span>' +
'</div>' +
'<div class="sharing-users-action">' +
'<button title="Remove this user from your project" class="user-remove">'+
'<i class="pi-trash"></i>'+
'</button>'+
'</div>'+
'</li>');
$("ul.sharing-users-list").find("[user-id='" + userId + "']").addClass('added');
setTimeout(function(){ $('.sharing-users-item').removeClass('added');}, 350);
statusBarSet('success', 'User added to this project!', 'pi-grin');
})
.fail(function (jsxhr){
data = jsxhr.responseJSON;
statusBarSet('error', 'Could not add user (' + data.message + ')', 'pi-warning');
});
} else {
statusBarSet('error', 'Please select a user from the list', 'pi-warning');
}
};
});
| {% endif %}
script.
$(document).ready(function() {
$('body').on('click', '.user-remove', function(e) {
var userId = $(this).parent().parent().attr('user-id');
removeUser(userId);
});
function removeUser(userId){
$.post("{{url_for('projects.sharing', project_url=project.url)}}",
{user_id: userId, action: 'remove'})
.done(function (data) {
$("ul.sharing-users-list").find("[user-id='" + userId + "']").remove();
statusBarSet('success', 'User removed from this project', 'pi-trash');
})
.fail(function (data){
statusBarSet('error', 'Could not remove user (' + data._status + ')', 'pi-warning');
});
}
});
| {% endblock %}

View File

@@ -0,0 +1,586 @@
| {% extends 'layout.html' %}
| {% from '_macros/_add_new_menu.html' import add_new_menu %}
| {% block page_title %}{{project.name}}{% endblock%}
| {% block og %}
meta(property="og:type", content="website")
| {% if og_picture %}
meta(property="og:image", content="{{ og_picture.thumbnail('l', api=api) }}")
| {% endif %}
| {% if show_project %}
meta(property="og:title", content="{{project.name}} - Blender Cloud")
meta(property="og:url", content="{{url_for('projects.view', project_url=project.url, _external=True)}}")
meta(property="og:description", content="{{project.summary}}")
| {% else %}
meta(property="og:title", content="{{node.name}} - Blender Cloud")
meta(property="og:url", content="{{url_for('projects.view_node', project_url=project.url, node_id=node._id)}}")
meta(property="og:description", content="{{node.description}}")
| {% endif %}
| {% endblock %}
| {% block tw %}
| {% if og_picture %}
meta(property="twitter:image", content="{{ og_picture.thumbnail('l', api=api) }}")
| {% endif %}
| {% if show_project %}
meta(name="twitter:title", content="{{project.name}} on Blender Cloud")
meta(name="twitter:description", content="{{project.summary}}")
| {% else %}
meta(name="twitter:title", content="{{node.name}} on Blender Cloud")
meta(name="twitter:description", content="{{node.description}}")
| {% endif %}
| {% endblock %}
| {% block head %}
link(href="//cdnjs.cloudflare.com/ajax/libs/jstree/3.3.1/themes/default/style.min.css", rel="stylesheet")
| {% endblock %}
| {% block css %}
link(href="{{ url_for('static_pillar', filename='assets/css/project-main.css', v=040820161) }}", rel="stylesheet")
| {% endblock %}
| {% block body %}
#project-container
#project-side-container
#project_sidebar
ul.project-tabs
li.tabs-thumbnail(
title="About",
data-toggle="tooltip",
data-placement="left",
class="{% if title == 'about' %}active {% endif %}{% if project.picture_square %}image{% endif %}")
a(href="{{url_for('projects.about', project_url=project.url, _external=True)}}")
#project-loading
i.pi-spin
| {% if project.picture_square %}
img(src="{{ project.picture_square.thumbnail('b', api=api) }}")
| {% else %}
i.pi-home
| {% endif %}
li.tabs-browse(
title="Browse",
data-toggle="tooltip",
data-placement="left",
class="{% if title != 'about' %}active{% endif %}")
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
i.pi-tree-flow
| {% if not project.is_private %}
li.tabs-search(
title="Search",
data-toggle="tooltip",
data-placement="left")
a(href="{{url_for('projects.search', project_url=project.url, _external=True)}}")
i.pi-search
| {% endif %}
.project_nav-toggle-btn(
title="Expand Navigation [T]",
data-toggle="tooltip",
data-placement="right")
i.pi-angle-double-left
#project_nav(class="{{ title }}")
#project_nav-container
| {% if title != 'about' %}
#project_nav-header
.project-title
a(href="{{url_for('projects.view', project_url=project.url, _external=True)}}")
| {{ project.name }}
#project_tree
| {% endif %}
.project_split(title="Toggle Navigation [T]")
#project_context-container
| {% if project.has_method('PUT') %}
#project_context-header
span#project-statusbar
ul.project-edit-tools.disabled
li.button-dropdown
a#item_add.dropdown-toggle.project-mode-view(
type="button",
data-toggle="dropdown",
aria-haspopup="true",
aria-expanded="false")
i.button-add-icon.pi-collection-plus
| New...
ul.dropdown-menu.add_new-menu
| {{ add_new_menu(project.node_types) }}
li.button-edit
a#item_edit.project-mode-view(
href="javascript:void(0);",
title="Edit",
data-project_id="{{project._id}}")
i.button-edit-icon.pi-edit
| Edit Project
li.button-dropdown
a.dropdown-toggle.project-mode-view(
type="button",
data-toggle="dropdown",
aria-haspopup="true",
aria-expanded="false")
i.pi-more-vertical
ul.dropdown-menu
| {% if current_user.has_role('admin') %}
li.button-featured
a#item_featured(
href="javascript:void(0);",
title="Feature on project's homepage",
data-toggle="tooltip",
data-placement="left")
i.button-featured-icon.pi-star
| Toggle Featured
li.button-toggle-public
a#item_toggle_public(
href="javascript:void(0);",
title="Toggle public",
data-toggle="tooltip",
data-placement="left")
i.pi-lock-open
| Toggle public
| {% endif %}
li.button-toggle-projheader
a#item_toggle_projheader(
href="javascript:void(0);",
title="Feature as project's header",
data-toggle="tooltip",
data-placement="left")
i.button-featured-icon.pi-star
| Toggle Project Header video
li.button-move
a#item_move(
href="javascript:void(0);",
title="Move into a folder...",
data-toggle="tooltip",
data-placement="left")
i.button-move-icon.pi-move
| Move
li.button-delete
a#item_delete(
href="javascript:void(0);",
title="Delete",
data-toggle="tooltip",
data-placement="left")
i.pi-trash
| Delete Project
// Edit Mode
li.button-cancel
a#item_cancel.project-mode-edit(
href="javascript:void(0);",
title="Cancel changes")
i.button-cancel-icon.pi-cancel
| Cancel
li.button-save
a#item_save.project-mode-edit(
href="javascript:void(0);",
title="Save changes")
i.button-save-icon.pi-check
| Save Changes
| {% endif %}
#project_context
| {% if show_project %}
| {% include "projects/view_embed.html" %}
| {% endif %}
#overlay-mode-move-container
.overlay-container
.title
i.pi-angle-left
| Select the <strong>folder</strong> where you want to move it
.buttons
button#item_move_accept.move.disabled
| Select a Folder
button#item_move_cancel.cancel
i.pi-cancel
| Cancel
| {% endblock %}
| {% block footer_navigation %}{% endblock %}
| {% block footer %}{% endblock %}
| {% block footer_scripts %}
script(src="//cdnjs.cloudflare.com/ajax/libs/jstree/3.3.1/jstree.min.js")
script(src="//releases.flowplayer.org/6.0.5/flowplayer.min.js")
| {% if project.has_method('PUT') %}
| {# JS containing the Edit, Add, Featured, and Move functions #}
script(type="text/javascript", src="{{ url_for('static_pillar', filename='assets/js/project-edit.min.js', v=190520161) }}")
| {% endif %}
script.
{% if show_project %}
ProjectUtils.setProjectAttributes({projectId: "{{project._id}}", isProject: true, nodeId: ''});
{% else %}
ProjectUtils.setProjectAttributes({projectId: "{{project._id}}", isProject: false, nodeId: '{{node._id}}'});
{% endif %}
var projectTree = document.getElementById('project_tree');
/* Initialize project_tree scrollbar */
if ((typeof Ps !== 'undefined') && projectTree && window.innerWidth > 768){
Ps.initialize(projectTree, {suppressScrollX: true});
}
var urlNodeMove = "{{url_for('projects.move_node')}}";
var urlNodeFeature = "{{url_for('projects.add_featured_node')}}";
var urlNodeDelete = "{{url_for('projects.delete_node')}}";
var urlNodeTogglePublic = "{{url_for('projects.toggle_node_public')}}";
var urlNodeToggleProjHeader = "{{url_for('projects.toggle_node_project_header')}}";
var urlProjectDelete = "{{url_for('projects.delete')}}";
var urlProjectEdit = "{{url_for('projects.edit', project_url=project.url)}}";
function updateToggleProjHeaderMenuItem() {
var $toggle_projheader = $('#item_toggle_projheader');
if (ProjectUtils.isProject()) {
$toggle_projheader.hide();
return;
}
if (ProjectUtils.nodeType() == 'asset') {
$toggle_projheader.show();
} else {
$toggle_projheader.hide();
}
}
$(updateToggleProjHeaderMenuItem);
// Function to update the interface on loadNodeContent, and edit/saving assets
function updateUi(nodeId, mode){
if (mode === 'view') {
$('.project-mode-view').show();
$('.project-mode-edit').hide();
$("#node-edit-form").unbind( "submit" );
$("#item_save").unbind( "click" );
$("#item_cancel").unbind( "click" );
} else if (mode === 'edit') {
$('.project-mode-view').hide();
$('.project-mode-edit').show();
} else {
if (console) console.log('Invalid mode:', mode);
}
// Prevent flicker by scrolling to top
$("#project_context-container").scrollTop(0);
// Enable specific items under the Add New dropdown
if (ProjectUtils.nodeType() === 'group') {
addMenuEnable(['asset', 'group']);
} else if (ProjectUtils.nodeType() === 'group_texture') {
addMenuEnable(['group_texture', 'texture']);
} else if (ProjectUtils.nodeType() === 'group_hdri') {
addMenuEnable(['group_hdri', 'hdri']);
} else if (!ProjectUtils.isProject()) {
addMenuEnable(false);
}
updateToggleProjHeaderMenuItem();
var nodeTitle = document.getElementById('node-title');
var nodeTitleText = $(nodeTitle).text() + " - {{project.name}} - Blender Cloud";
document.title = nodeTitleText;
// TODO: Maybe remove this, now it's also in loadNodeContent(), but double-check
// it's done like that in all users of updateUi().
$('#project-loading').removeAttr('class');
}
function loadNodeContent(url, nodeId) {
$('#project-loading').addClass('active');
$.get(url, function(dataHtml) {
// Update the DOM injecting the generate HTML into the page
$('#project_context').html(dataHtml);
})
.done(function(){
updateUi(nodeId, 'view');
})
.fail(function(dataResponse) {
$('#project_context').html($('<iframe id="server_error"/>'));
$('#server_error').attr('src', url);
})
.always(function(){
$('#project-loading').removeAttr('class');
$('.button-edit-icon').addClass('pi-edit').removeClass('pi-spin spin');
});
}
function loadProjectContent(url) {
$('#project-loading').addClass('active');
$.get(url, function(dataHtml) {
// Update the DOM injecting the generated HTML into the page
$('#project_context').html(dataHtml);
})
.done(function() {
updateUi('', 'view');
addMenuEnable();
addMenuDisable(['texture']);
})
.fail(function(dataResponse) {
$('#project_context').html($('<iframe id="server_error"/>'));
$('#server_error').attr('src', url);
})
.always(function(){
$('#project-loading').removeAttr('class');
$('.button-edit-icon').addClass('pi-edit').removeClass('pi-spin spin');
});
}
function displayStorage(storageNodeId, path) {
var url = '/nodes/' + storageNodeId + '/view?path=' + path;
loadNodeContent(url);
}
function displayNode(nodeId, pushState) {
// Remove the 'n_' suffix from the id
if (nodeId.substring(0, 2) == 'n_') {
nodeId = nodeId.substr(2);
}
var url = '/nodes/' + nodeId + '/view';
loadNodeContent(url, nodeId);
// Determine whether we should push the new state or not.
pushState = (typeof pushState !== 'undefined') ? pushState : true;
if (!pushState) return;
// Push the correct URL onto the history.
var push_state = {nodeId: nodeId, url: url};
var push_url = '{{url_for("projects.view", project_url=project.url)}}' + nodeId;
// console.log('Pushing state ', push_state, ' with URL ', push_url);
window.history.pushState(
push_state,
'Node ' + nodeId, // TODO: use sensible title
push_url
);
}
function redirectToNode(nodeId) {
var generic_url = '{{ url_for("projects.view_node", project_url=project.url, node_id="theNodeId") }}';
var node_url = generic_url.replace('theNodeId', nodeId);
// This makes the user skip the current page when using the 'back' button,
// i.e. it works as a proper redirect.
location.replace(node_url);
}
window.onpopstate = function(event) {
var state = event.state;
// console.log('State popped. location:', document.location, 'state:', state);
// Deselect any selected node. We'll select the visited node (if any) later on.
var jstreeAPI = $(projectTree).jstree(true);
jstreeAPI.deselect_all(true);
if (state == null) {
// Went back to the project.
displayProject();
return;
}
// Went back to a node.
loadNodeContent(state.url, state.nodeId);
// Annoying hack because jstreeAPI.select_node() can only suppress the
// changed.jstree event, and NOT the selected_node.jstree event.
projectTree.dataset.ignoreSelectNode = true;
jstreeAPI.select_node('n_' + state.nodeId, true);
delete projectTree.dataset.ignoreSelectNode;
};
function displayProject() {
var url = "{{url_for('projects.view', project_url=project.url, embed=1)}}";
loadProjectContent(url);
}
function getHashId() {
if (console)
console.log('getHashId() should not be used any more!');
}
/* Loaded once, on page load */
function loadContent() {
var nodeId = ProjectUtils.nodeId();
var isProject = ProjectUtils.isProject();
if (isProject) {
// No need to asynchronously load the project, as it's embedded by Jinja.
// displayProject() is still needed, though, when people use 'back' to go there.
if (location.hash) {
// Handle old-style /p/{url}/#node-ID links, and redirect them to the correct spot.
redirectToNode(location.hash.substr(1));
}
$('.project-mode-view').show();
$('.project-mode-edit').hide();
} else {
displayNode(nodeId, false);
}
$(projectTree).jstree({
'core': {
'data': function (obj, callback) {
if(obj.id === '#') { //tree root
if (isProject) {
$.getJSON("{{url_for('projects.jstree', project_url=project.url)}}", function (jsonObject) {
callback.call(this, jsonObject['items']);
});
} else {
$.getJSON('/nodes/' + nodeId + '/jstree', function(jsonObject) {
callback.call(this, jsonObject['items']);
});
}
} else { //normal node
var childNodeId;
if (obj.original.type == 'group_storage') {
childNodeId = obj.original.storage_node;
$.getJSON('/nodes/' + childNodeId + '/jstree?children=1&path=' + obj.original.path, function(jsonObject) {
callback.call(this, jsonObject.children);
});
} else {
// Remove the 'n_' suffix from the id
childNodeId = obj.id.substring(2);
$.getJSON('/nodes/' + childNodeId + '/jstree?children=1', function(jsonObject) {
callback.call(this, jsonObject.children);
});
}
}
}
},
"types" : {
"#": {"valid_children": ["collection"]},
"chapter" : {"icon": "pi-folder"},
"group" : {"icon": "pi-folder"},
"group_texture" : {"icon": "pi-folder-texture"},
"group_hdri" : {"icon": "pi-folder-texture", "max_children": 0},
"group_storage" : {"icon": "pi-folder"},
"filesystem_node" : {"icon": "pi-folder"},
"file" : {"icon": "pi-file-archive", "max_children": 0},
"filesystem_file" : {"icon": "pi-document", "max_children": 0},
"image" : {"icon": "pi-image", "max_children": 0},
"hdri" : {"icon": "pi-globe", "max_children": 0},
"texture" : {"icon": "pi-texture", "max_children": 0},
"video" : {"icon": "pi-film-thick", "max_children": 0},
"blog" : {"icon": "pi-newspaper", "max_children": 0},
"default" : {"icon": "pi-document"}
},
"plugins": ["types",] //, "state", "sort"
});
var jstreeAPI = $(projectTree).jstree(true);
$(projectTree).on("select_node.jstree", function (e, data) {
var selectedNodeId = data.node.id.substr(2);
// Ignore events that can't be suppressed otherwise.
// This can be removed if jstreeAPI.select_node() allows suppressing
// the select_node.jstree event.
if (e.target.dataset.ignoreSelectNode === 'true') return;
if (typeof(data.node.original.path) === 'undefined') {
var movingMode = Cookies.getJSON('bcloud_moving_node');
// Check if we are in the process of moving a node
if (movingMode) {
// Allow moving nodes only inside of node_type group
if (data.node.original.type != 'group' || movingMode.node_id === selectedNodeId || movingMode.node_id === ProjectUtils.parentNodeId()) {
if (movingMode.node_type === 'texture') {
if (data.node.original.type === 'group_texture') {
$('#item_move_accept').html('<i class="pi-check"></i>Move Here').removeClass('disabled');
} else {
$('#item_move_accept').html('Select a Texture Folder').addClass('disabled');
}
} else if (movingMode.node_type === 'hdri') {
if (data.node.original.type === 'group_hdri') {
$('#item_move_accept').html('<i class="pi-check"></i>Move Here').removeClass('disabled');
} else {
$('#item_move_accept').html('Select an HDRi Folder').addClass('disabled');
}
} else {
$('#item_move_accept').html('Select a Folder').addClass('disabled');
}
} else {
$('#item_move_accept').html('<i class="pi-check"></i>Move Here').removeClass('disabled');
}
}
// Check the type of node and act accordingly
if (data.node.original.type == 'blog') {
window.location.replace('blog');
} else {
var currentNodeId = ProjectUtils.nodeId();
if (currentNodeId != selectedNodeId) {
displayNode(selectedNodeId);
}
jstreeAPI.open_node(data.node);
}
} else {
displayStorage(data.node.original.storage_node, data.node.original.path);
jstreeAPI.toggle_node(data.node);
}
/* Update scrollbar */
Ps.update(projectTree);
});
$(projectTree).on("open_node.jstree", function () {
/* Update scrollbar */
Ps.update(projectTree);
});
$(projectTree).on("close_node.jstree", function () {
/* Update scrollbar */
Ps.update(projectTree);
});
};
// Initialize the page
loadContent();
/* UI Stuff */
$(window).on("load resize",function(){
containerResizeY($(window).height());
});
if (projectTree){
$(projectTree).hover(function(){
Ps.update(projectTree);
});
}
| {% endblock %}
| {% block comment_scripts %} {% endblock%}

View File

@@ -0,0 +1,188 @@
| {% block head %}
| {% if header_video_file %}
script(src="//releases.flowplayer.org/6.0.5/flowplayer.min.js")
script.
$(function() {
$('#flowplayer_container').flowplayer({
key: "{{config.FLOWPLAYER_KEY}}",
embed: false,
splash: true,
clip: { sources: [
{% for var in header_video_file.variations %}
{type: "{{ var.content_type }}", src: "{{ var.link|safe }}"},
{% endfor %}
]}
});
});
| {% endif %}
| {% endblock %}
| {% block body %}
#node-container
section.node-preview.project
| {% if project.url == 'caminandes-3' %}
iframe(
style="height: 490px",
src="https://www.youtube.com/embed/SkVqJ1SGeL0?rel=0&amp;controls=0&amp;showinfo=0",
frameborder="0",
allowfullscreen)
| {% elif header_video_file %}
#flowplayer_container.is-splash.play-button(
style="{% if header_video_node.picture %}background-image:url({{header_video_node.picture.thumbnail('l', api=api)}}); background-repeat:no-repeat; {% endif %}")
.fp-startscreen.fp-toggle
a.big-play-button
i.pi-play
.fp-endscreen
a.watch-again.fp-toggle
i.pi-replay
| Watch again
.fp-waiting
i.pi-spin.spin
| {% elif project.picture_header %}
a(href="{{ url_for( 'projects.about', project_url=project.url) }}")
img.header(src="{{ project.picture_header.thumbnail('l', api=api) }}")
| {% endif %}
section.node-details-container.project
| {# Hide for now
.node-details-header
.node-title-details
.date(title="Last updated {{ project._updated | pretty_date }}") {{ project._created | pretty_date }}
| {% if project.status %}
.status {{project.status}}
| {% endif %}
| #}
.node-details-title
h1
a(href="{{ url_for( 'projects.about', project_url=project.url) }}") {{ project.name }}
| {% if title != 'about' or not project.description %}
| {% set description = project.summary %}
| {% else %}
| {% set description = project.description %}
| {% endif %}
.node-details-description
| {{ description }}
| {% if title != 'about' %}
.node-extra
a.learn-more(href="{{ url_for( 'projects.about', project_url=project.url) }}") LEARN MORE
| {% endif %}
| {% if project.nodes_featured %}
.project-featured-container
h3 Featured Content
.featured-list#featured-list
| {% for n in project.nodes_featured %}
| {% if n.picture %}
a.featured-item.hidden(href="{{ url_for_node(node=n) }}")
.featured-item-info
span.type {{ n.properties.content_type }} - {{ n.user.full_name }}
span.title {{ n.name }}
img(src="{{ n.picture.thumbnail('l', api=api) }}")
| {% endif %}
| {% endfor %}
| {% endif %}
.node-extra
| {% if project.nodes_blog %}
.node-blog
h3 Blog
ul.node-blog-list
| {% for n in project.nodes_blog %}
li.node-blog-list-item(data-node_id="{{ n._id }}")
a.image(href="{{ url_for_node(node=n) }}")
| {% if n.picture %}
img(src="{{ n.picture.thumbnail('s', api=api) }}")
| {% else %}
i.pi-chatbubble-working
| {% endif %}
.info
a.title(href="{{ url_for_node(node=n) }}") {{ n.name }}
span.details
span.when {{ n._updated | pretty_date }} by
span.who {{ n.user.full_name }}
| {% endfor %}
| {% endif %}
| {% if project.nodes_latest %}
.node-updates
h3 Latest Updates
ul.node-updates-list
| {% for n in project.nodes_latest %}
| {% if n.node_type not in ['comment'] %}
li.node-updates-list-item(data-node_id="{{ n._id }}")
a.image(href="{{ url_for_node(node=n) }}")
| {% if n.picture %}
img(src="{{ n.picture.thumbnail('s', api=api) }}")
| {% else %}
| {% if n.properties.content_type == 'video' %}
i.pi-film-thick
| {% elif n.properties.content_type == 'image' %}
i.pi-picture
| {% elif n.properties.content_type == 'file' %}
i.pi-file-archive
| {% else %}
i.pi-folder
| {% endif %}
| {% endif %}
.info
a.title(href="{{ url_for_node(node=n) }}") {{ n.name }}
span.details
span.what {% if n.properties.content_type %}{{ n.properties.content_type }}{% else %}folder{% endif %} ·
span.when {{ n._updated | pretty_date }} by
span.who {{ n.user.full_name }}
| {% endif %}
| {% endfor %}
| {% endif %}
include _scripts
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.montage.min.js') }}")
script.
function montage(){
var $container = $('#featured-list'),
$imgs = $container.find('img').hide(),
totalImgs = $imgs.length,
cnt = 0;
$imgs.each(function(i) {
var $img = $(this);
$('<img/>').load(function() {
++cnt;
if( cnt === totalImgs ) {
$imgs.show();
$container.montage({
fillLastRow : true,
alternateHeight : true,
alternateHeightRange : {
min : 180,
max : 240
},
margin : 3
});
}
}).attr('src',$img.attr('src'));
$img.parent().removeClass('hidden');
});
}
$(function() {
montage();
$(".node-updates-list-item, .node-blog-list-item")
.unbind('click')
.click(function(e) {
e.preventDefault();
displayNode($(this).data('node_id'));
});
});
| {% endblock %}

View File

@@ -0,0 +1,57 @@
| {% extends 'layout.html' %}
| {% set title = 'theatre' %}
| {% block og %}
meta(property="og:title", content="{{ node.name }}")
meta(property="og:url", content="{{ url_for('projects.view_node', project_url=project.url, node_id=node._id, t=1, _external=True) }}")
meta(property="og:type", content="website")
meta(property="og:description", content="Created on {{ node._created.strftime('%d %b %Y') }}")
| {% if og_picture %}
meta(property="og:image", content="{{ og_picture.thumbnail('l', api=api) }}")
meta(property="og:image:secure_url", content="{{ og_picture.thumbnail('l', api=api) }}")
meta(property="og:image:type", content="{{ og_picture.content_type }}")
meta(property="og:image:witdh", content="{{ og_picture.width }}")
meta(property="og:image:height", content="{{ og_picture.height }}")
| {% endif %}
| {% endblock %}
| {% block tw %}
| {% if og_picture %}
meta(property="twitter:image", content="{{ og_picture.thumbnail('l', api=api) }}")
| {% endif %}
meta(name="twitter:title", content="{{node.name}}")
meta(name="twitter:description", content="Created on {{ node._created.strftime('%d %b %Y') }}")
| {% endblock %}
| {% block header_backdrop %}{% endblock %}
| {% block navigation_search %}{% endblock %}
| {% block navigation_sections %}
li
a.navbar-item.info(
href="",
title="Toggle info & sidebar")
i.pi-info
| {% endblock %}
| {% block css %}
link(href="{{ url_for('static_pillar', filename='assets/css/theatre.css', v=2016) }}", rel="stylesheet")
| {% endblock %}
| {% block body %}
#theatre-container(class="{% if current_user.is_authenticated %}with-info{% endif %}")
| {% endblock %}
| {% block footer_scripts %}
script.
$(function(){
$.get("{{url_for('nodes.view', node_id=node._id, t=True)}}", function(dataHtml) {
$("#theatre-container").html(dataHtml);
});
});
| {% endblock %}
| {% block footer %}{% endblock %}
| {% block footer_navigation %}{% endblock %}

218
src/templates/services.jade Normal file
View File

@@ -0,0 +1,218 @@
| {% extends 'layout.html' %}
| {% block page_title %}Services{% endblock %}
| {% set title = 'services' %}
| {% block og %}
meta(property="og:title", content="Services - Blender Cloud")
meta(property="og:url", content="https://cloud.blender.org/services")
meta(property="og:image", content="{{ url_for('static', filename='assets/img/backgrounds/background_services.jpg')}}")
| {% endblock %}
| {% block header_backdrop %}
.navbar-backdrop(
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/services_projects.jpg')}})")
.navbar-backdrop-overlay.services
| {% endblock %}
| {% block page_overlay %}
#page-overlay.video
.video-embed
| {% endblock %}
| {% block body %}
#page-container
#page-header
.page-title(style="text-align: left")
i.pi-blender-cloud-logo
em SERVICES
.page-title-summary
| Personal Projects · Blender Integration · Texture Browsing · Production Management
#page-content
section.page-card
.page-card-side
h2.page-card-title Private Projects
.page-card-summary.
Create and manage your own personal projects.
Upload assets and collaborate with other Blender Cloud members.
a.page-card-cta(
href="https://cloud.blender.org/blog/introducing-private-projects")
| Learn More
.page-card-side
.page-card-icon
svg(xmlns='http://www.w3.org/2000/svg', width='56', height='64', viewbox='0 0 56 64')
g(fill='#555')
path(d='M42 38H14V26h28v12zm-26-2h24v-8H16v8zm-4-5H8c-1.654 0-3-1.346-3-3V15h2v13c0 .55.45 1 1 1h4v2z')
path(d='M9.293 19.707L6 16.414l-3.293 3.293-1.414-1.414 4-4c.39-.39 1.023-.39 1.414 0l4 4-1.414 1.414zM48 31h-4v-2h4c.55 0 1-.45 1-1V15h2v13c0 1.654-1.346 3-3 3z')
path(d='M53.293 19.707L50 16.414l-3.293 3.293-1.414-1.414L50 13.586l4.707 4.707M27 15h2v9h-2z')
path(d='M31.293 19.707L28 16.414l-3.293 3.293-1.414-1.414L28 13.586l4.707 4.707M7 49H5V36c0-1.654 1.346-3 3-3h4v2H8c-.55 0-1 .45-1 1v13z')
path(d='M6 50c-.256 0-.512-.098-.707-.293l-4-4 1.414-1.414L6 47.586l3.293-3.293 1.414 1.414-4 4c-.195.195-.45.293-.707.293zm45-1h-2V36c0-.55-.45-1-1-1h-4v-2h4c1.654 0 3 1.346 3 3v13z')
path(d='M50 50.414l-4.707-4.707 1.414-1.414L50 47.586l3.293-3.293 1.414 1.414M27 40h2v9h-2z')
path(d='M28 50.414l-4.707-4.707 1.414-1.414L28 47.586l3.293-3.293 1.414 1.414M6 12c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zM6 2C3.794 2 2 3.794 2 6s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zm22 10c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zm22 10c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zM6 64c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zm22 10c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zm22 10c-3.31 0-6-2.692-6-6s2.69-6 6-6 6 2.692 6 6-2.69 6-6 6zm0-10c-2.206 0-4 1.794-4 4s1.794 4 4 4 4-1.794 4-4-1.794-4-4-4zM27 31h2v2h-2zm-4 0h2v2h-2zm8 0h2v2h-2z')
#blender-addon.page-section-container(
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/pattern_bw_01.jpg')}})")
section.page-card-header.dark Blender Cloud Add-on
span.page-card-header_lead.dark Connect Blender with the Cloud
a.page-card-cta.download(
href="https://cloud.blender.org/r/downloads/blender_cloud-latest-bundle.zip")
i.pi-download
| Download <small>v</small>1.3
section.page-card.dark.right
.page-card-side
h2.page-card-title Blender Sync
.page-card-summary.
Save your settings once. Use them anywhere.
Carry your Blender configuration with you,
use our free add-on to sync your keymaps and preferences.
<hr/>
<small>Syncing settings is free for everyone! No subscription required.</small>
a.page-card-cta(
href="https://cloud.blender.org/blog/introducing-blender-sync")
| Learn More
.page-card-side
.page-card-icon
svg(xmlns="http://www.w3.org/2000/svg",
width="64", height="54", viewBox="0 0 64 54")
g(fill="none", stroke="#aaa", stroke-width="2", stroke-miterlimit="10")
path(d="M29 47H5l-4-4v-2h24l2 2h2M29 3H10C8.344 3 7 4.343 7 6v32M35 51h24l4-4v-2H39l-2 2h-2M35 7h19c1.656 0 3 1.343 3 3v32M32 34v20M32 20v8M32 0v14")
g
path(d="M32 31c-3.866 0-7-3.134-7-7M32 17c3.866 0 7 3.134 7 7M32 31h8M24 17h8M36 35l4-4-4-4M28 21l-4-4 4-4")
path(d="M29 37H11V7h18M35 11h18v30H35")
section.page-card.dark
.page-card-side
h2.page-card-title Texture Browser
.page-card-summary
p.
Access the <a href="https://cloud.blender.org/p/textures/">Blender Cloud Textures</a>
library from within Blender using our exclusive add-on.
Create, manage and share <em>your own</em> texture libraries!
a.page-card-cta.watch-video(
href="https://www.youtube.com/watch?v=-srXYv2Osjw",
data-youtube-id="-srXYv2Osjw")
i.pi-play
| Watch Video
.page-card-side
.page-card-icon
svg(xmlns="http://www.w3.org/2000/svg",
width="64", height="60",
viewBox="0 0 64 60")
g(fill="#aaa")
path(d="M32 60c-.188 0-.377-.053-.542-.16l-31-20C.173 39.656 0 39.34 0 39s.173-.656.458-.84l31-20c.33-.213.754-.213 1.084 0l31 20c.285.184.458.5.458.84s-.173.656-.458.84l-31 20c-.165.107-.354.16-.542.16zM2.845 39L32 57.81 61.155 39 32 20.19 2.845 39z")
path(d="M32 51c-.188 0-.377-.053-.542-.16l-31-20C.173 30.656 0 30.34 0 30s.173-.656.458-.84l31-20c.33-.213.754-.213 1.084 0l31 20c.285.184.458.5.458.84s-.173.656-.458.84l-31 20c-.165.107-.354.16-.542.16zM2.845 30L32 48.81 61.155 30 32 11.19 2.845 30z")
path(d="M32 42c-.188 0-.377-.053-.542-.16l-31-20C.173 21.656 0 21.34 0 21s.173-.656.458-.84l31-20c.33-.213.754-.213 1.084 0l31 20c.285.184.458.5.458.84s-.173.656-.458.84l-31 20c-.165.107-.354.16-.542.16zM2.845 21L32 39.81 61.155 21 32 2.19 2.845 21z")
path(d="M31 27h2v2h-2zm0-4h2v2h-2zm0 8h2v2h-2zm4-4h2v2h-2zm4 2h2v2h-2zm-16 0h2v2h-2zm12 2h2v2h-2zm-8-4h2v2h-2zm0 4h2v2h-2zm4 4h2v2h-2zm31 15h2v2h-2zm0-4h2v2h-2zm0 8h2v2h-2zm0 4h2v2h-2zm0-54h2v2h-2zm0-4h2v2h-2zm0 8h2v2h-2zm0 4h2v2h-2zM0 50h2v2H0zm0-4h2v2H0zm0 8h2v2H0zm0 4h2v2H0zM0 4h2v2H0zm0-4h2v2H0zm4 0h2v2H4zm4 0h2v2H8zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm22 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zM4 58h2v2H4zm4 0h2v2H8zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm22 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zM0 8h2v2H0zm0 4h2v2H0z")
section.page-card.dark.right
.page-card-side
h2.page-card-title Image Sharing
.page-card-summary.
Got a nice render, a Blender oddity, a cool screenshot?
Share it instantly from within Blender to the Cloud, to the world!
a.page-card-cta(
href="https://cloud.blender.org/blog/introducing-image-sharing")
| Learn More
.page-card-side
.page-card-icon
svg(xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64")
g(fill="none",
stroke="#aaa",
stroke-width="2",
stroke-linejoin="round",
stroke-miterlimit="10")
path(d="M1 1h62v62H1zM4 59h2M8 59h2M12 59h2M60 49H48M46 49H27M60 53H40")
path(d="M5 5h54v40H5z")
path(d="M9 45v-3c0-1.656 1.344-3 3-3h6c1.656 0 3 1.344 3 3v3M29 45v-3c0-1.656 1.344-3 3-3h6c1.656 0 3 1.344 3 3v3M13 45v-3M17 45v-3M33 45v-3M37 45v-3M22 31h-5c-2.762 0-5 2.238-5 5v3M38 39v-3c0-2.762-2.238-5-5-5h-5M31 20c0 3.313-1 9-6 9s-6-5.687-6-9c0-1 0-5 6-5s6 4 6 5z")
path(d="M29 27l-2 8h-4l-2-8M18 31c-4-3-5-9-5-9l6-3M32 31c4-3 5-9 5-9l-6-3M59 24L44 9l-8 8M44 9l8 36")
circle(cx="12", cy="12", r="3")
section.page-card.services-attract
.page-card-side
h2.page-card-title
| Attract
small (soon)
.page-card-summary.
Production-management software for your film, game, or commercial projects.
.page-card-side
.page-card-icon
svg(xmlns='http://www.w3.org/2000/svg', width='56', height='64', viewbox='0 0 56 64')
path(fill='#aaa', d='M16 32C7.178 32 0 24.822 0 16S7.178 0 16 0s16 7.178 16 16-7.178 16-16 16zm0-30C8.28 2 2 8.28 2 16s6.28 14 14 14 14-6.28 14-14S23.72 2 16 2z')
path(fill='#aaa', d='M16 56c-2.757 0-5-2.243-5-5V31h2v20c0 1.654 1.346 3 3 3s3-1.346 3-3V31h2v20c0 2.757-2.243 5-5 5z')
path(fill='#aaa', d='M15 40h2v2h-2zM15 44h2v2h-2zM15 48h2v2h-2zM46.414 64H15v-6h2v4h28.586L54 53.586V8H32V6h24v48.414')
path(fill='#aaa', d='M47 63h-2V53h10v2h-8M24 24H8V8h12v2H10v12h12v-6h2')
path(fill='#aaa', d='M16 19.414l-3.707-3.707 1.414-1.414L16 16.586l6.293-6.293 1.414 1.414M29 39h-6v-6h6v6zm-4-2h2v-2h-2v2zM29 47h-6v-6h6v6zm-4-2h2v-2h-2v2zM29 55h-6v-6h6v6zm-4-2h2v-2h-2v2zM31 33h19v2H31zM31 37h19v2H31zM31 41h19v2H31zM31 45h19v2H31zM31 53h10v2H31zM31 49h19v2H31z')
g
path(fill='#aaa', d='M50 29H31v-2h17v-8H34v-2h16')
g
path(fill='#aaa', d='M48 13h2v2h-2z')
g
path(fill='#aaa', d='M44 13h2v2h-2z')
g
path(fill='#aaa', d='M40 13h2v2h-2z')
| {% if not current_user.has_role('subscriber') %}
section.page-card.subscribe(
style="background-image: url({{ url_for('static', filename='assets/img/backgrounds/background_services.jpg')}});background-size: cover")
.page-card-side
h2.page-card-title
| All of this, plus hours of training and production assets.
.page-card-summary
| Join us for only $9.90/month!
a.page-card-cta(
href="https://store.blender.org/product/membership/")
| Subscribe Now
| {% endif %}
| {% endblock %}
| {% block footer_scripts %}
script.
// Click anywhere in the page to hide the overlay
function hideOverlay() {
$('#page-overlay.video').removeClass('active');
$('#page-overlay.video .video-embed').html('');
}
$(document).click(function() {
hideOverlay();
});
$(document).keyup(function(e) {
if (e.keyCode == 27) {
hideOverlay();
}
});
$('a.watch-video').click(function(e){
e.preventDefault();
e.stopPropagation();
$('#page-overlay.video').addClass('active');
var videoId = $(this).attr('data-youtube-id');
$('#page-overlay .video-embed').html('<iframe src="https://www.youtube.com/embed/' + videoId +'?rel=0&amp;showinfo=0;autoplay=1" frameborder="0" allowfullscreen></iframe>')
});
| {% endblock %}

120
src/templates/stats.jade Normal file
View File

@@ -0,0 +1,120 @@
| {% extends 'layout.html' %}
| {% block page_title %}Stats{% endblock %}
| {% block body %}
.container
#stats-container.page-content
.row
.col-md-6
.box
span.stats__graph-title.income
span.stats__graph-title-amount
small &dollar;
| 20307
span.stats__graph-title-label Monthly Income
#site-stats.stats__graph
.col-md-6
.box
span.stats__graph-title subscribers
span.stats__graph-title-amount 1807
span.stats__graph-title-label Active Subscribers
#site-stats2.stats__graph
hr
.row
.col-md-12
.box
.row
.col-md-6.text-left
p.
The Blender Cloud is our Open Production platform - a hub for creating and sharing open content and training online. The Blender Institute projects - developers and artists who work on compelling technical creative targets - are made possible thanks to the support of subscribers to the Cloud.
p.
We created this page to share with you the numbers that make the Cloud.
p.
Thank you very much for your support.
<br/>
The Blender Institute team
.col-md-6 stats__join
a(href="https://cloud.blender.org/join")
h3.
Get a subscription
h3.
Now only &dollar;10 per month
.btn.btn-default
| Join the Cloud
hr
.row.stats__data
.col-md-12
.row
.col-md-3 stats__data-type_money
.box
h2.
<small>&dollar;</small> 20307
h3.
<small>Monthly Income</small>
i.fa.fa-money backicon
</div>
</div>
.col-md-2 stats__data-type_money
.box
i.fa.fa-users backicon
h2.
1807
h3.
<small>Active Subscribers</small>
</div>
</div>
.col-md-2 stats__data-type_quantity
.box
i.fa.fa-film backicon
h2.
245
h3.
<small>Hours of Video</small>
</div>
</div>
.col-md-2 stats__data-type_quantity
.box
i.fa.fa-database backicon
h2.
94
h3.
<small>Gigabytes of Data</small>
</div>
</div>
.col-md-3 stats__data-type_quantity
.box
i.fa.fa-cloud-download backicon
h2.
3641
h3.
<small>Downloadable Assets</small>
| {% endblock %}

25
src/templates/upload.jade Normal file
View File

@@ -0,0 +1,25 @@
| {% extends 'layout.html' %}
| {% block head %}
| {{ super() }}
// blueimp Gallery styles
link(rel="stylesheet", href="{{ url_for('static_pillar', filename='assets/css/blueimp/blueimp-gallery.min.css') }}")
// CSS to style the file input field as button and adjust the Bootstrap progress bars
link(rel="stylesheet", href="{{ url_for('static_pillar', filename='jquery-file-upload/css/jquery.fileupload.css') }}")
link(rel="stylesheet", href="{{ url_for('static_pillar', filename='jquery-file-upload/css/jquery.fileupload-ui.css') }}")
| {% endblock %}
| {% block body %}
.container
#project-container(style="background-color:white;padding:20px")
| {% include '_macros/_file_uploader_form.html' %}
| {% endblock %}
| {% block footer_scripts %}
| {% include '_macros/_file_uploader_javascript.html' %}
| {% endblock %}

View File

@@ -0,0 +1,4 @@
#node-add-container
| {% include '_macros/_file_uploader_form.html' %}
| {% include '_macros/_file_uploader_javascript.html' %}

View File

@@ -0,0 +1,78 @@
| {% block body %}
#user-edit-container
#user-edit-header
.user-edit-name {{user.full_name}}
.user-edit-username {{user.username}}
.user-edit-email {{user.email}}
form(
id="user-edit-form",
method="POST",
enctype="multipart/form-data",
action="{{url_for('users.users_edit', user_id=user._id)}}")
| {% for field in form %}
| {% if field.name == 'csrf_token' %}
| {{ field }}
| {% else %}
| {% if field.type == 'HiddenField' %}
| {{ field }}
| {% else %}
.form-group(class="{{field.name}}{% if field.errors %} error{% endif %}")
| {{ field.label }}
| {{ field(class='form-control') }}
| {% if field.errors %}
ul.error
| {% for error in field.errors %}
li {{ error }}
| {% endfor %}
| {% endif %}
| {% endif %}
| {% endif %}
| {% endfor %}
a#button-cancel.btn.btn-default(href="#", data-user-id='{{user._id}}') Cancel
input#submit_edit_user.btn.btn-default(
data-user-id="{{user._id}}",
type="submit" value="Submit")
#user-edit-notification
script(type="text/javascript").
$('#roles').select2();
$('#user-edit-form').submit(function(e){
e.preventDefault();
//- console.log($(this).serialize());
$.post($(this).attr('action'), $(this).serialize())
.done(function(data){
$('#user-edit-notification').addClass('success').html('Success!');
})
.fail(function(data){
$('#user-edit-notification').addClass('fail').html('Houston!');
});
//- $("#user-edit-form").submit();
});
$('#button-cancel').click(function(e){
$('#user-container').html('')
});
| {% endblock %}

View File

@@ -0,0 +1,120 @@
| {% extends 'layout.html' %}
| {% block page_title %}Users{% endblock %}
| {% block body %}
#search-container
#search-sidebar
input.search-field(
type="text",
name="q",
id="q",
autocomplete="off",
spellcheck="false",
autocorrect="false",
placeholder="Search by Full Name, Username...")
.search-list-filters
#accordion.panel-group.accordion(role="tablist", aria-multiselectable="true")
#facets
#pagination
.search-list-stats
#stats
#search-list
#hits
#search-details
#search-hit-container
| {% raw %}
// Facet template
script(type="text/template", id="facet-template")
.panel.panel-default
a(data-toggle='collapse', data-parent='#accordion', href='#filter_{{ facet }}', aria-expanded='true', aria-controls='filter_{{ facet }}')
.panel-heading(role='tab')
.panel-title {{ title }}
.panel-collapse.collapse.in(id='filter_{{ facet }}', role='tabpanel', aria-labelledby='headingOne')
.panel-body
| {{#values}}
a.facet_link.toggleRefine(
class='{{#refined}}refined{{/refined}}',
data-facet='{{ facet }}',
data-value='{{ value }}',
href='#')
span
| {{ label }}
small.facet_count.text-muted.pull-right {{ count }}
| {{/values}}
// Hit template
script(type="text/template", id="hit-template")
.search-hit.users(data-user-id='{{ objectID }}')
.search-hit-name
| {{{ _highlightResult.full_name.value }}}
small ({{{ username }}})
.search-hit-roles
| {{{ roles }}}
// Pagination template
script(type="text/template", id="pagination-template")
ul.search-pagination.
<li {{^prev_page}}class="disabled"{{/prev_page}}><a href="#" {{#prev_page}} class="gotoPage" data-page="{{ prev_page }}" {{/prev_page}}><i class="pi-angle-left"></i></a></li>
{{#pages}}
<li class="{{#current}}active{{/current}}{{#disabled}}disabled{{/disabled}}"><a href="#" {{^disabled}} class="gotoPage" data-page="{{ number }}" {{/disabled}}>{{ number }}</a></li>
{{/pages}}
<li {{^next_page}}class="disabled"{{/next_page}}><a href="#" {{#next_page}} class="gotoPage" data-page="{{ next_page }}" {{/next_page}}><i class="pi-angle-right"></i></a></li>
// Stats template
script(type="text/template", id="stats-template")
h5 {{ nbHits }} result{{#nbHits_plural}}s{{/nbHits_plural}}
span ({{ processingTimeMS }}ms)
| {% endraw %}
| {% endblock %}
| {% block footer_scripts %}
script().
var APPLICATION_ID = '{{config.ALGOLIA_USER}}';
var SEARCH_ONLY_API_KEY = '{{config.ALGOLIA_PUBLIC_KEY}}';
var INDEX_NAME = '{{config.ALGOLIA_INDEX_USERS}}';
var sortByCountDesc = null;
var FACET_CONFIG = [
{ name: 'roles', title: 'Roles', disjunctive: false, sortFunction: sortByCountDesc },
];
script(src="//cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js")
script(src="//cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js")
script(src="//cdn.jsdelivr.net/hogan.js/3.0.0/hogan.common.js")
script(src="{{ url_for('static_pillar', filename='assets/js/algolia_search.min.js') }}")
script(type='text/javascript', src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.select2.min.js') }}")
script(type="text/javascript").
if (typeof Ps !== 'undefined'){
Ps.initialize(document.getElementById('hits'), {suppressScrollX: true});
}
function displayUser(userId) {
var url = '/u/' + userId + '/edit?embed=1';
$.get(url, function(dataHtml){
$('#search-hit-container').html(dataHtml);
});
}
$('body').on('click', '.search-hit', function(){
displayUser($(this).data('user-id'));
});
// Remove focus from search input so that the click event bound to .user-hit
// can be fired on the first click.
$('#search-list').hover(function(){
$('#q').blur();
});
| {% endblock %}

Some files were not shown because too many files have changed in this diff Show More