chore: insight search: split file & add untitled item support

This commit is contained in:
ppoffice 2016-04-07 23:51:53 +08:00
parent c3cf7c52e9
commit 029442f7c4
12 changed files with 218 additions and 189 deletions

View File

@ -34,4 +34,5 @@ insight:
pages: 'Pages'
categories: 'Categories'
tags: 'Tags'
untitled: '(Untitled)'

View File

@ -34,3 +34,4 @@ insight:
pages: 'Pages'
categories: 'Categorias'
tags: 'Etiquetas'
untitled: '(Untitled)'

View File

@ -33,3 +33,4 @@ insight:
pages: 'Pages'
categories: 'Catégories'
tags: 'Tags'
untitled: '(Untitled)'

View File

@ -32,3 +32,4 @@ insight:
pages: 'Pages'
categories: 'kategori'
tags: 'tag'
untitled: '(Untitled)'

View File

@ -33,3 +33,4 @@ insight:
pages: 'Pages'
categories: 'カテゴリ'
tags: 'タグ'
untitled: '(Untitled)'

View File

@ -34,3 +34,4 @@ insight:
pages: 'Pages'
categories: '카테고리'
tags: '태그'
untitled: '(Untitled)'

View File

@ -34,3 +34,4 @@ insight:
pages: 'Pages'
categories: 'категории'
tags: 'тэги'
untitled: '(Untitled)'

View File

@ -34,3 +34,4 @@ insight:
pages: '页面'
categories: '分类'
tags: '标签'
untitled: '(未命名)'

View File

@ -33,3 +33,4 @@ insight:
pages: 'Pages'
categories: '分類'
tags: '標籤'
untitled: '(Untitled)'

View File

@ -11,192 +11,19 @@
</div>
</div>
<script>
(function ($) {
$main = $('.ins-search');
$container = $('.ins-section-container');
$main.parent().remove('.ins-search');
$('body').append($main);
$(document).on('click focus', '.search-form-input', function () {
$main.addClass('show');
$main.find('.ins-search-input').focus();
}).on('click', '.ins-search-item', function () {
location.href=$(this).attr('data-url');
}).on('click', '.ins-close', function () {
$main.removeClass('show');
});
function section (title) {
return $('<section>').addClass('ins-section')
.append($('<header>').addClass('ins-section-header').text(title));
}
function searchItem (icon, title, slug, preview, url) {
return $('<div>').addClass('ins-selectable').addClass('ins-search-item')
.append($('<header>').append($('<i>').addClass('fa').addClass('fa-' + icon)).append(title)
.append(slug ? $('<span>').addClass('ins-slug').text(slug) : null))
.append(preview ? $('<p>').addClass('ins-search-preview').text(preview) : null)
.attr('data-url', url);
}
function sectionFactory (type, array) {
var sectionTitle;
var $searchItems;
if (array.length == 0) return null;
switch (type) {
case 'POSTS':
case 'PAGES':
sectionTitle = type == 'POSTS' ? '<%= __("insight.posts") %>' : '<%= __("insight.pages") %>';
$searchItems = array.map(function (item) {
// Use config.root instead of permalink to fix url issue
return searchItem('file', item.title, null, item.text.slice(0, 150), '<%= config.root %>' + item.path);
});
break;
case 'CATEGORIES':
case 'TAGS':
sectionTitle = type == 'CATEGORIES' ? '<%= __("insight.categories") %>' : '<%= __("insight.tags") %>';
$searchItems = array.map(function (item) {
return searchItem(type == 'CATEGORIES' ? 'folder' : 'tag', item.name, item.slug, null, item.permalink);
});
break;
default:
return null;
}
return section(sectionTitle).append($searchItems);
}
function extractToSet (json, key) {
var values = {};
var entries = json.pages.concat(json.posts);
entries.forEach(function (entry) {
if (entry[key]) {
entry[key].forEach(function (value) {
values[value.name] = value;
});
}
});
var result = [];
for (var key in values) {
result.push(values[key]);
}
return result;
}
function parseKeywords (keywords) {
return keywords.split(' ').filter(function (keyword) {
return !!keyword;
}).map(function (keyword) {
return keyword.toUpperCase();
});
}
/**
* Judge if a given post/page/category/tag contains all of the keywords.
* @param Object obj Object to be weighted
* @param Array<String> fields Object's fields to find matches
*/
function filter (keywords, obj, fields) {
var result = false;
var keywordArray = parseKeywords(keywords);
var containKeywords = keywordArray.filter(function (keyword) {
var containFields = fields.filter(function (field) {
if (!obj.hasOwnProperty(field))
return false;
if (obj[field].toUpperCase().indexOf(keyword) > -1)
return true;
});
if (containFields.length > 0)
return true;
return false;
});
return containKeywords.length == keywordArray.length;
}
function filterFactory (keywords) {
return {
POST: function (obj) {
return filter(keywords, obj, ['title', 'text']);
},
PAGE: function (obj) {
return filter(keywords, obj, ['title', 'text']);
},
CATEGORY: function (obj) {
return filter(keywords, obj, ['name', 'slug']);
},
TAG: function (obj) {
return filter(keywords, obj, ['name', 'slug']);
}
};
}
/**
* Calculate the weight of a matched post/page/category/tag.
* @param Object obj Object to be weighted
* @param Array<String> fields Object's fields to find matches
* @param Array<Integer> weights Weight of every field
*/
function weight (keywords, obj, fields, weights) {
var value = 0;
parseKeywords(keywords).forEach(function (keyword) {
var pattern = new RegExp(keyword, 'img'); // Global, Multi-line, Case-insensitive
fields.forEach(function (field, index) {
if (obj.hasOwnProperty(field)) {
var matches = obj[field].match(pattern);
value += matches ? matches.length * weights[index] : 0;
}
});
});
return value;
}
function weightFactory (keywords) {
return {
POST: function (obj) {
return weight(keywords, obj, ['title', 'text'], [3, 1]);
},
PAGE: function (obj) {
return weight(keywords, obj, ['title', 'text'], [3, 1]);
},
CATEGORY: function (obj) {
return weight(keywords, obj, ['name', 'slug'], [1, 1]);
},
TAG: function (obj) {
return weight(keywords, obj, ['name', 'slug'], [1, 1]);
}
};
}
function search (json, keywords) {
var WEIGHTS = weightFactory(keywords);
var FILTERS = filterFactory(keywords);
var posts = json.posts;
var pages = json.pages;
var tags = extractToSet(json, 'tags');
var categories = extractToSet(json, 'categories');
return {
posts: posts.filter(FILTERS.POST).sort(function (a, b) { return WEIGHTS.POST(b) - WEIGHTS.POST(a); }).slice(0, 5),
pages: pages.filter(FILTERS.PAGE).sort(function (a, b) { return WEIGHTS.PAGE(b) - WEIGHTS.PAGE(a); }).slice(0, 5),
categories: categories.filter(FILTERS.CATEGORY).sort(function (a, b) { return WEIGHTS.CATEGORY(b) - WEIGHTS.CATEGORY(a); }).slice(0, 5),
tags: tags.filter(FILTERS.TAG).sort(function (a, b) { return WEIGHTS.TAG(b) - WEIGHTS.TAG(a); }).slice(0, 5)
};
}
function searchResultToDOM (searchResult) {
$container.empty();
for (var key in searchResult) {
$container.append(sectionFactory(key.toUpperCase(), searchResult[key]));
}
}
$.getJSON('<%- url_for("/content.json")%>', function (json) {
if (location.hash.trim() == '#ins-search') {
$main.addClass('show');
}
$('.ins-search-input').on('input', function () {
var keywords = $(this).val();
searchResultToDOM(search(json, keywords));
});
$('.ins-search-input').trigger('input');
});
})(jQuery);
</script>
(function (window) {
var INSIGHT_CONFIG = {
TRANSLATION: {
POSTS: '<%= __("insight.posts") %>',
PAGES: '<%= __("insight.pages") %>',
CATEGORIES: '<%= __("insight.categories") %>',
TAGS: '<%= __("insight.tags") %>',
UNTITLED: '<%= __("insight.untitled") %>',
},
ROOT_URL: '<%= config.root %>',
CONTENT_URL: '<%- url_for("/content.json")%>',
};
window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>
<%- js('js/insight') %>

View File

@ -40,7 +40,9 @@ $ins-full-screen
border: none
outline: none
font-size: 16px
box-shadow: none
font-weight: 200
border-radius: 0
background: white
line-height: 20px
box-sizing: border-box

191
source/js/insight.js Normal file
View File

@ -0,0 +1,191 @@
/**
* Insight search plugin
* @author PPOffice { @link https://github.com/ppoffice }
*/
(function ($, CONFIG) {
$main = $('.ins-search');
$container = $('.ins-section-container');
$main.parent().remove('.ins-search');
$('body').append($main);
$(document).on('click focus', '.search-form-input', function () {
$main.addClass('show');
$main.find('.ins-search-input').focus();
}).on('click', '.ins-search-item', function () {
location.href=$(this).attr('data-url');
}).on('click', '.ins-close', function () {
$main.removeClass('show');
});
function section (title) {
return $('<section>').addClass('ins-section')
.append($('<header>').addClass('ins-section-header').text(title));
}
function searchItem (icon, title, slug, preview, url) {
return $('<div>').addClass('ins-selectable').addClass('ins-search-item')
.append($('<header>').append($('<i>').addClass('fa').addClass('fa-' + icon)).append(title != null && title != '' ? title : CONFIG.TRANSLATION['UNTITLED'])
.append(slug ? $('<span>').addClass('ins-slug').text(slug) : null))
.append(preview ? $('<p>').addClass('ins-search-preview').text(preview) : null)
.attr('data-url', url);
}
function sectionFactory (type, array) {
var sectionTitle;
var $searchItems;
if (array.length == 0) return null;
sectionTitle = CONFIG.TRANSLATION[type];
switch (type) {
case 'POSTS':
case 'PAGES':
$searchItems = array.map(function (item) {
// Use config.root instead of permalink to fix url issue
return searchItem('file', item.title, null, item.text.slice(0, 150), CONFIG.ROOT_URL + item.path);
});
break;
case 'CATEGORIES':
case 'TAGS':
$searchItems = array.map(function (item) {
return searchItem(type == 'CATEGORIES' ? 'folder' : 'tag', item.name, item.slug, null, item.permalink);
});
break;
default:
return null;
}
return section(sectionTitle).append($searchItems);
}
function extractToSet (json, key) {
var values = {};
var entries = json.pages.concat(json.posts);
entries.forEach(function (entry) {
if (entry[key]) {
entry[key].forEach(function (value) {
values[value.name] = value;
});
}
});
var result = [];
for (var key in values) {
result.push(values[key]);
}
return result;
}
function parseKeywords (keywords) {
return keywords.split(' ').filter(function (keyword) {
return !!keyword;
}).map(function (keyword) {
return keyword.toUpperCase();
});
}
/**
* Judge if a given post/page/category/tag contains all of the keywords.
* @param Object obj Object to be weighted
* @param Array<String> fields Object's fields to find matches
*/
function filter (keywords, obj, fields) {
var result = false;
var keywordArray = parseKeywords(keywords);
var containKeywords = keywordArray.filter(function (keyword) {
var containFields = fields.filter(function (field) {
if (!obj.hasOwnProperty(field))
return false;
if (obj[field].toUpperCase().indexOf(keyword) > -1)
return true;
});
if (containFields.length > 0)
return true;
return false;
});
return containKeywords.length == keywordArray.length;
}
function filterFactory (keywords) {
return {
POST: function (obj) {
return filter(keywords, obj, ['title', 'text']);
},
PAGE: function (obj) {
return filter(keywords, obj, ['title', 'text']);
},
CATEGORY: function (obj) {
return filter(keywords, obj, ['name', 'slug']);
},
TAG: function (obj) {
return filter(keywords, obj, ['name', 'slug']);
}
};
}
/**
* Calculate the weight of a matched post/page/category/tag.
* @param Object obj Object to be weighted
* @param Array<String> fields Object's fields to find matches
* @param Array<Integer> weights Weight of every field
*/
function weight (keywords, obj, fields, weights) {
var value = 0;
parseKeywords(keywords).forEach(function (keyword) {
var pattern = new RegExp(keyword, 'img'); // Global, Multi-line, Case-insensitive
fields.forEach(function (field, index) {
if (obj.hasOwnProperty(field)) {
var matches = obj[field].match(pattern);
value += matches ? matches.length * weights[index] : 0;
}
});
});
return value;
}
function weightFactory (keywords) {
return {
POST: function (obj) {
return weight(keywords, obj, ['title', 'text'], [3, 1]);
},
PAGE: function (obj) {
return weight(keywords, obj, ['title', 'text'], [3, 1]);
},
CATEGORY: function (obj) {
return weight(keywords, obj, ['name', 'slug'], [1, 1]);
},
TAG: function (obj) {
return weight(keywords, obj, ['name', 'slug'], [1, 1]);
}
};
}
function search (json, keywords) {
var WEIGHTS = weightFactory(keywords);
var FILTERS = filterFactory(keywords);
var posts = json.posts;
var pages = json.pages;
var tags = extractToSet(json, 'tags');
var categories = extractToSet(json, 'categories');
return {
posts: posts.filter(FILTERS.POST).sort(function (a, b) { return WEIGHTS.POST(b) - WEIGHTS.POST(a); }).slice(0, 5),
pages: pages.filter(FILTERS.PAGE).sort(function (a, b) { return WEIGHTS.PAGE(b) - WEIGHTS.PAGE(a); }).slice(0, 5),
categories: categories.filter(FILTERS.CATEGORY).sort(function (a, b) { return WEIGHTS.CATEGORY(b) - WEIGHTS.CATEGORY(a); }).slice(0, 5),
tags: tags.filter(FILTERS.TAG).sort(function (a, b) { return WEIGHTS.TAG(b) - WEIGHTS.TAG(a); }).slice(0, 5)
};
}
function searchResultToDOM (searchResult) {
$container.empty();
for (var key in searchResult) {
$container.append(sectionFactory(key.toUpperCase(), searchResult[key]));
}
}
$.getJSON(CONFIG.CONTENT_URL, function (json) {
if (location.hash.trim() == '#ins-search') {
$main.addClass('show');
}
$('.ins-search-input').on('input', function () {
var keywords = $(this).val();
searchResultToDOM(search(json, keywords));
});
$('.ins-search-input').trigger('input');
});
})(jQuery, window.INSIGHT_CONFIG);