├── .gitignore ├── ReadMe.markdown └── template ├── 404.jade ├── album ├── index.jade ├── js │ ├── album.coffee │ ├── album.js │ └── gmaps.js ├── loading.gif └── style.scss ├── archive+tags.jade ├── base.jade ├── blog ├── script.coffee └── style.scss ├── categories.jade ├── feed.jade ├── include ├── comments.jade └── paginator.jade ├── index+category+tag.jade ├── interface.json ├── markdown+pages.jade ├── post.jade └── template.jade /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | /template/admin/* 3 | -------------------------------------------------------------------------------- /ReadMe.markdown: -------------------------------------------------------------------------------- 1 | This is the default template frame for all the sites on FarBox. 2 | 3 | If you do not want this frame working for your site ,just turn the `inherit` off by modify your site configs. -------------------------------------------------------------------------------- /template/404.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | meta(name="viewport", content="width=device-width") 4 | title HTTP 404 5 | style(type="text/css") 6 | * { 7 | margin: 0; 8 | padding: 0; 9 | } 10 | html, body { 11 | width: 100%; 12 | height: 100%; 13 | font: 200 25px/1.5 Helvetica Neue, Helvetica, Sans-Serif; 14 | -webkit-font-smoothing: antialiased; 15 | position: relative; 16 | } 17 | h1 { 18 | font-size: 30px; 19 | font-weight: 400; 20 | margin: 0 0 10px; 21 | color: #555; 22 | } 23 | h1 a{ 24 | color: #555; 25 | } 26 | p, a { 27 | color: #999; 28 | text-decoration: none; 29 | display: block; 30 | } 31 | p { 32 | margin-left: 100px;; 33 | font-size: 0.8em; 34 | margin-top: 1.0em; 35 | } 36 | a:hover { 37 | color: #5ba2ee; 38 | } 39 | .center { 40 | margin: 0 0 0 100px; 41 | position: relative; 42 | top: 45%; 43 | -webkit-transform: translateY(-50%); 44 | -moz-transform: translateY(-50%); 45 | -ms-transform: translateY(-50%); 46 | -o-transform: translateY(-50%); 47 | transform: translateY(-50%); 48 | } 49 | .back { 50 | position: fixed; 51 | right: 15px; 52 | bottom: 10px; 53 | font-size: 0.7em; 54 | 55 | } 56 | .back a { 57 | color: #acacac; 58 | } 59 | @media only screen and (max-width: 320px) { 60 | body { 61 | padding: 30px; 62 | } 63 | .center { 64 | margin-left: 0; 65 | } 66 | } 67 | body 68 | .center 69 | h1 70 | a(href="/") Page Not Found. 71 | p 72 | if site.configs.template_inherit==False 73 | a(href="#") 74 | small > 75 | |   Maybe you should not set `Inherits from Default Template` to `False`! 76 | else 77 | a(href="http://help.farbox.com/read/how-to-make-a-template", target="_blank") 78 | small > 79 | |   custom this page by '/template/404.html' 80 | 81 | p.back 82 | small back to 83 | a(href="/")  Home 84 | -------------------------------------------------------------------------------- /template/album/index.jade: -------------------------------------------------------------------------------- 1 | html 2 | is_index = request.path.strip('/')=='album' or request.path.startswith('/album/page') 3 | head 4 | if is_index 5 | title= 'Albums' 6 | else 7 | title= folder.title or site.title 8 | load("pure /template/album/style.scss") 9 | body 10 | if is_index 11 | .body_wrap 12 | h1 Albums 13 | .home 14 | a(href="/") Home 15 | ul.albums.pure-g-r 16 | if request.path.strip('/')=='album' 17 | a_root_photo = get_data(types='image', min_images_count=1, level=1, limit=1, with_page=False) 18 | if a_root_photo 19 | li.album.pure-u-1-3 20 | a(href="/album/~root") 21 | .img_container 22 | img(src="{{a_root_photo.url}}?width=120&height=120&fixed=true") 23 | span.title= site.title 24 | for album in get_data(types='folder', min_images_count=1, level=1, limit=60) 25 | li.album.pure-u-1-3 26 | a(href='/album/{{album.path}}') 27 | .img_container 28 | img(src="{{album.url}}?width=120&height=120&fixed=true") 29 | span.title= album.title 30 | include include/paginator.jade 31 | else 32 | //for album viewer 33 | if request.path.startswith('/album/~root') 34 | sub_images = get_data(types='image', min_images_count=1, level=1, limit=60, pager_name='this_album', sort='asc') 35 | +bread_nav('/~root', prefix='/album', prefix_name='Albums>', split_by='>') 36 | else 37 | sub_images = get_data(types='folder+image', path=folder.path, min_images_count=1, level=1, limit=60, pager_name='this_album', sort='asc') 38 | +bread_nav(folder.path, prefix='/album', prefix_name='Albums>', split_by='>') 39 | 40 | .boxes.pure-g-r 41 | .map-box.pure-u-2-3 42 | .box-container 43 | #map 44 | 45 | for im in sub_images 46 | .photo-box.pure-u-1-3 47 | .box-container 48 | if im.content_type == 'folder' 49 | a(href='/album/{{im.path}}') 50 | if im.images_count == 1 51 | span.sub_folder= '1 photo' 52 | else 53 | span.sub_folder= '%s photos'%im.images_count 54 | img(src="{{ im.url }}?width=320&height=214&fixed=true", alt=im.title) 55 | .caption 56 | span= im.title 57 | else 58 | a.fb-image(href="#") 59 | img(src="{{ im.url }}?width=320&height=214&fixed=true", alt=im.title) 60 | .caption 61 | b= im.title 62 | span.image-date= im.date.format('%Y-%m-%d %H:%M') 63 | 64 | paginator = get_paginator('this_album') 65 | include include/paginator.jade 66 | 67 | div(data-bind="visible: current_window(), template: {name: current_window()}") 68 | script#image-viewer(type='text/html') 69 | .image-viewer 70 | .wrap 71 | .head 72 | .caption(data-bind="text: current_image().title") 73 | .image-info(data-bind="visible:show_exif(),foreach:exif_list") 74 | span.exif-field 75 | span(data-bind="text:k") 76 | |: 77 | span(data-bind="text:v") 78 | .commands 79 | a(href="#", data-bind="click: show_full") 80 | i.fa.fa-external-link-square 81 | a(href="#", data-bind="visible:has_exif(),click: function(){show_exif(!show_exif())}") EXIF 82 | a(href="#", data-bind="click: function(){current_window(null)}") 83 | i.fa.fa-times-circle-o  Close 84 | .connected 85 | a.pre(href="#", data-bind="click: pre, visible: has_pre()") 86 | i.fa.fa-arrow-left 87 | a.next(href="#", data-bind="click: next, visible: has_next()") 88 | i.fa.fa-arrow-right 89 | 90 | .body 91 | .image 92 | div(data-bind="click: next") 93 | img(data-bind="attr: {src: current_image().url, alt: current_image().title}", onload="$(this).addClass('loaded')") 94 | 95 | if not is_index 96 | load("fonts jquery#1.8.1 knockout") 97 | //load('http://maps.google.com/maps/api/js?sensor=true') 98 | //load('/template/album/js/gmaps.js') 99 | load("/template/album/js/album.coffee") 100 | 101 | script(type="text/javascript") 102 | var images = {{ sub_images.json}}; 103 | run_viewer(images); 104 | //draw_map(images); 105 | 106 | 107 | #footer 108 | .powered_by 109 | a(target='_blank', href="http://www.farbox.com") Powered By FarBox.com 110 | a(href="/admin") Admin 111 | span ©2012-2015 Z.R.E.Y Inc. 112 | -------------------------------------------------------------------------------- /template/album/js/album.coffee: -------------------------------------------------------------------------------- 1 | exports = this 2 | 3 | Viewer = (images) -> 4 | self = this 5 | @images = images 6 | @image_paths = (image.path for image in images) 7 | @image_doms = $('.fb-image') 8 | @image_doms.click -> 9 | current_index = $.inArray(this, self.image_doms) 10 | self.current_index(current_index) 11 | self.current_window('image-viewer') 12 | return false 13 | 14 | @current_window = ko.observable() 15 | @current_window.subscribe (show_window) => $(document.body).css {'overflow': if show_window then 'hidden' else 'auto'} 16 | @current_index = ko.observable() 17 | @has_exif = ko.observable(false) 18 | @current_image = ko.computed => 19 | if @current_index()? 20 | location.hash = @image_paths[@current_index()] 21 | return @images[@current_index()] 22 | @has_next = ko.computed => @current_index()? and @current_index() < @images.length-1 23 | @has_pre = ko.computed => @current_index()? and @current_index() > 0 24 | 25 | 26 | @exif_list = ko.computed => 27 | fields = {'model': 'Model', 'fn': 'Fn', 'exposure': 'Exposure', 'focal_length': 'Focal', 'iso': 'ISO'} 28 | @has_exif(false) 29 | exif_list = [] 30 | if @current_image() and @current_image().exif 31 | exif = @current_image().exif 32 | for info_name of exif 33 | exif_list.push(k:fields[info_name], v:exif[info_name]) if info_name of fields 34 | @has_exif(true) 35 | return exif_list 36 | return [] 37 | 38 | @next = => 39 | if @has_next() 40 | self.current_index(@current_index()+1) 41 | $('.image img').removeClass('loaded') 42 | 43 | @pre = => 44 | if @has_pre() 45 | self.current_index(@current_index()-1) 46 | $('.image img').removeClass('loaded') 47 | 48 | @show_exif = ko.observable(false) 49 | 50 | @show_full = => 51 | img_dom = $('.image-viewer .image img') 52 | if img_dom 53 | if img_dom.css('max-height') == '100%' # to fullscreen 54 | img_dom.css({'max-height': 'none'}) 55 | $('.icon-resize-full').removeClass('icon-resize-full').addClass('icon-resize-small') 56 | $('.image-viewer .wrap').css(width: '100%', 'margin-top': 0) 57 | $('.image-viewer .body').css('max-height': '100%') 58 | else 59 | img_dom.css({'max-height': '100%'}) 60 | $('.icon-resize-small').removeClass('icon-resize-small').addClass('icon-resize-full') 61 | $('.image-viewer .wrap').css(width: '80%', 'margin-top': '4%') 62 | $('.image-viewer .body').css('max-height': '80%') 63 | 64 | 65 | return this 66 | 67 | compute_gps = (image) -> 68 | if image and image.exif and image.exif.latitude and image.exif.longitude 69 | gps = {lat: image.exif.latitude, lng: image.exif.longitude, title: image.title} 70 | if 75 76 | markers = [] 77 | for image in images 78 | gps = compute_gps(image) 79 | if gps 80 | index = $.inArray(image, images) 81 | click_marker = (index)=> 82 | @viewer.current_index(index) 83 | @viewer.current_window('image-viewer') 84 | gps.click = click_marker.bind({}, index) 85 | markers.push(gps) 86 | 87 | if markers.length 88 | $('.map-box').css({display: 'inline-block'}) 89 | map = new GMaps {div: '#map', lat: markers[0].lat, lng: markers[0].lng} 90 | map.addMarkers(markers) 91 | map.fitZoom() 92 | 93 | 94 | @run_viewer = (images) -> 95 | $(document).ready -> 96 | viewer = new Viewer images 97 | exports.viewer = viewer 98 | 99 | default_hash = location.hash.replace('#', '') 100 | if default_hash in viewer.image_paths 101 | viewer.current_index($.inArray(default_hash, viewer.image_paths)) 102 | show_viewer = -> 103 | viewer.current_window('image-viewer') 104 | setTimeout(show_viewer, 100) 105 | 106 | ko.applyBindings viewer 107 | 108 | -------------------------------------------------------------------------------- /template/album/js/album.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.2 2 | (function() { 3 | var Viewer, compute_gps, exports, 4 | _this = this, 5 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 6 | 7 | exports = this; 8 | 9 | Viewer = function(images) { 10 | var image, self, 11 | _this = this; 12 | 13 | self = this; 14 | this.images = images; 15 | this.image_paths = (function() { 16 | var _i, _len, _results; 17 | 18 | _results = []; 19 | for (_i = 0, _len = images.length; _i < _len; _i++) { 20 | image = images[_i]; 21 | _results.push(image.path); 22 | } 23 | return _results; 24 | })(); 25 | this.image_doms = $('.fb-image'); 26 | this.image_doms.click(function() { 27 | var current_index; 28 | 29 | current_index = $.inArray(this, self.image_doms); 30 | self.current_index(current_index); 31 | self.current_window('image-viewer'); 32 | return false; 33 | }); 34 | this.current_window = ko.observable(); 35 | this.current_window.subscribe(function(show_window) { 36 | return $(document.body).css({ 37 | 'overflow': show_window ? 'hidden' : 'auto' 38 | }); 39 | }); 40 | this.current_index = ko.observable(); 41 | this.has_exif = ko.observable(false); 42 | this.current_image = ko.computed(function() { 43 | if (_this.current_index() != null) { 44 | location.hash = _this.image_paths[_this.current_index()]; 45 | return _this.images[_this.current_index()]; 46 | } 47 | }); 48 | this.has_next = ko.computed(function() { 49 | return (_this.current_index() != null) && _this.current_index() < _this.images.length - 1; 50 | }); 51 | this.has_pre = ko.computed(function() { 52 | return (_this.current_index() != null) && _this.current_index() > 0; 53 | }); 54 | this.exif_list = ko.computed(function() { 55 | var exif, exif_list, fields, info_name; 56 | 57 | fields = { 58 | 'model': 'Model', 59 | 'fn': 'Fn', 60 | 'exposure': 'Exposure', 61 | 'focal_length': 'Focal', 62 | 'iso': 'ISO' 63 | }; 64 | _this.has_exif(false); 65 | exif_list = []; 66 | if (_this.current_image() && _this.current_image().exif) { 67 | exif = _this.current_image().exif; 68 | for (info_name in exif) { 69 | if (info_name in fields) { 70 | exif_list.push({ 71 | k: fields[info_name], 72 | v: exif[info_name] 73 | }); 74 | } 75 | _this.has_exif(true); 76 | } 77 | return exif_list; 78 | } 79 | return []; 80 | }); 81 | this.next = function() { 82 | if (_this.has_next()) { 83 | self.current_index(_this.current_index() + 1); 84 | return $('.image img').removeClass('loaded'); 85 | } 86 | }; 87 | this.pre = function() { 88 | if (_this.has_pre()) { 89 | self.current_index(_this.current_index() - 1); 90 | return $('.image img').removeClass('loaded'); 91 | } 92 | }; 93 | this.show_exif = ko.observable(false); 94 | this.show_full = function() { 95 | var img_dom; 96 | 97 | img_dom = $('.image-viewer .image img'); 98 | if (img_dom) { 99 | if (img_dom.css('max-height') === '100%') { 100 | img_dom.css({ 101 | 'max-height': 'none' 102 | }); 103 | $('.icon-resize-full').removeClass('icon-resize-full').addClass('icon-resize-small'); 104 | $('.image-viewer .wrap').css({ 105 | width: '100%', 106 | 'margin-top': 0 107 | }); 108 | return $('.image-viewer .body').css({ 109 | 'max-height': '100%' 110 | }); 111 | } else { 112 | img_dom.css({ 113 | 'max-height': '100%' 114 | }); 115 | $('.icon-resize-small').removeClass('icon-resize-small').addClass('icon-resize-full'); 116 | $('.image-viewer .wrap').css({ 117 | width: '80%', 118 | 'margin-top': '4%' 119 | }); 120 | return $('.image-viewer .body').css({ 121 | 'max-height': '80%' 122 | }); 123 | } 124 | } 125 | }; 126 | return this; 127 | }; 128 | 129 | compute_gps = function(image) { 130 | var gps, _ref, _ref1; 131 | 132 | if (image && image.exif && image.exif.latitude && image.exif.longitude) { 133 | gps = { 134 | lat: image.exif.latitude, 135 | lng: image.exif.longitude, 136 | title: image.title 137 | }; 138 | if ((75 < (_ref = gps.lng) && _ref < 125) && (20 < (_ref1 = gps.lat) && _ref1 < 50)) { 139 | gps.lat = gps.lat - 0.0020746128999990844; 140 | gps.lng = gps.lng + 0.0047; 141 | } 142 | return gps; 143 | } 144 | }; 145 | 146 | this.draw_map = function(images) { 147 | var click_marker, gps, image, index, map, markers, _i, _len; 148 | 149 | markers = []; 150 | for (_i = 0, _len = images.length; _i < _len; _i++) { 151 | image = images[_i]; 152 | gps = compute_gps(image); 153 | if (gps) { 154 | index = $.inArray(image, images); 155 | click_marker = function(index) { 156 | _this.viewer.current_index(index); 157 | return _this.viewer.current_window('image-viewer'); 158 | }; 159 | gps.click = click_marker.bind({}, index); 160 | markers.push(gps); 161 | } 162 | } 163 | if (markers.length) { 164 | $('.map-box').css({ 165 | display: 'inline-block' 166 | }); 167 | map = new GMaps({ 168 | div: '#map', 169 | lat: markers[0].lat, 170 | lng: markers[0].lng 171 | }); 172 | map.addMarkers(markers); 173 | return map.fitZoom(); 174 | } 175 | }; 176 | 177 | this.run_viewer = function(images) { 178 | return $(document).ready(function() { 179 | var default_hash, show_viewer, viewer; 180 | 181 | viewer = new Viewer(images); 182 | exports.viewer = viewer; 183 | default_hash = location.hash.replace('#', ''); 184 | if (__indexOf.call(viewer.image_paths, default_hash) >= 0) { 185 | viewer.current_index($.inArray(default_hash, viewer.image_paths)); 186 | show_viewer = function() { 187 | return viewer.current_window('image-viewer'); 188 | }; 189 | setTimeout(show_viewer, 100); 190 | } 191 | return ko.applyBindings(viewer); 192 | }); 193 | }; 194 | 195 | }).call(this); 196 | -------------------------------------------------------------------------------- /template/album/js/gmaps.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * GMaps.js v0.4.4 3 | * http://hpneo.github.com/gmaps/ 4 | * 5 | * Copyright 2013, Gustavo Leon 6 | * Released under the MIT License. 7 | */ 8 | 9 | if (window.google == undefined && window.google.maps == undefined) { 10 | throw 'Google Maps API is required. Please register the following JavaScript library http://maps.google.com/maps/api/js?sensor=true.' 11 | } 12 | 13 | var extend_object = function(obj, new_obj) { 14 | var name; 15 | 16 | if (obj === new_obj) { 17 | return obj; 18 | } 19 | 20 | for (name in new_obj) { 21 | obj[name] = new_obj[name]; 22 | } 23 | 24 | return obj; 25 | }; 26 | 27 | var replace_object = function(obj, replace) { 28 | var name; 29 | 30 | if (obj === replace) { 31 | return obj; 32 | } 33 | 34 | for (name in replace) { 35 | if (obj[name] != undefined) { 36 | obj[name] = replace[name]; 37 | } 38 | } 39 | 40 | return obj; 41 | }; 42 | 43 | var array_map = function(array, callback) { 44 | var original_callback_params = Array.prototype.slice.call(arguments, 2), 45 | array_return = [], 46 | array_length = array.length, 47 | i; 48 | 49 | if (Array.prototype.map && array.map === Array.prototype.map) { 50 | array_return = Array.prototype.map.call(array, function(item) { 51 | callback_params = original_callback_params; 52 | callback_params.splice(0, 0, item); 53 | 54 | return callback.apply(this, callback_params); 55 | }); 56 | } 57 | else { 58 | for (i = 0; i < array_length; i++) { 59 | callback_params = original_callback_params; 60 | callback_params = callback_params.splice(0, 0, array[i]); 61 | array_return.push(callback.apply(this, callback_params)); 62 | } 63 | } 64 | 65 | return array_return; 66 | }; 67 | 68 | var array_flat = function(array) { 69 | var new_array = [], 70 | i; 71 | 72 | for (i = 0; i < array.length; i++) { 73 | new_array = new_array.concat(array[i]); 74 | } 75 | 76 | return new_array; 77 | }; 78 | 79 | var coordsToLatLngs = function(coords, useGeoJSON) { 80 | var first_coord = coords[0], 81 | second_coord = coords[1]; 82 | 83 | if (useGeoJSON) { 84 | first_coord = coords[1]; 85 | second_coord = coords[0]; 86 | } 87 | 88 | return new google.maps.LatLng(first_coord, second_coord); 89 | }; 90 | 91 | var arrayToLatLng = function(coords, useGeoJSON) { 92 | var i; 93 | 94 | for (i = 0; i < coords.length; i++) { 95 | if (coords[i].length > 0 && typeof(coords[i][0]) != "number") { 96 | coords[i] = arrayToLatLng(coords[i], useGeoJSON); 97 | } 98 | else { 99 | coords[i] = coordsToLatLngs(coords[i], useGeoJSON); 100 | } 101 | } 102 | 103 | return coords; 104 | }; 105 | 106 | var getElementById = function(id, context) { 107 | var element, 108 | id = id.replace('#', ''); 109 | 110 | if ('jQuery' in this && context) { 111 | element = $("#" + id, context)[0]; 112 | } else { 113 | element = document.getElementById(id); 114 | }; 115 | 116 | return element; 117 | }; 118 | 119 | var findAbsolutePosition = function(obj) { 120 | var curleft = 0, 121 | curtop = 0; 122 | 123 | if (obj.offsetParent) { 124 | do { 125 | curleft += obj.offsetLeft; 126 | curtop += obj.offsetTop; 127 | } while (obj = obj.offsetParent); 128 | } 129 | 130 | return [curleft, curtop]; 131 | }; 132 | 133 | var GMaps = (function(global) { 134 | "use strict"; 135 | 136 | var doc = document; 137 | 138 | var GMaps = function(options) { 139 | options.zoom = options.zoom || 15; 140 | options.mapType = options.mapType || 'roadmap'; 141 | 142 | var self = this, 143 | i, 144 | events_that_hide_context_menu = ['bounds_changed', 'center_changed', 'click', 'dblclick', 'drag', 'dragend', 'dragstart', 'idle', 'maptypeid_changed', 'projection_changed', 'resize', 'tilesloaded', 'zoom_changed'], 145 | events_that_doesnt_hide_context_menu = ['mousemove', 'mouseout', 'mouseover'], 146 | options_to_be_deleted = ['el', 'lat', 'lng', 'mapType', 'width', 'height', 'markerClusterer', 'enableNewStyle'], 147 | container_id = options.el || options.div, 148 | markerClustererFunction = options.markerClusterer, 149 | mapType = google.maps.MapTypeId[options.mapType.toUpperCase()], 150 | map_center = new google.maps.LatLng(options.lat, options.lng), 151 | zoomControl = options.zoomControl || true, 152 | zoomControlOpt = options.zoomControlOpt || { 153 | style: 'DEFAULT', 154 | position: 'TOP_LEFT' 155 | }, 156 | zoomControlStyle = zoomControlOpt.style || 'DEFAULT', 157 | zoomControlPosition = zoomControlOpt.position || 'TOP_LEFT', 158 | panControl = options.panControl || true, 159 | mapTypeControl = options.mapTypeControl || true, 160 | scaleControl = options.scaleControl || true, 161 | streetViewControl = options.streetViewControl || true, 162 | overviewMapControl = overviewMapControl || true, 163 | map_options = {}, 164 | map_base_options = { 165 | zoom: this.zoom, 166 | center: map_center, 167 | mapTypeId: mapType 168 | }, 169 | map_controls_options = { 170 | panControl: panControl, 171 | zoomControl: zoomControl, 172 | zoomControlOptions: { 173 | style: google.maps.ZoomControlStyle[zoomControlStyle], 174 | position: google.maps.ControlPosition[zoomControlPosition] 175 | }, 176 | mapTypeControl: mapTypeControl, 177 | scaleControl: scaleControl, 178 | streetViewControl: streetViewControl, 179 | overviewMapControl: overviewMapControl 180 | }; 181 | 182 | if (typeof(options.el) === 'string' || typeof(options.div) === 'string') { 183 | this.el = getElementById(container_id, options.context); 184 | } else { 185 | this.el = container_id; 186 | } 187 | 188 | if (typeof(this.el) === 'undefined' || this.el === null) { 189 | throw 'No element defined.'; 190 | } 191 | 192 | window.context_menu = window.context_menu || {}; 193 | window.context_menu[self.el.id] = {}; 194 | 195 | this.controls = []; 196 | this.overlays = []; 197 | this.layers = []; // array with kml/georss and fusiontables layers, can be as many 198 | this.singleLayers = {}; // object with the other layers, only one per layer 199 | this.markers = []; 200 | this.polylines = []; 201 | this.routes = []; 202 | this.polygons = []; 203 | this.infoWindow = null; 204 | this.overlay_el = null; 205 | this.zoom = options.zoom; 206 | this.registered_events = {}; 207 | 208 | this.el.style.width = options.width || this.el.scrollWidth || this.el.offsetWidth; 209 | this.el.style.height = options.height || this.el.scrollHeight || this.el.offsetHeight; 210 | 211 | google.maps.visualRefresh = options.enableNewStyle; 212 | 213 | for (i = 0; i < options_to_be_deleted.length; i++) { 214 | delete options[options_to_be_deleted[i]]; 215 | } 216 | 217 | if(options.disableDefaultUI != true) { 218 | map_base_options = extend_object(map_base_options, map_controls_options); 219 | } 220 | 221 | map_options = extend_object(map_base_options, options); 222 | 223 | for (i = 0; i < events_that_hide_context_menu.length; i++) { 224 | delete map_options[events_that_hide_context_menu[i]]; 225 | } 226 | 227 | for (i = 0; i < events_that_doesnt_hide_context_menu.length; i++) { 228 | delete map_options[events_that_doesnt_hide_context_menu[i]]; 229 | } 230 | 231 | this.map = new google.maps.Map(this.el, map_options); 232 | 233 | if (markerClustererFunction) { 234 | this.markerClusterer = markerClustererFunction.apply(this, [this.map]); 235 | } 236 | 237 | var buildContextMenuHTML = function(control, e) { 238 | var html = '', 239 | options = window.context_menu[self.el.id][control]; 240 | 241 | for (var i in options){ 242 | if (options.hasOwnProperty(i)) { 243 | var option = options[i]; 244 | 245 | html += '
  • ' + option.title + '
  • '; 246 | } 247 | } 248 | 249 | if (!getElementById('gmaps_context_menu')) return; 250 | 251 | var context_menu_element = getElementById('gmaps_context_menu'); 252 | 253 | context_menu_element.innerHTML = html; 254 | 255 | var context_menu_items = context_menu_element.getElementsByTagName('a'), 256 | context_menu_items_count = context_menu_items.length 257 | i; 258 | 259 | for (i = 0; i < context_menu_items_count; i++) { 260 | var context_menu_item = context_menu_items[i]; 261 | 262 | var assign_menu_item_action = function(ev){ 263 | ev.preventDefault(); 264 | 265 | options[this.id.replace(control + '_', '')].action.apply(self, [e]); 266 | self.hideContextMenu(); 267 | }; 268 | 269 | google.maps.event.clearListeners(context_menu_item, 'click'); 270 | google.maps.event.addDomListenerOnce(context_menu_item, 'click', assign_menu_item_action, false); 271 | } 272 | 273 | var position = findAbsolutePosition.apply(this, [self.el]), 274 | left = position[0] + e.pixel.x - 15, 275 | top = position[1] + e.pixel.y- 15; 276 | 277 | context_menu_element.style.left = left + "px"; 278 | context_menu_element.style.top = top + "px"; 279 | 280 | context_menu_element.style.display = 'block'; 281 | }; 282 | 283 | this.buildContextMenu = function(control, e) { 284 | if (control === 'marker') { 285 | e.pixel = {}; 286 | 287 | var overlay = new google.maps.OverlayView(); 288 | overlay.setMap(self.map); 289 | 290 | overlay.draw = function() { 291 | var projection = overlay.getProjection(), 292 | position = e.marker.getPosition(); 293 | 294 | e.pixel = projection.fromLatLngToContainerPixel(position); 295 | 296 | buildContextMenuHTML(control, e); 297 | }; 298 | } 299 | else { 300 | buildContextMenuHTML(control, e); 301 | } 302 | }; 303 | 304 | this.setContextMenu = function(options) { 305 | window.context_menu[self.el.id][options.control] = {}; 306 | 307 | var i, 308 | ul = doc.createElement('ul'); 309 | 310 | for (i in options.options) { 311 | if (options.options.hasOwnProperty(i)) { 312 | var option = options.options[i]; 313 | 314 | window.context_menu[self.el.id][options.control][option.name] = { 315 | title: option.title, 316 | action: option.action 317 | }; 318 | } 319 | } 320 | 321 | ul.id = 'gmaps_context_menu'; 322 | ul.style.display = 'none'; 323 | ul.style.position = 'absolute'; 324 | ul.style.minWidth = '100px'; 325 | ul.style.background = 'white'; 326 | ul.style.listStyle = 'none'; 327 | ul.style.padding = '8px'; 328 | ul.style.boxShadow = '2px 2px 6px #ccc'; 329 | 330 | doc.body.appendChild(ul); 331 | 332 | var context_menu_element = getElementById('gmaps_context_menu') 333 | 334 | google.maps.event.addDomListener(context_menu_element, 'mouseout', function(ev) { 335 | if (!ev.relatedTarget || !this.contains(ev.relatedTarget)) { 336 | window.setTimeout(function(){ 337 | context_menu_element.style.display = 'none'; 338 | }, 400); 339 | } 340 | }, false); 341 | }; 342 | 343 | this.hideContextMenu = function() { 344 | var context_menu_element = getElementById('gmaps_context_menu'); 345 | 346 | if (context_menu_element) { 347 | context_menu_element.style.display = 'none'; 348 | } 349 | }; 350 | 351 | var setupListener = function(object, name) { 352 | google.maps.event.addListener(object, name, function(e){ 353 | if (e == undefined) { 354 | e = this; 355 | } 356 | 357 | options[name].apply(this, [e]); 358 | 359 | self.hideContextMenu(); 360 | }); 361 | }; 362 | 363 | for (var ev = 0; ev < events_that_hide_context_menu.length; ev++) { 364 | var name = events_that_hide_context_menu[ev]; 365 | 366 | if (name in options) { 367 | setupListener(this.map, name); 368 | } 369 | } 370 | 371 | for (var ev = 0; ev < events_that_doesnt_hide_context_menu.length; ev++) { 372 | var name = events_that_doesnt_hide_context_menu[ev]; 373 | 374 | if (name in options) { 375 | setupListener(this.map, name); 376 | } 377 | } 378 | 379 | google.maps.event.addListener(this.map, 'rightclick', function(e) { 380 | if (options.rightclick) { 381 | options.rightclick.apply(this, [e]); 382 | } 383 | 384 | if(window.context_menu[self.el.id]['map'] != undefined) { 385 | self.buildContextMenu('map', e); 386 | } 387 | }); 388 | 389 | this.refresh = function() { 390 | google.maps.event.trigger(this.map, 'resize'); 391 | }; 392 | 393 | this.fitZoom = function() { 394 | var latLngs = [], 395 | markers_length = this.markers.length, 396 | i; 397 | 398 | for (i = 0; i < markers_length; i++) { 399 | latLngs.push(this.markers[i].getPosition()); 400 | } 401 | 402 | this.fitLatLngBounds(latLngs); 403 | }; 404 | 405 | this.fitLatLngBounds = function(latLngs) { 406 | var total = latLngs.length; 407 | var bounds = new google.maps.LatLngBounds(); 408 | 409 | for(var i=0; i < total; i++) { 410 | bounds.extend(latLngs[i]); 411 | } 412 | 413 | this.map.fitBounds(bounds); 414 | }; 415 | 416 | this.setCenter = function(lat, lng, callback) { 417 | this.map.panTo(new google.maps.LatLng(lat, lng)); 418 | 419 | if (callback) { 420 | callback(); 421 | } 422 | }; 423 | 424 | this.getElement = function() { 425 | return this.el; 426 | }; 427 | 428 | this.zoomIn = function(value) { 429 | value = value || 1; 430 | 431 | this.zoom = this.map.getZoom() + value; 432 | this.map.setZoom(this.zoom); 433 | }; 434 | 435 | this.zoomOut = function(value) { 436 | value = value || 1; 437 | 438 | this.zoom = this.map.getZoom() - value; 439 | this.map.setZoom(this.zoom); 440 | }; 441 | 442 | var native_methods = [], 443 | method; 444 | 445 | for (method in this.map) { 446 | if (typeof(this.map[method]) == 'function' && !this[method]) { 447 | native_methods.push(method); 448 | } 449 | } 450 | 451 | for (i=0; i < native_methods.length; i++) { 452 | (function(gmaps, scope, method_name) { 453 | gmaps[method_name] = function(){ 454 | return scope[method_name].apply(scope, arguments); 455 | }; 456 | })(this, this.map, native_methods[i]); 457 | } 458 | }; 459 | 460 | return GMaps; 461 | })(this); 462 | 463 | GMaps.prototype.createControl = function(options) { 464 | var control = document.createElement('div'); 465 | 466 | control.style.cursor = 'pointer'; 467 | control.style.fontFamily = 'Arial, sans-serif'; 468 | control.style.fontSize = '13px'; 469 | control.style.boxShadow = 'rgba(0, 0, 0, 0.398438) 0px 2px 4px'; 470 | 471 | for (var option in options.style) { 472 | control.style[option] = options.style[option]; 473 | } 474 | 475 | if (options.id) { 476 | control.id = options.id; 477 | } 478 | 479 | if (options.classes) { 480 | control.className = options.classes; 481 | } 482 | 483 | if (options.content) { 484 | control.innerHTML = options.content; 485 | } 486 | 487 | for (var ev in options.events) { 488 | (function(object, name) { 489 | google.maps.event.addDomListener(object, name, function(){ 490 | options.events[name].apply(this, [this]); 491 | }); 492 | })(control, ev); 493 | } 494 | 495 | control.index = 1; 496 | 497 | return control; 498 | }; 499 | 500 | GMaps.prototype.addControl = function(options) { 501 | var position = google.maps.ControlPosition[options.position.toUpperCase()]; 502 | 503 | delete options.position; 504 | 505 | var control = this.createControl(options); 506 | this.controls.push(control); 507 | 508 | this.map.controls[position].push(control); 509 | 510 | return control; 511 | }; 512 | 513 | GMaps.prototype.createMarker = function(options) { 514 | if (options.lat == undefined && options.lng == undefined && options.position == undefined) { 515 | throw 'No latitude or longitude defined.'; 516 | } 517 | 518 | var self = this, 519 | details = options.details, 520 | fences = options.fences, 521 | outside = options.outside, 522 | base_options = { 523 | position: new google.maps.LatLng(options.lat, options.lng), 524 | map: null 525 | }; 526 | 527 | delete options.lat; 528 | delete options.lng; 529 | delete options.fences; 530 | delete options.outside; 531 | 532 | var marker_options = extend_object(base_options, options), 533 | marker = new google.maps.Marker(marker_options); 534 | 535 | marker.fences = fences; 536 | 537 | if (options.infoWindow) { 538 | marker.infoWindow = new google.maps.InfoWindow(options.infoWindow); 539 | 540 | var info_window_events = ['closeclick', 'content_changed', 'domready', 'position_changed', 'zindex_changed']; 541 | 542 | for (var ev = 0; ev < info_window_events.length; ev++) { 543 | (function(object, name) { 544 | if (options.infoWindow[name]) { 545 | google.maps.event.addListener(object, name, function(e){ 546 | options.infoWindow[name].apply(this, [e]); 547 | }); 548 | } 549 | })(marker.infoWindow, info_window_events[ev]); 550 | } 551 | } 552 | 553 | var marker_events = ['animation_changed', 'clickable_changed', 'cursor_changed', 'draggable_changed', 'flat_changed', 'icon_changed', 'position_changed', 'shadow_changed', 'shape_changed', 'title_changed', 'visible_changed', 'zindex_changed']; 554 | 555 | var marker_events_with_mouse = ['dblclick', 'drag', 'dragend', 'dragstart', 'mousedown', 'mouseout', 'mouseover', 'mouseup']; 556 | 557 | for (var ev = 0; ev < marker_events.length; ev++) { 558 | (function(object, name) { 559 | if (options[name]) { 560 | google.maps.event.addListener(object, name, function(){ 561 | options[name].apply(this, [this]); 562 | }); 563 | } 564 | })(marker, marker_events[ev]); 565 | } 566 | 567 | for (var ev = 0; ev < marker_events_with_mouse.length; ev++) { 568 | (function(map, object, name) { 569 | if (options[name]) { 570 | google.maps.event.addListener(object, name, function(me){ 571 | if(!me.pixel){ 572 | me.pixel = map.getProjection().fromLatLngToPoint(me.latLng) 573 | } 574 | 575 | options[name].apply(this, [me]); 576 | }); 577 | } 578 | })(this.map, marker, marker_events_with_mouse[ev]); 579 | } 580 | 581 | google.maps.event.addListener(marker, 'click', function() { 582 | this.details = details; 583 | 584 | if (options.click) { 585 | options.click.apply(this, [this]); 586 | } 587 | 588 | if (marker.infoWindow) { 589 | self.hideInfoWindows(); 590 | marker.infoWindow.open(self.map, marker); 591 | } 592 | }); 593 | 594 | google.maps.event.addListener(marker, 'rightclick', function(e) { 595 | e.marker = this; 596 | 597 | if (options.rightclick) { 598 | options.rightclick.apply(this, [e]); 599 | } 600 | 601 | if (window.context_menu[self.el.id]['marker'] != undefined) { 602 | self.buildContextMenu('marker', e); 603 | } 604 | }); 605 | 606 | if (marker.fences) { 607 | google.maps.event.addListener(marker, 'dragend', function() { 608 | self.checkMarkerGeofence(marker, function(m, f) { 609 | outside(m, f); 610 | }); 611 | }); 612 | } 613 | 614 | return marker; 615 | }; 616 | 617 | GMaps.prototype.addMarker = function(options) { 618 | var marker; 619 | if(options.hasOwnProperty('gm_accessors_')) { 620 | // Native google.maps.Marker object 621 | marker = options; 622 | } 623 | else { 624 | if ((options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) || options.position) { 625 | marker = this.createMarker(options); 626 | } 627 | else { 628 | throw 'No latitude or longitude defined.'; 629 | } 630 | } 631 | 632 | marker.setMap(this.map); 633 | 634 | if(this.markerClusterer) { 635 | this.markerClusterer.addMarker(marker); 636 | } 637 | 638 | this.markers.push(marker); 639 | 640 | GMaps.fire('marker_added', marker, this); 641 | 642 | return marker; 643 | }; 644 | 645 | GMaps.prototype.addMarkers = function(array) { 646 | for (var i = 0, marker; marker=array[i]; i++) { 647 | this.addMarker(marker); 648 | } 649 | 650 | return this.markers; 651 | }; 652 | 653 | GMaps.prototype.hideInfoWindows = function() { 654 | for (var i = 0, marker; marker = this.markers[i]; i++){ 655 | if (marker.infoWindow){ 656 | marker.infoWindow.close(); 657 | } 658 | } 659 | }; 660 | 661 | GMaps.prototype.removeMarker = function(marker) { 662 | for (var i = 0; i < this.markers.length; i++) { 663 | if (this.markers[i] === marker) { 664 | this.markers[i].setMap(null); 665 | this.markers.splice(i, 1); 666 | 667 | GMaps.fire('marker_removed', marker, this); 668 | 669 | break; 670 | } 671 | } 672 | 673 | return marker; 674 | }; 675 | 676 | GMaps.prototype.removeMarkers = function(collection) { 677 | var collection = (collection || this.markers); 678 | 679 | for (var i = 0;i < this.markers.length; i++) { 680 | if(this.markers[i] === collection[i]) { 681 | this.markers[i].setMap(null); 682 | } 683 | } 684 | 685 | var new_markers = []; 686 | 687 | for (var i = 0;i < this.markers.length; i++) { 688 | if(this.markers[i].getMap() != null) { 689 | new_markers.push(this.markers[i]); 690 | } 691 | } 692 | 693 | this.markers = new_markers; 694 | }; 695 | 696 | GMaps.prototype.drawOverlay = function(options) { 697 | var overlay = new google.maps.OverlayView(), 698 | auto_show = true; 699 | 700 | overlay.setMap(this.map); 701 | 702 | if (options.auto_show != null) { 703 | auto_show = options.auto_show; 704 | } 705 | 706 | overlay.onAdd = function() { 707 | var el = document.createElement('div'); 708 | 709 | el.style.borderStyle = "none"; 710 | el.style.borderWidth = "0px"; 711 | el.style.position = "absolute"; 712 | el.style.zIndex = 100; 713 | el.innerHTML = options.content; 714 | 715 | overlay.el = el; 716 | 717 | if (!options.layer) { 718 | options.layer = 'overlayLayer'; 719 | } 720 | 721 | var panes = this.getPanes(), 722 | overlayLayer = panes[options.layer], 723 | stop_overlay_events = ['contextmenu', 'DOMMouseScroll', 'dblclick', 'mousedown']; 724 | 725 | overlayLayer.appendChild(el); 726 | 727 | for (var ev = 0; ev < stop_overlay_events.length; ev++) { 728 | (function(object, name) { 729 | google.maps.event.addDomListener(object, name, function(e){ 730 | if (navigator.userAgent.toLowerCase().indexOf('msie') != -1 && document.all) { 731 | e.cancelBubble = true; 732 | e.returnValue = false; 733 | } 734 | else { 735 | e.stopPropagation(); 736 | } 737 | }); 738 | })(el, stop_overlay_events[ev]); 739 | } 740 | 741 | google.maps.event.trigger(this, 'ready'); 742 | }; 743 | 744 | overlay.draw = function() { 745 | var projection = this.getProjection(), 746 | pixel = projection.fromLatLngToDivPixel(new google.maps.LatLng(options.lat, options.lng)); 747 | 748 | options.horizontalOffset = options.horizontalOffset || 0; 749 | options.verticalOffset = options.verticalOffset || 0; 750 | 751 | var el = overlay.el, 752 | content = el.children[0], 753 | content_height = content.clientHeight, 754 | content_width = content.clientWidth; 755 | 756 | switch (options.verticalAlign) { 757 | case 'top': 758 | el.style.top = (pixel.y - content_height + options.verticalOffset) + 'px'; 759 | break; 760 | default: 761 | case 'middle': 762 | el.style.top = (pixel.y - (content_height / 2) + options.verticalOffset) + 'px'; 763 | break; 764 | case 'bottom': 765 | el.style.top = (pixel.y + options.verticalOffset) + 'px'; 766 | break; 767 | } 768 | 769 | switch (options.horizontalAlign) { 770 | case 'left': 771 | el.style.left = (pixel.x - content_width + options.horizontalOffset) + 'px'; 772 | break; 773 | default: 774 | case 'center': 775 | el.style.left = (pixel.x - (content_width / 2) + options.horizontalOffset) + 'px'; 776 | break; 777 | case 'right': 778 | el.style.left = (pixel.x + options.horizontalOffset) + 'px'; 779 | break; 780 | } 781 | 782 | el.style.display = auto_show ? 'block' : 'none'; 783 | 784 | if (!auto_show) { 785 | options.show.apply(this, [el]); 786 | } 787 | }; 788 | 789 | overlay.onRemove = function() { 790 | var el = overlay.el; 791 | 792 | if (options.remove) { 793 | options.remove.apply(this, [el]); 794 | } 795 | else { 796 | overlay.el.parentNode.removeChild(overlay.el); 797 | overlay.el = null; 798 | } 799 | }; 800 | 801 | this.overlays.push(overlay); 802 | return overlay; 803 | }; 804 | 805 | GMaps.prototype.removeOverlay = function(overlay) { 806 | for (var i = 0; i < this.overlays.length; i++) { 807 | if (this.overlays[i] === overlay) { 808 | this.overlays[i].setMap(null); 809 | this.overlays.splice(i, 1); 810 | 811 | break; 812 | } 813 | } 814 | }; 815 | 816 | GMaps.prototype.removeOverlays = function() { 817 | for (var i = 0, item; item = this.overlays[i]; i++) { 818 | item.setMap(null); 819 | } 820 | 821 | this.overlays = []; 822 | }; 823 | 824 | GMaps.prototype.drawPolyline = function(options) { 825 | var path = [], 826 | points = options.path; 827 | 828 | if (points.length) { 829 | if (points[0][0] === undefined) { 830 | path = points; 831 | } 832 | else { 833 | for (var i=0, latlng; latlng=points[i]; i++) { 834 | path.push(new google.maps.LatLng(latlng[0], latlng[1])); 835 | } 836 | } 837 | } 838 | 839 | var polyline_options = { 840 | map: this.map, 841 | path: path, 842 | strokeColor: options.strokeColor, 843 | strokeOpacity: options.strokeOpacity, 844 | strokeWeight: options.strokeWeight, 845 | geodesic: options.geodesic, 846 | clickable: true, 847 | editable: false, 848 | visible: true 849 | }; 850 | 851 | if (options.hasOwnProperty("clickable")) { 852 | polyline_options.clickable = options.clickable; 853 | } 854 | 855 | if (options.hasOwnProperty("editable")) { 856 | polyline_options.editable = options.editable; 857 | } 858 | 859 | if (options.hasOwnProperty("icons")) { 860 | polyline_options.icons = options.icons; 861 | } 862 | 863 | if (options.hasOwnProperty("zIndex")) { 864 | polyline_options.zIndex = options.zIndex; 865 | } 866 | 867 | var polyline = new google.maps.Polyline(polyline_options); 868 | 869 | var polyline_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick']; 870 | 871 | for (var ev = 0; ev < polyline_events.length; ev++) { 872 | (function(object, name) { 873 | if (options[name]) { 874 | google.maps.event.addListener(object, name, function(e){ 875 | options[name].apply(this, [e]); 876 | }); 877 | } 878 | })(polyline, polyline_events[ev]); 879 | } 880 | 881 | this.polylines.push(polyline); 882 | 883 | GMaps.fire('polyline_added', polyline, this); 884 | 885 | return polyline; 886 | }; 887 | 888 | GMaps.prototype.removePolyline = function(polyline) { 889 | for (var i = 0; i < this.polylines.length; i++) { 890 | if (this.polylines[i] === polyline) { 891 | this.polylines[i].setMap(null); 892 | this.polylines.splice(i, 1); 893 | 894 | GMaps.fire('polyline_removed', polyline, this); 895 | 896 | break; 897 | } 898 | } 899 | }; 900 | 901 | GMaps.prototype.removePolylines = function() { 902 | for (var i = 0, item; item = this.polylines[i]; i++) { 903 | item.setMap(null); 904 | } 905 | 906 | this.polylines = []; 907 | }; 908 | 909 | GMaps.prototype.drawCircle = function(options) { 910 | options = extend_object({ 911 | map: this.map, 912 | center: new google.maps.LatLng(options.lat, options.lng) 913 | }, options); 914 | 915 | delete options.lat; 916 | delete options.lng; 917 | 918 | var polygon = new google.maps.Circle(options), 919 | polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick']; 920 | 921 | for (var ev = 0; ev < polygon_events.length; ev++) { 922 | (function(object, name) { 923 | if (options[name]) { 924 | google.maps.event.addListener(object, name, function(e){ 925 | options[name].apply(this, [e]); 926 | }); 927 | } 928 | })(polygon, polygon_events[ev]); 929 | } 930 | 931 | this.polygons.push(polygon); 932 | 933 | return polygon; 934 | }; 935 | 936 | GMaps.prototype.drawRectangle = function(options) { 937 | options = extend_object({ 938 | map: this.map 939 | }, options); 940 | 941 | var latLngBounds = new google.maps.LatLngBounds( 942 | new google.maps.LatLng(options.bounds[0][0], options.bounds[0][1]), 943 | new google.maps.LatLng(options.bounds[1][0], options.bounds[1][1]) 944 | ); 945 | 946 | options.bounds = latLngBounds; 947 | 948 | var polygon = new google.maps.Rectangle(options), 949 | polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick']; 950 | 951 | for (var ev = 0; ev < polygon_events.length; ev++) { 952 | (function(object, name) { 953 | if (options[name]) { 954 | google.maps.event.addListener(object, name, function(e){ 955 | options[name].apply(this, [e]); 956 | }); 957 | } 958 | })(polygon, polygon_events[ev]); 959 | } 960 | 961 | this.polygons.push(polygon); 962 | 963 | return polygon; 964 | }; 965 | 966 | GMaps.prototype.drawPolygon = function(options) { 967 | var useGeoJSON = false; 968 | 969 | if(options.hasOwnProperty("useGeoJSON")) { 970 | useGeoJSON = options.useGeoJSON; 971 | } 972 | 973 | delete options.useGeoJSON; 974 | 975 | options = extend_object({ 976 | map: this.map 977 | }, options); 978 | 979 | if (useGeoJSON == false) { 980 | options.paths = [options.paths.slice(0)]; 981 | } 982 | 983 | if (options.paths.length > 0) { 984 | if (options.paths[0].length > 0) { 985 | options.paths = array_flat(array_map(options.paths, arrayToLatLng, useGeoJSON)); 986 | } 987 | } 988 | 989 | var polygon = new google.maps.Polygon(options), 990 | polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick']; 991 | 992 | for (var ev = 0; ev < polygon_events.length; ev++) { 993 | (function(object, name) { 994 | if (options[name]) { 995 | google.maps.event.addListener(object, name, function(e){ 996 | options[name].apply(this, [e]); 997 | }); 998 | } 999 | })(polygon, polygon_events[ev]); 1000 | } 1001 | 1002 | this.polygons.push(polygon); 1003 | 1004 | GMaps.fire('polygon_added', polygon, this); 1005 | 1006 | return polygon; 1007 | }; 1008 | 1009 | GMaps.prototype.removePolygon = function(polygon) { 1010 | for (var i = 0; i < this.polygons.length; i++) { 1011 | if (this.polygons[i] === polygon) { 1012 | this.polygons[i].setMap(null); 1013 | this.polygons.splice(i, 1); 1014 | 1015 | GMaps.fire('polygon_removed', polygon, this); 1016 | 1017 | break; 1018 | } 1019 | } 1020 | }; 1021 | 1022 | GMaps.prototype.removePolygons = function() { 1023 | for (var i = 0, item; item = this.polygons[i]; i++) { 1024 | item.setMap(null); 1025 | } 1026 | 1027 | this.polygons = []; 1028 | }; 1029 | 1030 | GMaps.prototype.getFromFusionTables = function(options) { 1031 | var events = options.events; 1032 | 1033 | delete options.events; 1034 | 1035 | var fusion_tables_options = options, 1036 | layer = new google.maps.FusionTablesLayer(fusion_tables_options); 1037 | 1038 | for (var ev in events) { 1039 | (function(object, name) { 1040 | google.maps.event.addListener(object, name, function(e) { 1041 | events[name].apply(this, [e]); 1042 | }); 1043 | })(layer, ev); 1044 | } 1045 | 1046 | this.layers.push(layer); 1047 | 1048 | return layer; 1049 | }; 1050 | 1051 | GMaps.prototype.loadFromFusionTables = function(options) { 1052 | var layer = this.getFromFusionTables(options); 1053 | layer.setMap(this.map); 1054 | 1055 | return layer; 1056 | }; 1057 | 1058 | GMaps.prototype.getFromKML = function(options) { 1059 | var url = options.url, 1060 | events = options.events; 1061 | 1062 | delete options.url; 1063 | delete options.events; 1064 | 1065 | var kml_options = options, 1066 | layer = new google.maps.KmlLayer(url, kml_options); 1067 | 1068 | for (var ev in events) { 1069 | (function(object, name) { 1070 | google.maps.event.addListener(object, name, function(e) { 1071 | events[name].apply(this, [e]); 1072 | }); 1073 | })(layer, ev); 1074 | } 1075 | 1076 | this.layers.push(layer); 1077 | 1078 | return layer; 1079 | }; 1080 | 1081 | GMaps.prototype.loadFromKML = function(options) { 1082 | var layer = this.getFromKML(options); 1083 | layer.setMap(this.map); 1084 | 1085 | return layer; 1086 | }; 1087 | 1088 | GMaps.prototype.addLayer = function(layerName, options) { 1089 | //var default_layers = ['weather', 'clouds', 'traffic', 'transit', 'bicycling', 'panoramio', 'places']; 1090 | options = options || {}; 1091 | var layer; 1092 | 1093 | switch(layerName) { 1094 | case 'weather': this.singleLayers.weather = layer = new google.maps.weather.WeatherLayer(); 1095 | break; 1096 | case 'clouds': this.singleLayers.clouds = layer = new google.maps.weather.CloudLayer(); 1097 | break; 1098 | case 'traffic': this.singleLayers.traffic = layer = new google.maps.TrafficLayer(); 1099 | break; 1100 | case 'transit': this.singleLayers.transit = layer = new google.maps.TransitLayer(); 1101 | break; 1102 | case 'bicycling': this.singleLayers.bicycling = layer = new google.maps.BicyclingLayer(); 1103 | break; 1104 | case 'panoramio': 1105 | this.singleLayers.panoramio = layer = new google.maps.panoramio.PanoramioLayer(); 1106 | layer.setTag(options.filter); 1107 | delete options.filter; 1108 | 1109 | //click event 1110 | if (options.click) { 1111 | google.maps.event.addListener(layer, 'click', function(event) { 1112 | options.click(event); 1113 | delete options.click; 1114 | }); 1115 | } 1116 | break; 1117 | case 'places': 1118 | this.singleLayers.places = layer = new google.maps.places.PlacesService(this.map); 1119 | 1120 | //search and nearbySearch callback, Both are the same 1121 | if (options.search || options.nearbySearch) { 1122 | var placeSearchRequest = { 1123 | bounds : options.bounds || null, 1124 | keyword : options.keyword || null, 1125 | location : options.location || null, 1126 | name : options.name || null, 1127 | radius : options.radius || null, 1128 | rankBy : options.rankBy || null, 1129 | types : options.types || null 1130 | }; 1131 | 1132 | if (options.search) { 1133 | layer.search(placeSearchRequest, options.search); 1134 | } 1135 | 1136 | if (options.nearbySearch) { 1137 | layer.nearbySearch(placeSearchRequest, options.nearbySearch); 1138 | } 1139 | } 1140 | 1141 | //textSearch callback 1142 | if (options.textSearch) { 1143 | var textSearchRequest = { 1144 | bounds : options.bounds || null, 1145 | location : options.location || null, 1146 | query : options.query || null, 1147 | radius : options.radius || null 1148 | }; 1149 | 1150 | layer.textSearch(textSearchRequest, options.textSearch); 1151 | } 1152 | break; 1153 | } 1154 | 1155 | if (layer !== undefined) { 1156 | if (typeof layer.setOptions == 'function') { 1157 | layer.setOptions(options); 1158 | } 1159 | if (typeof layer.setMap == 'function') { 1160 | layer.setMap(this.map); 1161 | } 1162 | 1163 | return layer; 1164 | } 1165 | }; 1166 | 1167 | GMaps.prototype.removeLayer = function(layer) { 1168 | if (typeof(layer) == "string" && this.singleLayers[layer] !== undefined) { 1169 | this.singleLayers[layer].setMap(null); 1170 | 1171 | delete this.singleLayers[layer]; 1172 | } 1173 | else { 1174 | for (var i = 0; i < this.layers.length; i++) { 1175 | if (this.layers[i] === layer) { 1176 | this.layers[i].setMap(null); 1177 | this.layers.splice(i, 1); 1178 | 1179 | break; 1180 | } 1181 | } 1182 | } 1183 | }; 1184 | 1185 | var travelMode, unitSystem; 1186 | 1187 | GMaps.prototype.getRoutes = function(options) { 1188 | switch (options.travelMode) { 1189 | case 'bicycling': 1190 | travelMode = google.maps.TravelMode.BICYCLING; 1191 | break; 1192 | case 'transit': 1193 | travelMode = google.maps.TravelMode.TRANSIT; 1194 | break; 1195 | case 'driving': 1196 | travelMode = google.maps.TravelMode.DRIVING; 1197 | break; 1198 | default: 1199 | travelMode = google.maps.TravelMode.WALKING; 1200 | break; 1201 | } 1202 | 1203 | if (options.unitSystem === 'imperial') { 1204 | unitSystem = google.maps.UnitSystem.IMPERIAL; 1205 | } 1206 | else { 1207 | unitSystem = google.maps.UnitSystem.METRIC; 1208 | } 1209 | 1210 | var base_options = { 1211 | avoidHighways: false, 1212 | avoidTolls: false, 1213 | optimizeWaypoints: false, 1214 | waypoints: [] 1215 | }, 1216 | request_options = extend_object(base_options, options); 1217 | 1218 | request_options.origin = /string/.test(typeof options.origin) ? options.origin : new google.maps.LatLng(options.origin[0], options.origin[1]); 1219 | request_options.destination = /string/.test(typeof options.destination) ? options.destination : new google.maps.LatLng(options.destination[0], options.destination[1]); 1220 | request_options.travelMode = travelMode; 1221 | request_options.unitSystem = unitSystem; 1222 | 1223 | delete request_options.callback; 1224 | 1225 | var self = this, 1226 | service = new google.maps.DirectionsService(); 1227 | 1228 | service.route(request_options, function(result, status) { 1229 | if (status === google.maps.DirectionsStatus.OK) { 1230 | for (var r in result.routes) { 1231 | if (result.routes.hasOwnProperty(r)) { 1232 | self.routes.push(result.routes[r]); 1233 | } 1234 | } 1235 | } 1236 | 1237 | if (options.callback) { 1238 | options.callback(self.routes); 1239 | } 1240 | }); 1241 | }; 1242 | 1243 | GMaps.prototype.removeRoutes = function() { 1244 | this.routes = []; 1245 | }; 1246 | 1247 | GMaps.prototype.getElevations = function(options) { 1248 | options = extend_object({ 1249 | locations: [], 1250 | path : false, 1251 | samples : 256 1252 | }, options); 1253 | 1254 | if (options.locations.length > 0) { 1255 | if (options.locations[0].length > 0) { 1256 | options.locations = array_flat(array_map([options.locations], arrayToLatLng, false)); 1257 | } 1258 | } 1259 | 1260 | var callback = options.callback; 1261 | delete options.callback; 1262 | 1263 | var service = new google.maps.ElevationService(); 1264 | 1265 | //location request 1266 | if (!options.path) { 1267 | delete options.path; 1268 | delete options.samples; 1269 | 1270 | service.getElevationForLocations(options, function(result, status) { 1271 | if (callback && typeof(callback) === "function") { 1272 | callback(result, status); 1273 | } 1274 | }); 1275 | //path request 1276 | } else { 1277 | var pathRequest = { 1278 | path : options.locations, 1279 | samples : options.samples 1280 | }; 1281 | 1282 | service.getElevationAlongPath(pathRequest, function(result, status) { 1283 | if (callback && typeof(callback) === "function") { 1284 | callback(result, status); 1285 | } 1286 | }); 1287 | } 1288 | }; 1289 | 1290 | GMaps.prototype.cleanRoute = GMaps.prototype.removePolylines; 1291 | 1292 | GMaps.prototype.drawRoute = function(options) { 1293 | var self = this; 1294 | 1295 | this.getRoutes({ 1296 | origin: options.origin, 1297 | destination: options.destination, 1298 | travelMode: options.travelMode, 1299 | waypoints: options.waypoints, 1300 | unitSystem: options.unitSystem, 1301 | callback: function(e) { 1302 | if (e.length > 0) { 1303 | self.drawPolyline({ 1304 | path: e[e.length - 1].overview_path, 1305 | strokeColor: options.strokeColor, 1306 | strokeOpacity: options.strokeOpacity, 1307 | strokeWeight: options.strokeWeight 1308 | }); 1309 | 1310 | if (options.callback) { 1311 | options.callback(e[e.length - 1]); 1312 | } 1313 | } 1314 | } 1315 | }); 1316 | }; 1317 | 1318 | GMaps.prototype.travelRoute = function(options) { 1319 | if (options.origin && options.destination) { 1320 | this.getRoutes({ 1321 | origin: options.origin, 1322 | destination: options.destination, 1323 | travelMode: options.travelMode, 1324 | waypoints : options.waypoints, 1325 | callback: function(e) { 1326 | //start callback 1327 | if (e.length > 0 && options.start) { 1328 | options.start(e[e.length - 1]); 1329 | } 1330 | 1331 | //step callback 1332 | if (e.length > 0 && options.step) { 1333 | var route = e[e.length - 1]; 1334 | if (route.legs.length > 0) { 1335 | var steps = route.legs[0].steps; 1336 | for (var i=0, step; step=steps[i]; i++) { 1337 | step.step_number = i; 1338 | options.step(step, (route.legs[0].steps.length - 1)); 1339 | } 1340 | } 1341 | } 1342 | 1343 | //end callback 1344 | if (e.length > 0 && options.end) { 1345 | options.end(e[e.length - 1]); 1346 | } 1347 | } 1348 | }); 1349 | } 1350 | else if (options.route) { 1351 | if (options.route.legs.length > 0) { 1352 | var steps = options.route.legs[0].steps; 1353 | for (var i=0, step; step=steps[i]; i++) { 1354 | step.step_number = i; 1355 | options.step(step); 1356 | } 1357 | } 1358 | } 1359 | }; 1360 | 1361 | GMaps.prototype.drawSteppedRoute = function(options) { 1362 | var self = this; 1363 | 1364 | if (options.origin && options.destination) { 1365 | this.getRoutes({ 1366 | origin: options.origin, 1367 | destination: options.destination, 1368 | travelMode: options.travelMode, 1369 | waypoints : options.waypoints, 1370 | callback: function(e) { 1371 | //start callback 1372 | if (e.length > 0 && options.start) { 1373 | options.start(e[e.length - 1]); 1374 | } 1375 | 1376 | //step callback 1377 | if (e.length > 0 && options.step) { 1378 | var route = e[e.length - 1]; 1379 | if (route.legs.length > 0) { 1380 | var steps = route.legs[0].steps; 1381 | for (var i=0, step; step=steps[i]; i++) { 1382 | step.step_number = i; 1383 | self.drawPolyline({ 1384 | path: step.path, 1385 | strokeColor: options.strokeColor, 1386 | strokeOpacity: options.strokeOpacity, 1387 | strokeWeight: options.strokeWeight 1388 | }); 1389 | options.step(step, (route.legs[0].steps.length - 1)); 1390 | } 1391 | } 1392 | } 1393 | 1394 | //end callback 1395 | if (e.length > 0 && options.end) { 1396 | options.end(e[e.length - 1]); 1397 | } 1398 | } 1399 | }); 1400 | } 1401 | else if (options.route) { 1402 | if (options.route.legs.length > 0) { 1403 | var steps = options.route.legs[0].steps; 1404 | for (var i=0, step; step=steps[i]; i++) { 1405 | step.step_number = i; 1406 | self.drawPolyline({ 1407 | path: step.path, 1408 | strokeColor: options.strokeColor, 1409 | strokeOpacity: options.strokeOpacity, 1410 | strokeWeight: options.strokeWeight 1411 | }); 1412 | options.step(step); 1413 | } 1414 | } 1415 | } 1416 | }; 1417 | 1418 | GMaps.Route = function(options) { 1419 | this.origin = options.origin; 1420 | this.destination = options.destination; 1421 | this.waypoints = options.waypoints; 1422 | 1423 | this.map = options.map; 1424 | this.route = options.route; 1425 | this.step_count = 0; 1426 | this.steps = this.route.legs[0].steps; 1427 | this.steps_length = this.steps.length; 1428 | 1429 | this.polyline = this.map.drawPolyline({ 1430 | path: new google.maps.MVCArray(), 1431 | strokeColor: options.strokeColor, 1432 | strokeOpacity: options.strokeOpacity, 1433 | strokeWeight: options.strokeWeight 1434 | }).getPath(); 1435 | }; 1436 | 1437 | GMaps.Route.prototype.getRoute = function(options) { 1438 | var self = this; 1439 | 1440 | this.map.getRoutes({ 1441 | origin : this.origin, 1442 | destination : this.destination, 1443 | travelMode : options.travelMode, 1444 | waypoints : this.waypoints || [], 1445 | callback : function() { 1446 | self.route = e[0]; 1447 | 1448 | if (options.callback) { 1449 | options.callback.call(self); 1450 | } 1451 | } 1452 | }); 1453 | }; 1454 | 1455 | GMaps.Route.prototype.back = function() { 1456 | if (this.step_count > 0) { 1457 | this.step_count--; 1458 | var path = this.route.legs[0].steps[this.step_count].path; 1459 | 1460 | for (var p in path){ 1461 | if (path.hasOwnProperty(p)){ 1462 | this.polyline.pop(); 1463 | } 1464 | } 1465 | } 1466 | }; 1467 | 1468 | GMaps.Route.prototype.forward = function() { 1469 | if (this.step_count < this.steps_length) { 1470 | var path = this.route.legs[0].steps[this.step_count].path; 1471 | 1472 | for (var p in path){ 1473 | if (path.hasOwnProperty(p)){ 1474 | this.polyline.push(path[p]); 1475 | } 1476 | } 1477 | this.step_count++; 1478 | } 1479 | }; 1480 | 1481 | GMaps.prototype.checkGeofence = function(lat, lng, fence) { 1482 | return fence.containsLatLng(new google.maps.LatLng(lat, lng)); 1483 | }; 1484 | 1485 | GMaps.prototype.checkMarkerGeofence = function(marker, outside_callback) { 1486 | if (marker.fences) { 1487 | for (var i = 0, fence; fence = marker.fences[i]; i++) { 1488 | var pos = marker.getPosition(); 1489 | if (!this.checkGeofence(pos.lat(), pos.lng(), fence)) { 1490 | outside_callback(marker, fence); 1491 | } 1492 | } 1493 | } 1494 | }; 1495 | 1496 | GMaps.prototype.toImage = function(options) { 1497 | var options = options || {}, 1498 | static_map_options = {}; 1499 | 1500 | static_map_options['size'] = options['size'] || [this.el.clientWidth, this.el.clientHeight]; 1501 | static_map_options['lat'] = this.getCenter().lat(); 1502 | static_map_options['lng'] = this.getCenter().lng(); 1503 | 1504 | if (this.markers.length > 0) { 1505 | static_map_options['markers'] = []; 1506 | 1507 | for (var i = 0; i < this.markers.length; i++) { 1508 | static_map_options['markers'].push({ 1509 | lat: this.markers[i].getPosition().lat(), 1510 | lng: this.markers[i].getPosition().lng() 1511 | }); 1512 | } 1513 | } 1514 | 1515 | if (this.polylines.length > 0) { 1516 | var polyline = this.polylines[0]; 1517 | 1518 | static_map_options['polyline'] = {}; 1519 | static_map_options['polyline']['path'] = google.maps.geometry.encoding.encodePath(polyline.getPath()); 1520 | static_map_options['polyline']['strokeColor'] = polyline.strokeColor 1521 | static_map_options['polyline']['strokeOpacity'] = polyline.strokeOpacity 1522 | static_map_options['polyline']['strokeWeight'] = polyline.strokeWeight 1523 | } 1524 | 1525 | return GMaps.staticMapURL(static_map_options); 1526 | }; 1527 | 1528 | GMaps.staticMapURL = function(options){ 1529 | var parameters = [], 1530 | data, 1531 | static_root = 'http://maps.googleapis.com/maps/api/staticmap'; 1532 | 1533 | if (options.url) { 1534 | static_root = options.url; 1535 | delete options.url; 1536 | } 1537 | 1538 | static_root += '?'; 1539 | 1540 | var markers = options.markers; 1541 | 1542 | delete options.markers; 1543 | 1544 | if (!markers && options.marker) { 1545 | markers = [options.marker]; 1546 | delete options.marker; 1547 | } 1548 | 1549 | var polyline = options.polyline; 1550 | delete options.polyline; 1551 | 1552 | /** Map options **/ 1553 | if (options.center) { 1554 | parameters.push('center=' + options.center); 1555 | delete options.center; 1556 | } 1557 | else if (options.address) { 1558 | parameters.push('center=' + options.address); 1559 | delete options.address; 1560 | } 1561 | else if (options.lat) { 1562 | parameters.push(['center=', options.lat, ',', options.lng].join('')); 1563 | delete options.lat; 1564 | delete options.lng; 1565 | } 1566 | else if (options.visible) { 1567 | var visible = encodeURI(options.visible.join('|')); 1568 | parameters.push('visible=' + visible); 1569 | } 1570 | 1571 | var size = options.size; 1572 | if (size) { 1573 | if (size.join) { 1574 | size = size.join('x'); 1575 | } 1576 | delete options.size; 1577 | } 1578 | else { 1579 | size = '630x300'; 1580 | } 1581 | parameters.push('size=' + size); 1582 | 1583 | if (!options.zoom) { 1584 | options.zoom = 15; 1585 | } 1586 | 1587 | var sensor = options.hasOwnProperty('sensor') ? !!options.sensor : true; 1588 | delete options.sensor; 1589 | parameters.push('sensor=' + sensor); 1590 | 1591 | for (var param in options) { 1592 | if (options.hasOwnProperty(param)) { 1593 | parameters.push(param + '=' + options[param]); 1594 | } 1595 | } 1596 | 1597 | /** Markers **/ 1598 | if (markers) { 1599 | var marker, loc; 1600 | 1601 | for (var i=0; data=markers[i]; i++) { 1602 | marker = []; 1603 | 1604 | if (data.size && data.size !== 'normal') { 1605 | marker.push('size:' + data.size); 1606 | } 1607 | else if (data.icon) { 1608 | marker.push('icon:' + encodeURI(data.icon)); 1609 | } 1610 | 1611 | if (data.color) { 1612 | marker.push('color:' + data.color.replace('#', '0x')); 1613 | } 1614 | 1615 | if (data.label) { 1616 | marker.push('label:' + data.label[0].toUpperCase()); 1617 | } 1618 | 1619 | loc = (data.address ? data.address : data.lat + ',' + data.lng); 1620 | 1621 | if (marker.length || i === 0) { 1622 | marker.push(loc); 1623 | marker = marker.join('|'); 1624 | parameters.push('markers=' + encodeURI(marker)); 1625 | } 1626 | // New marker without styles 1627 | else { 1628 | marker = parameters.pop() + encodeURI('|' + loc); 1629 | parameters.push(marker); 1630 | } 1631 | } 1632 | } 1633 | 1634 | /** Polylines **/ 1635 | function parseColor(color, opacity) { 1636 | if (color[0] === '#'){ 1637 | color = color.replace('#', '0x'); 1638 | 1639 | if (opacity) { 1640 | opacity = parseFloat(opacity); 1641 | opacity = Math.min(1, Math.max(opacity, 0)); 1642 | if (opacity === 0) { 1643 | return '0x00000000'; 1644 | } 1645 | opacity = (opacity * 255).toString(16); 1646 | if (opacity.length === 1) { 1647 | opacity += opacity; 1648 | } 1649 | 1650 | color = color.slice(0,8) + opacity; 1651 | } 1652 | } 1653 | return color; 1654 | } 1655 | 1656 | if (polyline) { 1657 | data = polyline; 1658 | polyline = []; 1659 | 1660 | if (data.strokeWeight) { 1661 | polyline.push('weight:' + parseInt(data.strokeWeight, 10)); 1662 | } 1663 | 1664 | if (data.strokeColor) { 1665 | var color = parseColor(data.strokeColor, data.strokeOpacity); 1666 | polyline.push('color:' + color); 1667 | } 1668 | 1669 | if (data.fillColor) { 1670 | var fillcolor = parseColor(data.fillColor, data.fillOpacity); 1671 | polyline.push('fillcolor:' + fillcolor); 1672 | } 1673 | 1674 | var path = data.path; 1675 | if (path.join) { 1676 | for (var j=0, pos; pos=path[j]; j++) { 1677 | polyline.push(pos.join(',')); 1678 | } 1679 | } 1680 | else { 1681 | polyline.push('enc:' + path); 1682 | } 1683 | 1684 | polyline = polyline.join('|'); 1685 | parameters.push('path=' + encodeURI(polyline)); 1686 | } 1687 | 1688 | parameters = parameters.join('&'); 1689 | return static_root + parameters; 1690 | }; 1691 | 1692 | GMaps.prototype.addMapType = function(mapTypeId, options) { 1693 | if (options.hasOwnProperty("getTileUrl") && typeof(options["getTileUrl"]) == "function") { 1694 | options.tileSize = options.tileSize || new google.maps.Size(256, 256); 1695 | 1696 | var mapType = new google.maps.ImageMapType(options); 1697 | 1698 | this.map.mapTypes.set(mapTypeId, mapType); 1699 | } 1700 | else { 1701 | throw "'getTileUrl' function required."; 1702 | } 1703 | }; 1704 | 1705 | GMaps.prototype.addOverlayMapType = function(options) { 1706 | if (options.hasOwnProperty("getTile") && typeof(options["getTile"]) == "function") { 1707 | var overlayMapTypeIndex = options.index; 1708 | 1709 | delete options.index; 1710 | 1711 | this.map.overlayMapTypes.insertAt(overlayMapTypeIndex, options); 1712 | } 1713 | else { 1714 | throw "'getTile' function required."; 1715 | } 1716 | }; 1717 | 1718 | GMaps.prototype.removeOverlayMapType = function(overlayMapTypeIndex) { 1719 | this.map.overlayMapTypes.removeAt(overlayMapTypeIndex); 1720 | }; 1721 | 1722 | GMaps.prototype.addStyle = function(options) { 1723 | var styledMapType = new google.maps.StyledMapType(options.styles, options.styledMapName); 1724 | 1725 | this.map.mapTypes.set(options.mapTypeId, styledMapType); 1726 | }; 1727 | 1728 | GMaps.prototype.setStyle = function(mapTypeId) { 1729 | this.map.setMapTypeId(mapTypeId); 1730 | }; 1731 | 1732 | GMaps.prototype.createPanorama = function(streetview_options) { 1733 | if (!streetview_options.hasOwnProperty('lat') || !streetview_options.hasOwnProperty('lng')) { 1734 | streetview_options.lat = this.getCenter().lat(); 1735 | streetview_options.lng = this.getCenter().lng(); 1736 | } 1737 | 1738 | this.panorama = GMaps.createPanorama(streetview_options); 1739 | 1740 | this.map.setStreetView(this.panorama); 1741 | 1742 | return this.panorama; 1743 | }; 1744 | 1745 | GMaps.createPanorama = function(options) { 1746 | var el = getElementById(options.el, options.context); 1747 | 1748 | options.position = new google.maps.LatLng(options.lat, options.lng); 1749 | 1750 | delete options.el; 1751 | delete options.context; 1752 | delete options.lat; 1753 | delete options.lng; 1754 | 1755 | var streetview_events = ['closeclick', 'links_changed', 'pano_changed', 'position_changed', 'pov_changed', 'resize', 'visible_changed'], 1756 | streetview_options = extend_object({visible : true}, options); 1757 | 1758 | for (var i = 0; i < streetview_events.length; i++) { 1759 | delete streetview_options[streetview_events[i]]; 1760 | } 1761 | 1762 | var panorama = new google.maps.StreetViewPanorama(el, streetview_options); 1763 | 1764 | for (var i = 0; i < streetview_events.length; i++) { 1765 | (function(object, name) { 1766 | if (options[name]) { 1767 | google.maps.event.addListener(object, name, function(){ 1768 | options[name].apply(this); 1769 | }); 1770 | } 1771 | })(panorama, streetview_events[i]); 1772 | } 1773 | 1774 | return panorama; 1775 | }; 1776 | 1777 | GMaps.prototype.on = function(event_name, handler) { 1778 | return GMaps.on(event_name, this, handler); 1779 | }; 1780 | 1781 | GMaps.prototype.off = function(event_name) { 1782 | GMaps.off(event_name, this); 1783 | }; 1784 | 1785 | GMaps.custom_events = ['marker_added', 'marker_removed', 'polyline_added', 'polyline_removed', 'polygon_added', 'polygon_removed', 'geolocated', 'geolocation_failed']; 1786 | 1787 | GMaps.on = function(event_name, object, handler) { 1788 | if (GMaps.custom_events.indexOf(event_name) == -1) { 1789 | return google.maps.event.addListener(object, event_name, handler); 1790 | } 1791 | else { 1792 | var registered_event = { 1793 | handler : handler, 1794 | eventName : event_name 1795 | }; 1796 | 1797 | object.registered_events[event_name] = object.registered_events[event_name] || []; 1798 | object.registered_events[event_name].push(registered_event); 1799 | 1800 | return registered_event; 1801 | } 1802 | }; 1803 | 1804 | GMaps.off = function(event_name, object) { 1805 | if (GMaps.custom_events.indexOf(event_name) == -1) { 1806 | google.maps.event.clearListeners(object, event_name); 1807 | } 1808 | else { 1809 | object.registered_events[event_name] = []; 1810 | } 1811 | }; 1812 | 1813 | GMaps.fire = function(event_name, object, scope) { 1814 | if (GMaps.custom_events.indexOf(event_name) == -1) { 1815 | google.maps.event.trigger(object, event_name, Array.prototype.slice.apply(arguments).slice(2)); 1816 | } 1817 | else { 1818 | if(event_name in scope.registered_events) { 1819 | var firing_events = scope.registered_events[event_name]; 1820 | 1821 | for(var i = 0; i < firing_events.length; i++) { 1822 | (function(handler, scope, object) { 1823 | handler.apply(scope, [object]); 1824 | })(firing_events[i]['handler'], scope, object); 1825 | } 1826 | } 1827 | } 1828 | }; 1829 | 1830 | GMaps.geolocate = function(options) { 1831 | var complete_callback = options.always || options.complete; 1832 | 1833 | if (navigator.geolocation) { 1834 | navigator.geolocation.getCurrentPosition(function(position) { 1835 | options.success(position); 1836 | 1837 | if (complete_callback) { 1838 | complete_callback(); 1839 | } 1840 | }, function(error) { 1841 | options.error(error); 1842 | 1843 | if (complete_callback) { 1844 | complete_callback(); 1845 | } 1846 | }, options.options); 1847 | } 1848 | else { 1849 | options.not_supported(); 1850 | 1851 | if (complete_callback) { 1852 | complete_callback(); 1853 | } 1854 | } 1855 | }; 1856 | 1857 | GMaps.geocode = function(options) { 1858 | this.geocoder = new google.maps.Geocoder(); 1859 | var callback = options.callback; 1860 | if (options.hasOwnProperty('lat') && options.hasOwnProperty('lng')) { 1861 | options.latLng = new google.maps.LatLng(options.lat, options.lng); 1862 | } 1863 | 1864 | delete options.lat; 1865 | delete options.lng; 1866 | delete options.callback; 1867 | 1868 | this.geocoder.geocode(options, function(results, status) { 1869 | callback(results, status); 1870 | }); 1871 | }; 1872 | 1873 | //========================== 1874 | // Polygon containsLatLng 1875 | // https://github.com/tparkin/Google-Maps-Point-in-Polygon 1876 | // Poygon getBounds extension - google-maps-extensions 1877 | // http://code.google.com/p/google-maps-extensions/source/browse/google.maps.Polygon.getBounds.js 1878 | if (!google.maps.Polygon.prototype.getBounds) { 1879 | google.maps.Polygon.prototype.getBounds = function(latLng) { 1880 | var bounds = new google.maps.LatLngBounds(); 1881 | var paths = this.getPaths(); 1882 | var path; 1883 | 1884 | for (var p = 0; p < paths.getLength(); p++) { 1885 | path = paths.getAt(p); 1886 | for (var i = 0; i < path.getLength(); i++) { 1887 | bounds.extend(path.getAt(i)); 1888 | } 1889 | } 1890 | 1891 | return bounds; 1892 | }; 1893 | } 1894 | 1895 | if (!google.maps.Polygon.prototype.containsLatLng) { 1896 | // Polygon containsLatLng - method to determine if a latLng is within a polygon 1897 | google.maps.Polygon.prototype.containsLatLng = function(latLng) { 1898 | // Exclude points outside of bounds as there is no way they are in the poly 1899 | var bounds = this.getBounds(); 1900 | 1901 | if (bounds !== null && !bounds.contains(latLng)) { 1902 | return false; 1903 | } 1904 | 1905 | // Raycast point in polygon method 1906 | var inPoly = false; 1907 | 1908 | var numPaths = this.getPaths().getLength(); 1909 | for (var p = 0; p < numPaths; p++) { 1910 | var path = this.getPaths().getAt(p); 1911 | var numPoints = path.getLength(); 1912 | var j = numPoints - 1; 1913 | 1914 | for (var i = 0; i < numPoints; i++) { 1915 | var vertex1 = path.getAt(i); 1916 | var vertex2 = path.getAt(j); 1917 | 1918 | if (vertex1.lng() < latLng.lng() && vertex2.lng() >= latLng.lng() || vertex2.lng() < latLng.lng() && vertex1.lng() >= latLng.lng()) { 1919 | if (vertex1.lat() + (latLng.lng() - vertex1.lng()) / (vertex2.lng() - vertex1.lng()) * (vertex2.lat() - vertex1.lat()) < latLng.lat()) { 1920 | inPoly = !inPoly; 1921 | } 1922 | } 1923 | 1924 | j = i; 1925 | } 1926 | } 1927 | 1928 | return inPoly; 1929 | }; 1930 | } 1931 | 1932 | google.maps.LatLngBounds.prototype.containsLatLng = function(latLng) { 1933 | return this.contains(latLng); 1934 | }; 1935 | 1936 | google.maps.Marker.prototype.setFences = function(fences) { 1937 | this.fences = fences; 1938 | }; 1939 | 1940 | google.maps.Marker.prototype.addFence = function(fence) { 1941 | this.fences.push(fence); 1942 | }; 1943 | 1944 | google.maps.Marker.prototype.getId = function() { 1945 | return this['__gm_id']; 1946 | }; 1947 | 1948 | //========================== 1949 | // Array indexOf 1950 | // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf 1951 | if (!Array.prototype.indexOf) { 1952 | Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { 1953 | "use strict"; 1954 | if (this == null) { 1955 | throw new TypeError(); 1956 | } 1957 | var t = Object(this); 1958 | var len = t.length >>> 0; 1959 | if (len === 0) { 1960 | return -1; 1961 | } 1962 | var n = 0; 1963 | if (arguments.length > 1) { 1964 | n = Number(arguments[1]); 1965 | if (n != n) { // shortcut for verifying if it's NaN 1966 | n = 0; 1967 | } else if (n != 0 && n != Infinity && n != -Infinity) { 1968 | n = (n > 0 || -1) * Math.floor(Math.abs(n)); 1969 | } 1970 | } 1971 | if (n >= len) { 1972 | return -1; 1973 | } 1974 | var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); 1975 | for (; k < len; k++) { 1976 | if (k in t && t[k] === searchElement) { 1977 | return k; 1978 | } 1979 | } 1980 | return -1; 1981 | } 1982 | } -------------------------------------------------------------------------------- /template/album/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BuildFarBox/default-template/b0e25db0f48e830ca58ed99df86a962ce163fdf1/template/album/loading.gif -------------------------------------------------------------------------------- /template/album/style.scss: -------------------------------------------------------------------------------- 1 | $img-width: 320px; 2 | $img-height: 214px; 3 | $content-high-color: #099; 4 | 5 | body { 6 | -webkit-text-size-adjust:none; 7 | background-color:#fefdfa; 8 | font-family: sans-serif; 9 | 10 | } 11 | 12 | .body_wrap{ 13 | margin: 2.5em 6em; 14 | position: relative; 15 | 16 | h1{ 17 | color: #a9b1b3; 18 | font-size: 3.2em; 19 | margin: 0; 20 | margin-bottom: 0.5em; 21 | padding-left: 5px; 22 | } 23 | 24 | .home{ 25 | position: absolute; 26 | right: 0; 27 | top: 0; 28 | 29 | a{ 30 | font-size: 14px; 31 | padding: 5px; 32 | color: #666; 33 | text-decoration: none; 34 | 35 | &:hover{ 36 | background: red; 37 | color: white; 38 | } 39 | } 40 | } 41 | 42 | } 43 | 44 | 45 | 46 | 47 | .albums{ 48 | list-style: none; 49 | display: inline-block; 50 | padding: 0; 51 | marging: 0; 52 | width: 100%; 53 | 54 | .album{ 55 | float: left; 56 | margin: 30px 0; 57 | 58 | a{ 59 | display: block; 60 | position: relative; 61 | text-decoration: none; 62 | 63 | font-family: sans-serif; 64 | color: #555; 65 | 66 | .img_container{ 67 | border: 8px solid #f2f2f2; 68 | width: 120px; 69 | height: 120px; 70 | border-radius: 100%; 71 | } 72 | 73 | &:hover .img_container{ 74 | border: 8px solid #eaeaea; 75 | 76 | } 77 | 78 | img{ 79 | 80 | width: 120px; 81 | height: 120px; 82 | border-radius: 100%; 83 | } 84 | 85 | .title{ 86 | position: absolute; 87 | top: 60px; 88 | left: 145px; 89 | margin: 0.05em 0; 90 | height: 1.1em; 91 | overflow: hidden; 92 | } 93 | 94 | 95 | } 96 | 97 | } 98 | 99 | } 100 | 101 | 102 | 103 | .bread_nav{ 104 | display: inline-block; 105 | padding: 0 10px; 106 | margin: 1em 3% 0 3%; 107 | color: #333; 108 | font-size: 1.2em; 109 | 110 | li{ 111 | list-style-type: none; 112 | float: left; 113 | margin: 0 10px 0 0; 114 | padding: 0; 115 | } 116 | 117 | a{ 118 | color: #a9b1b3; 119 | text-decoration: none; 120 | &:hover{ 121 | text-decoration: underline; 122 | } 123 | } 124 | 125 | } 126 | 127 | 128 | 129 | .boxes{ 130 | width: 95%; 131 | margin: 0 auto; 132 | font-size: 12px; 133 | 134 | .map-box, .photo-box, .text-box{ 135 | height: $img-height+80px; 136 | margin: 20px 0; 137 | } 138 | 139 | .map-box{ 140 | display: none; 141 | 142 | img{ 143 | max-width: none; 144 | } 145 | .box-container{ 146 | width: 92%; 147 | margin: 0 auto; 148 | position: relative; 149 | } 150 | 151 | #map{ 152 | height: $img-height+70px; 153 | } 154 | 155 | } 156 | 157 | } 158 | 159 | 160 | .photo-box { 161 | overflow: hidden; 162 | position: relative; 163 | 164 | 165 | 166 | .box-container { 167 | width: $img-width; 168 | margin: 0 auto; 169 | position: relative; 170 | 171 | a{ 172 | border: 1px solid #dedede; 173 | border-radius: 5px; 174 | padding: 7px; 175 | display: block; 176 | text-decoration: none; 177 | color: #555; 178 | font-size: 14px; 179 | 180 | .sub_folder{ 181 | position: absolute; 182 | display: none; 183 | background: rgba(0, 0, 0, 0.5); 184 | width: 100%; 185 | top: 40%; 186 | left: 0; 187 | padding: 10px 0; 188 | color: #fff; 189 | text-align: center; 190 | 191 | } 192 | 193 | 194 | &:hover{ 195 | color: #111; 196 | border: 1px solid #bababa; 197 | 198 | .sub_folder{ 199 | display: block; 200 | } 201 | } 202 | 203 | 204 | 205 | .caption{ 206 | display: block; 207 | position: relative; 208 | margin-top: 10px; 209 | border-top: 1px solid #e6e6e6; 210 | padding: 10px 0 0 0; 211 | height: 30px; 212 | line-height: 30px; 213 | overflow: hidden; 214 | 215 | b{ 216 | padding: 0 10px; 217 | width: 80%; 218 | overflow: hidden; 219 | font-size: 1em; 220 | position: absolute; 221 | top: 10px; 222 | left: 0; 223 | } 224 | 225 | .image-date{ 226 | font-family: Geneva; 227 | position: absolute; 228 | background: #fefdfa; 229 | right: 0; 230 | bottom: 0; 231 | padding: 0 5px; 232 | color: #cccccc; 233 | font-size: 10px; 234 | } 235 | } 236 | 237 | img { 238 | width: $img-width; 239 | height: $img-height; 240 | } 241 | 242 | } 243 | 244 | } 245 | 246 | 247 | } 248 | 249 | .image-viewer { 250 | z-index: 9999; 251 | position: fixed; 252 | width: 100%; 253 | height: 100%; 254 | 255 | left: 0; 256 | top: 0; 257 | 258 | background: rgba(0,0,0,0.5); 259 | 260 | .wrap{ 261 | height: 100%; 262 | width: 80%; 263 | margin: 4% auto 0 auto; 264 | position: relative; 265 | } 266 | 267 | 268 | .head { 269 | z-index: 1000; 270 | border-top-left-radius: 10px; 271 | border-top-right-radius: 10px; 272 | 273 | position: relative; 274 | height: 42px; 275 | background: #999; 276 | 277 | box-shadow: 0 0 10px 3px rgba(255, 255, 255, 0.7); 278 | 279 | 280 | .caption { 281 | position: absolute; 282 | left: 15px; 283 | top: 15px; 284 | color: #fff; 285 | z-index: 100; 286 | background: #999; 287 | 288 | } 289 | 290 | .image-info{ 291 | position: absolute; 292 | width: 90%; 293 | margin: 0 5%; 294 | text-align: center; 295 | z-index: 1; 296 | left: 0; 297 | top: 15px; 298 | 299 | font-size: 12px; 300 | 301 | color: #f3f3f3; 302 | 303 | 304 | 305 | 306 | .exif-field{ 307 | margin-right: 10px; 308 | } 309 | } 310 | 311 | .commands { 312 | position: absolute; 313 | right: 0; 314 | top: 12px; 315 | z-index: 100; 316 | background: #999; 317 | 318 | a { 319 | font-size: 14px; 320 | text-decoration: none; 321 | color: #fff; 322 | margin-right: 25px; 323 | 324 | &:hover { 325 | color: red; 326 | } 327 | } 328 | } 329 | } 330 | 331 | 332 | .body { 333 | position: relative; 334 | border-bottom-left-radius: 10px; 335 | border-bottom-right-radius: 10px; 336 | 337 | box-shadow: 0 0 10px 3px rgba(255, 255, 255, 0.7); 338 | 339 | background: #fefdfa; 340 | overflow-x: hidden; 341 | overflow-y: auto; 342 | 343 | max-height: 80%; 344 | 345 | .image { 346 | position: relative; 347 | z-index: 999; 348 | cursor: pointer; 349 | background: url(loading.gif) center center no-repeat; 350 | 351 | 352 | img { 353 | display: block; 354 | margin: 0 auto; 355 | max-width: 100%; 356 | max-height: 100%; 357 | min-height: 100px; 358 | 359 | } 360 | 361 | img, .loaded { 362 | -webkit-transition: opacity 0.3s; 363 | -moz-transition: opacity 0.3s; 364 | -ms-transition: opacity 0.3s; 365 | -o-transition: opacity 0.3s; 366 | transition: opacity 0.3s; 367 | opacity: 0; 368 | } 369 | 370 | .loaded { opacity: 1; } 371 | } 372 | 373 | 374 | 375 | } 376 | 377 | 378 | #fb-comments-container { 379 | z-index: 1001; 380 | height: 80%; 381 | width: 100%; 382 | margin-top: 42px; 383 | 384 | background: #fff; 385 | 386 | position: absolute; 387 | right: 0; 388 | top: 0; 389 | 390 | overflow-y: auto; 391 | overflow-x: hidden; 392 | 393 | box-shadow: 1px 1px 10px 3px rgba(255, 255, 255, 0.7); 394 | 395 | .main-container { 396 | padding: 0 6%; 397 | } 398 | 399 | } 400 | 401 | 402 | 403 | 404 | 405 | .connected { 406 | position: absolute; 407 | z-index: 998; 408 | top: 0; 409 | width: 100%; 410 | height: 100%; 411 | 412 | a { 413 | display: block; 414 | position: absolute; 415 | top: 42%; 416 | height: 50px; 417 | width: 50px; 418 | border-radius: 100%; 419 | 420 | text-decoration: none; 421 | color: #666; 422 | background-color: #eee; 423 | font-size: 1.2em; 424 | text-align: center; 425 | 426 | i { 427 | font-size: 30px; 428 | line-height: 50px; 429 | } 430 | 431 | &:hover { 432 | background: red; 433 | color: #fff; 434 | } 435 | } 436 | 437 | .pre { 438 | left: -50px; 439 | } 440 | 441 | .next { 442 | right: -50px; 443 | } 444 | 445 | } 446 | 447 | } 448 | 449 | 450 | 451 | .clear{ 452 | clear: both; 453 | width: 0; 454 | height:0; 455 | } 456 | 457 | /*footer starts*/ 458 | #footer { 459 | margin: 80px 0 30px 0; 460 | text-align: center; 461 | font-size: 11px; 462 | height: 1em; 463 | font-family: Arial; 464 | color: #ccc; 465 | 466 | overflow: hidden; 467 | 468 | 469 | a { 470 | color: #bdbdbd; 471 | margin: 0 10px; 472 | 473 | &:hover { 474 | text-decoration: none; 475 | color: $content-high-color; 476 | } 477 | } 478 | 479 | a.admin{ 480 | color: darkred; 481 | font-weight: bold; 482 | } 483 | 484 | .powered_by { 485 | margin-bottom: 20px; 486 | text-align: center; 487 | 488 | span { 489 | font-size: 9px; 490 | padding: 0 15px; 491 | } 492 | } 493 | 494 | } 495 | 496 | 497 | 498 | 499 | .round { 500 | -moz-border-radius: 100%; 501 | -webkit-border-radius: 100%; 502 | border-radius: 100%; 503 | display: block; 504 | 505 | } 506 | 507 | /* paginator */ 508 | .pager { 509 | position: relative; 510 | height: 57px; 511 | margin: 50px 2.5% 0 2.5%; 512 | 513 | a { 514 | height: 57px; 515 | width: 57px; 516 | line-height: 57px; 517 | 518 | color: #333; 519 | letter-spacing: 1px; 520 | font-size: 12px; 521 | text-decoration: none; 522 | 523 | text-align: center; 524 | border: 1px solid #d3d3d3; 525 | 526 | &:hover { 527 | color: #fff; 528 | background: $content-high-color; 529 | } 530 | } 531 | 532 | .next { 533 | position: absolute; 534 | right: 0; 535 | } 536 | 537 | .pre { 538 | position: absolute; 539 | left: 0; 540 | } 541 | } -------------------------------------------------------------------------------- /template/archive+tags.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | 3 | block content 4 | if request.path.strip('/')=='tags' 5 | entries = get_data(type='post',limit=300, sort='desc').group('tags') 6 | else 7 | entries = get_data(type='post',limit=300, sort='desc').group('-date:year') 8 | 9 | ul.list_with_title.container 10 | for sub_title, posts in entries 11 | h3= sub_title 12 | ul.listing 13 | for post in posts 14 | li.listing_item 15 | a(href=post.url, title=post.title)= post.title 16 | span.date= post.date.format("%Y-%m-%d") 17 | 18 | include include/paginator.jade 19 | -------------------------------------------------------------------------------- /template/base.jade: -------------------------------------------------------------------------------- 1 | html 2 | +i18n('Home', '首页', 'zh_cn') 3 | +i18n('Albums', '相册', 'zh_cn') 4 | +i18n('Categories', '分类', 'zh_cn') 5 | +i18n('Archive', '归档', 'zh_cn') 6 | +i18n('Tags', '标签', 'zh_cn') 7 | +i18n('About', '关于', 'zh_cn') 8 | +i18n('Links', '友链', 'zh_cn') 9 | head 10 | +mobile_meta() 11 | meta(name="keywords", content=site.configs.keywords.escaped) 12 | meta(name="description", content=site.raw_content.escaped) 13 | block title 14 | title= request.args.s or post.title or tags.join('+') or category.title or site.title 15 | load('fonts /template/blog/style.scss') 16 | +get_resource("blog_basic.css") 17 | body 18 | #body_wrapper 19 | if site.configs.cover and has(site.configs.cover) 20 | site_cover = site.configs.cover 21 | else 22 | site_cover = '/farbox_free_image.jpg' 23 | site_cover = site_cover + '?height=428&width=1280&fixed=true' 24 | #header.header(style="background-image: url({{site_cover}})") 25 | .header_wrapper 26 | h1.title 27 | a(href='/')= site.title 28 | ul.nav 29 | li 30 | a(href="/")= _('Home') 31 | if has('images') 32 | li 33 | a(href='/album')= _('Albums') 34 | if not has('posts') 35 | +redirect('/album') 36 | if has('categories') 37 | li 38 | a(href='/categories')= _('Categories') 39 | if has('posts') 40 | li 41 | a(href='/archive')= _('Archive') 42 | if site.tags 43 | li 44 | a(href='/tags')= _('Tags') 45 | pages = get_data(status='pages', type='post', limit=5, with_page=False, sort='position') 46 | for page_p in pages 47 | li 48 | a(href="/{{page_p.path}}")= _(page_p.filename.split('.')[0].title()) 49 | li 50 | a(href="/feed") RSS 51 | #main 52 | block content 53 | #footer 54 | .powered_by 55 | a(href="https://www.farbox.com", target="_blank") Powered By FarBox.com 56 | a(href="http://colachan.com", target="_blank") Designer ColaChan 57 | a.admin(href="/admin") Admin 58 | span ©2012-2015 Z.R.E.Y Inc. 59 | 60 | +load('jquery') 61 | +load('script.coffee') 62 | 63 | -------------------------------------------------------------------------------- /template/blog/script.coffee: -------------------------------------------------------------------------------- 1 | #获取屏幕宽度,判断设备类型 2 | getWinSize = -> 3 | phoneLandscape = 568 4 | padPortrait = 768 5 | padLandscape = 1024 6 | pc = 1200 7 | winWidth = $(window).width() 8 | if winWidth < phoneLandscape #手机竖屏 9 | return 1 10 | else if winWidth >= phoneLandscape and winWidth < padPortrait #手机横屏 11 | return 2 12 | else if winWidth >= padPortrait and winWidth < padLandscape #平板 13 | return 3 14 | else if winWidth >= padLandscape and winWidth < pc #平板 15 | return 4 16 | else # PC 17 | return 5 18 | 19 | 20 | on_loading = false 21 | finish_loading = false 22 | next_page = 2 23 | 24 | 25 | @auto_list_layout = -> 26 | for post in $(".list .post") 27 | post_dom = $(post) 28 | 29 | if post_dom.find('a').length and getWinSize() < 3 # 移动端的整个dom点击事件 30 | link = post_dom.find('a').attr('href') 31 | post_dom.unbind('click') 32 | post_dom.click -> 33 | location.href = link 34 | 35 | if post_dom.find('img').length 36 | post_dom.addClass('img') 37 | if getWinSize() < 3 #如果是手机,将第一张图片作为背景图 38 | post_dom.css("background-image", "url(" + post_dom.find("img").attr("src") + ")") 39 | else #否则清除背景图 40 | post_dom.removeAttr('style') 41 | else 42 | post_dom.addClass('text') 43 | 44 | if getWinSize() < 3 # 移动端 45 | for img in $(".list .img") 46 | img_dom = $(img) 47 | img_dom.css("background-image", "url(" + img_dom.find("img").attr("src") + ")"); 48 | else 49 | $(".list .img").removeAttr("style") 50 | 51 | $(document).ready -> 52 | if $(".list").length #处于列表页 53 | auto_list_layout() 54 | 55 | $(window).resize(auto_list_layout) 56 | 57 | 58 | $(window).scroll -> 59 | #st = $(window).scrollTop() 60 | bottom_diff = $(document).height() - $(window).height() - $(window).scrollTop() 61 | # mobile & list page only 62 | if getWinSize() < 3 and bottom_diff < 150 and $('.list').length and not on_loading and not finish_loading and location.search.indexOf('?s=')==-1 63 | on_loading = true 64 | $('#on_loading').removeClass('hidden') 65 | url = location.pathname + 'page/' + next_page 66 | url = url.replace(/\/{2,}/g, '/') 67 | $.get url,{'pjax': 'true'}, (data) -> 68 | if data.length < 20 69 | finish_loading = true 70 | else 71 | $('.list').append(data) 72 | auto_list_layout() 73 | next_page += 1 74 | on_loading = false 75 | $('#on_loading').addClass('hidden') 76 | 77 | 78 | -------------------------------------------------------------------------------- /template/blog/style.scss: -------------------------------------------------------------------------------- 1 | //*********************************************************************// 2 | // settings // 3 | //*********************************************************************// 4 | 5 | 6 | // value settings 7 | $font_color: #191919; 8 | $bg1: #fff; 9 | $primary: #58b798; 10 | 11 | 12 | // related colors 13 | $font_color2: lighten($font_color, 45%); 14 | $bg2: darken($bg1, 3.6%); 15 | $line_color: lighten($font_color, 80%); 16 | $line_color2: lighten($font_color, 82%); 17 | 18 | // fixed colors 19 | $shadow_color: rgba(0,0,0,0.18); 20 | $easing1: cubic-bezier(0.15, 0.95, 0.41, 0.95); 21 | 22 | // responsive break points 23 | $phone_landscape-min: 33.37em; //534px 24 | $phone_landscape-max: 47.99em; //767px 25 | 26 | 27 | .border_container { 28 | box-shadow: 0 1px 5px $shadow_color; 29 | border: 1px solid #eee; 30 | border-radius: 0.2em; 31 | } 32 | 33 | 34 | 35 | // functions 36 | @mixin box-sizing ($value) { 37 | box-sizing: $value; 38 | -webkit-box-sizing: $value; 39 | } 40 | @mixin transform ($value) { 41 | transform: $value; 42 | -webkit-transform: $value; 43 | } 44 | 45 | @mixin aColor ($value) { 46 | color: $value; 47 | &:active { 48 | color: $value; 49 | } 50 | &:visited { 51 | color: $value; 52 | } 53 | &:hover { 54 | color: lighten($value, 5%); 55 | border-bottom: 1px solid lighten($value, 5%); 56 | } 57 | } 58 | 59 | 60 | 61 | 62 | //*********************************************************************// 63 | // global // 64 | //*********************************************************************// 65 | 66 | 67 | * { 68 | @include box-sizing (content-box); 69 | } 70 | html, body{ 71 | height:100%; 72 | } 73 | body { 74 | line-height: 1.6; 75 | //font-family: "droid-sans", "stheiti", 'hei', 'Hiragino Sans GB',"microsoft yahei", sans-serif; 76 | font-family: "Georgia", "Xin Gothic", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei", sans-serif; 77 | font-size: 1em; 78 | color: $font_color; 79 | background: $bg1; 80 | overflow-x: hidden; 81 | } 82 | 83 | 84 | a { 85 | @include aColor($primary); 86 | text-decoration: none; 87 | } 88 | 89 | input, textarea, button { 90 | font-family: "droid-sans", "stheiti", "microsoft yahei", sans-serif; 91 | } 92 | ::selection { 93 | background: $primary; 94 | color: #fff; 95 | } 96 | ::-moz-selection { 97 | background: $primary; 98 | color: #fff; 99 | } 100 | 101 | 102 | .hidden { 103 | display: none; 104 | } 105 | .clear { 106 | width:0; height:0; 107 | clear: both; 108 | } 109 | 110 | #on_loading{ 111 | height: 3em; 112 | line-height: 3em; 113 | text-align: center; 114 | } 115 | 116 | 117 | 118 | //*********************************************************************// 119 | // header // 120 | //*********************************************************************// 121 | 122 | #header { 123 | background: #303538 no-repeat center center; 124 | background-size: cover; 125 | height: 260px; 126 | 127 | .header_wrapper { 128 | padding-top:80px; 129 | 130 | h1 { 131 | padding: 0; 132 | margin: 0; 133 | font-size: 2.0em; 134 | text-align: center; 135 | a { 136 | letter-spacing: -1px; 137 | font-weight: normal; 138 | text-shadow: 2px 2px #555; 139 | color: #fff; 140 | text-decoration: none; 141 | &:hover { 142 | text-decoration: none; 143 | border-bottom: 1px solid #fff; 144 | } 145 | } 146 | } 147 | 148 | .nav { 149 | list-style: none; 150 | display: block; 151 | text-align: center; 152 | clear: both; 153 | padding: 0; 154 | 155 | li { 156 | position: relative; 157 | display: inline-block; 158 | *display: inline; 159 | 160 | a { 161 | color: #fff; 162 | font-size: 12px; 163 | margin: 0 1em; 164 | text-shadow: 1px 1px #333; 165 | 166 | &:hover{ 167 | border-bottom-color: #fff; 168 | } 169 | } 170 | } 171 | 172 | } 173 | } 174 | 175 | } 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | //*********************************************************************// 184 | // post in list or detail // 185 | //*********************************************************************// 186 | 187 | 188 | 189 | .post{ 190 | .post_body { 191 | img { 192 | max-width: 100%; 193 | } 194 | p { 195 | line-height: 1.8; 196 | margin: 0 0 1.7em; 197 | br{ 198 | line-height: 2.1; 199 | } 200 | } 201 | a { 202 | @include aColor($primary); 203 | &:hover{ 204 | border-bottom: 1px solid $primary; 205 | } 206 | } 207 | h1, h2, h3, h4, h5, h6 { 208 | font-weight: bold; 209 | } 210 | h1 { 211 | margin: 0 0 0.5em; 212 | font-size: 2.4em; 213 | } 214 | h2 { 215 | margin: 0 0 0.6em; 216 | font-size: 2em; 217 | } 218 | h3 { 219 | margin: 0 0 0.8em; 220 | font-size: 1.5em; 221 | } 222 | h4 { 223 | margin: 0 0 1em; 224 | font-size: 1.2em; 225 | } 226 | h5 { 227 | margin: 0 0 1.2em; 228 | font-size: 1em; 229 | } 230 | h6 { 231 | margin: 0 0 1.5em; 232 | font-size: 0.8em; 233 | } 234 | i, em { 235 | font-style: italic; 236 | } 237 | b, strong { 238 | font-weight: bold; 239 | } 240 | ul { 241 | padding: 0 0 0 1em; 242 | list-style: disc; 243 | } 244 | ol { 245 | margin: 0 0 1.2em; 246 | padding: 0 0 0 2em; 247 | list-style: decimal; 248 | } 249 | blockquote { 250 | padding: 1.5em 1.8em; 251 | margin: 0 0 1.2em; 252 | background: lighten($line_color, 8%);; 253 | border-left: 2px solid darken($line_color, 10%); 254 | font-size: 0.8em; 255 | p { 256 | margin: 0; 257 | } 258 | } 259 | hr { 260 | width: 100%; 261 | height: 1px; 262 | margin: 3em 0; 263 | padding: 0; 264 | background: none; 265 | border: none; 266 | border-bottom: 1px solid #eee; 267 | } 268 | 269 | } 270 | 271 | .info { 272 | color: $font_color2; 273 | line-height: 1.5; 274 | font-size: 0.75em; 275 | margin-top: 2.5em; 276 | i{ 277 | margin-right: 5px; 278 | position: relative; 279 | top: -1px; 280 | font-size: 1.1em; 281 | } 282 | span{ 283 | font-family: droid-sans, stheiti, 'microsoft yahei', sans-serif;; 284 | } 285 | .date { 286 | margin: 0 1.5em 0 0; 287 | } 288 | .visitors { 289 | margin: 0 1.5em 0 0; 290 | } 291 | .tags { 292 | a { 293 | margin: 0 0.5em 0 0; 294 | } 295 | i { 296 | font-size: 10px; 297 | margin-right: 5px; 298 | } 299 | } 300 | 301 | .go_editor, .show_raw{ 302 | color: $font_color2; 303 | margin-right: 10px; 304 | &:hover{ 305 | color: $primary; 306 | border-bottom: none; 307 | } 308 | .fa-edit{ 309 | top: 1px; 310 | } 311 | } 312 | .go_editor{ 313 | margin-right: 25px; 314 | } 315 | } 316 | } 317 | 318 | 319 | 320 | 321 | 322 | 323 | //****************************** post detail *******************************// 324 | 325 | .detail{ 326 | position: relative; 327 | @extend .border_container; 328 | 329 | .content { 330 | padding: 2.5em; 331 | 332 | .title { 333 | margin: 0 0 1em; 334 | padding: 0 0 1em; 335 | border-bottom: 1px solid $line_color; 336 | font-size: 2em; 337 | } 338 | } 339 | 340 | 341 | .other_posts { 342 | bottom: 0; 343 | left: 0; 344 | width: 100%; 345 | border-bottom: 1px solid $line_color2; 346 | border-top: 1px solid $line_color2; 347 | height: 4em; 348 | overflow: hidden; 349 | line-height: 4em; 350 | font-size: 1.0em; 351 | position: relative; 352 | a{ 353 | &:hover{ 354 | color: #fff; 355 | background: $primary; 356 | } 357 | } 358 | .pre { 359 | position: absolute; 360 | text-align: left; 361 | width: 50%; 362 | left: 0; 363 | top: 0; 364 | overflow: hidden; 365 | border-right: 1px solid $line_color2; 366 | span{ 367 | margin-left: 1.5em; 368 | } 369 | } 370 | .next { 371 | position: absolute; 372 | text-align: right; 373 | width: 50%; 374 | right: 0; 375 | top: 0; 376 | overflow: hidden; 377 | span{ 378 | margin-right: 1.5em; 379 | } 380 | } 381 | } 382 | } 383 | 384 | 385 | 386 | 387 | 388 | //****************************** post list starts *******************************// 389 | // pad and pc 390 | .list { 391 | 392 | .post { 393 | margin: 0 0 4.5em; 394 | @extend .border_container; 395 | border: 1px solid #f0f0f0; 396 | border-radius: 0.2em; 397 | .content { 398 | padding: 3em; 399 | } 400 | .title { 401 | font-size: 1.875em; 402 | font-weight: normal; 403 | margin: 0 0 1em; 404 | padding: 0 0 1em; 405 | border-bottom: 1px solid $line_color; 406 | 407 | a{ 408 | color: darken($font_color, 10%); 409 | 410 | &:hover{ 411 | color: $primary; 412 | border-bottom: none; 413 | } 414 | } 415 | } 416 | .post_body { 417 | img { 418 | max-width: 20em; 419 | } 420 | } 421 | 422 | } 423 | } 424 | 425 | // phone_landscape 426 | @media only screen and (min-width: $phone_landscape-min) and (max-width: $phone_landscape-max){ 427 | .list { 428 | .post { 429 | float: left; 430 | width: 50%; 431 | border: none; 432 | 433 | .title{ 434 | padding: 0; 435 | border-bottom: none; 436 | } 437 | } 438 | } 439 | } 440 | // phone 441 | @media only screen and (max-width: $phone_landscape-max){ 442 | .list { 443 | .post { 444 | position: relative; 445 | box-shadow: 0 0.1em 0.1em $shadow_color; 446 | border: none; 447 | margin: 0; 448 | .content { 449 | height: 7.475em; 450 | overflow: hidden; 451 | padding: 0.95em; 452 | box-shadow: inset 0 0 0.1em $line_color; 453 | .title { 454 | height: 1.2em; 455 | font-size: 1.125em; 456 | overflow: hidden; 457 | margin: 0.5em 0 0.4em; 458 | 459 | padding: 0; 460 | border-bottom: none; 461 | 462 | a { 463 | font-weight: bold; 464 | } 465 | } 466 | .post_body { 467 | margin: 0 0 0.5em; 468 | font-size: 0.875em; 469 | height: 3.5em; 470 | overflow: hidden; 471 | p{ 472 | margin: 0; 473 | } 474 | } 475 | 476 | } 477 | } 478 | .text { 479 | .content { 480 | .title a { 481 | @include aColor ($font_color); 482 | } 483 | .post_body { 484 | height: 4.3em; 485 | overflow: hidden; 486 | p { 487 | line-height: 1.5; 488 | margin: 0; 489 | } 490 | } 491 | .info { 492 | color: $font_color2; 493 | } 494 | } 495 | } 496 | .img { 497 | background-repeat: no-repeat; 498 | background-position: center center; 499 | background-size: cover; 500 | .content { 501 | .title { 502 | @include transform (translate(0, 4em)); 503 | a { 504 | @include aColor (#fff); 505 | } 506 | } 507 | .post_body { 508 | display: none; 509 | } 510 | .info { 511 | color: #fff; 512 | position: relative; 513 | bottom: 0; 514 | @include transform (translate(0, 5.2em)); 515 | } 516 | } 517 | } 518 | .img:nth-child(3n+1) .content { 519 | background: rgba(83,123,110,0.85); 520 | } 521 | .img:nth-child(3n+2) .content { 522 | background: rgba(96,69,73,0.85); 523 | } 524 | .img:nth-child(3n+3) .content { 525 | background: rgba(175,163,145,0.85); 526 | } 527 | } 528 | } 529 | //****************************** post list ends *******************************// 530 | 531 | 532 | 533 | 534 | 535 | 536 | /* for categories*/ 537 | 538 | .categories{ 539 | list-style:none; 540 | display:block; 541 | 542 | clear: both; 543 | padding: 0; 544 | margin-top: 50px; 545 | 546 | a{ 547 | text-decoration: none; 548 | color: #333; 549 | } 550 | 551 | li{ 552 | font-size:16px; 553 | display:inline-block; 554 | color: #fff; 555 | *display:inline; 556 | 557 | text-align:left; 558 | 559 | position: relative; 560 | 561 | h3{ 562 | font-size: 1.2em; 563 | word-wrap:break-word; 564 | overflow:hidden; 565 | font-weight: normal; 566 | } 567 | 568 | span{ 569 | color: #999; 570 | font-size: 0.8em; 571 | } 572 | 573 | a{ 574 | margin: 20px 40px; 575 | width: 160px; 576 | float: left; 577 | height: 180px; 578 | border: 1px solid #c8c8c8; 579 | background: #fff; 580 | padding: 60px 30px; 581 | 582 | &:hover{ 583 | border: 1px solid #b7b7b7; 584 | -webkit-box-shadow: 0 1px 5px rgba(0,0,0,0.2); 585 | -moz-box-shadow: 0 1px 5px rgba(0,0,0,0.2); 586 | box-shadow: 0 1px 5px rgba(0,0,0,0.2); 587 | 588 | h3{ 589 | padding-left: 2px; 590 | } 591 | } 592 | } 593 | } 594 | } 595 | 596 | 597 | 598 | /* for archive and tags*/ 599 | .list_with_title { 600 | font-size: 16px; 601 | background: $bg1; 602 | @extend .border_container; 603 | padding: 1.5em 3em; 604 | margin: 0; 605 | 606 | li { 607 | list-style-type: none; 608 | padding: 0; 609 | } 610 | 611 | h3 { 612 | font-weight: bold; 613 | line-height: 2.2em; 614 | font-size: 1.4em; 615 | color: #555; 616 | text-align: center; 617 | border-bottom: 1px solid $line_color; 618 | margin: 0; 619 | } 620 | 621 | .listing { 622 | margin: 0 0 50px 0; 623 | padding: 0; 624 | 625 | li { 626 | list-style-type: none; 627 | line-height: 1.3; 628 | border-bottom: 1px solid $line_color; 629 | position: relative; 630 | padding: 1em 0; 631 | text-align: left; 632 | font-weight: normal; 633 | *font-size: 18px; 634 | 635 | a { 636 | color: $font_color; 637 | text-decoration: none; 638 | padding-bottom: 1px; 639 | 640 | &:hover { 641 | color: darken($font_color, 10%); 642 | text-shadow: 1px 1px $line_color; 643 | border-bottom: none; 644 | } 645 | } 646 | 647 | &:hover { 648 | background: $bg2; 649 | } 650 | } 651 | } 652 | 653 | .date { 654 | float: right; 655 | color: #999999; 656 | font-size: 14px; 657 | margin-right: 5px; 658 | } 659 | } 660 | 661 | 662 | 663 | 664 | .round { 665 | -moz-border-radius: 100%; 666 | -webkit-border-radius: 100%; 667 | border-radius: 100%; 668 | display: block; 669 | 670 | } 671 | 672 | /* paginator */ 673 | .pager { 674 | position: relative; 675 | height: 57px; 676 | margin: 50px 0 0 0; 677 | 678 | a { 679 | height: 57px; 680 | width: 57px; 681 | line-height: 57px; 682 | 683 | color: #333; 684 | background: #fff; 685 | letter-spacing: 1px; 686 | font-size: 12px; 687 | text-decoration: none; 688 | 689 | text-align: center; 690 | border: 1px solid #d3d3d3; 691 | 692 | &:hover { 693 | color: #fff; 694 | background: $primary; 695 | } 696 | } 697 | 698 | .next { 699 | position: absolute; 700 | right: 0; 701 | } 702 | 703 | .pre { 704 | position: absolute; 705 | left: 0; 706 | } 707 | } 708 | 709 | 710 | 711 | 712 | 713 | /*footer starts*/ 714 | #footer { 715 | margin: 80px 0 30px 0; 716 | text-align: center; 717 | font-size: 11px; 718 | height: 1em; 719 | font-family: Arial; 720 | color: #ccc; 721 | 722 | a { 723 | color: #bdbdbd; 724 | margin: 0 10px; 725 | 726 | &:hover { 727 | text-decoration: none; 728 | color: $primary; 729 | } 730 | } 731 | 732 | a.admin { 733 | color: $primary; 734 | font-weight: bold; 735 | } 736 | 737 | .powered_by { 738 | margin-bottom: 20px; 739 | text-align: center; 740 | 741 | span { 742 | font-size: 9px; 743 | padding: 0 15px; 744 | } 745 | } 746 | 747 | } 748 | 749 | 750 | 751 | 752 | // main container 753 | 754 | #main, #fb_comments_container{ 755 | //padding: 1.875em 0; 756 | padding: 2.7em 0; 757 | margin: 1.5em 4%; 758 | } 759 | 760 | #main{ 761 | #fb_comments_container{ 762 | padding: 1em 0; 763 | 764 | #fb_comments{ 765 | padding: 0 2em; 766 | } 767 | 768 | #fb_new_comment{ 769 | margin-top: 3em; 770 | padding: 0 2em; 771 | } 772 | 773 | } 774 | } 775 | 776 | @media only screen and (min-width: 960px){ 777 | #main, #fb_comments_container{ 778 | //margin: 1.5em 8%; 779 | } 780 | } 781 | 782 | 783 | 784 | // for phone 785 | @media only screen and (max-width: $phone_landscape-max){ 786 | 787 | #header { 788 | width: 100%; 789 | height: 3.5em; 790 | .header_wrapper { 791 | height: 3.5em; 792 | padding-top: 0; 793 | background: #333; 794 | h1{ 795 | display: none; 796 | } 797 | .nav{ 798 | margin: 0; 799 | line-height: 3.5em; 800 | height: 3.5em; 801 | overflow: hidden; 802 | } 803 | } 804 | } 805 | 806 | // for loading 807 | #footer{ 808 | display: none; 809 | } 810 | .pager{ 811 | display: none; 812 | } 813 | 814 | .list { 815 | .post{ 816 | .info{ 817 | position: absolute; 818 | bottom: 5px; 819 | span.visitors, .tags, i{ 820 | display: none; 821 | } 822 | .show_raw{ 823 | display: none; 824 | } 825 | } 826 | } 827 | } 828 | 829 | .detail{ 830 | padding: 0; 831 | box-shadow: none; 832 | 833 | .content{ 834 | padding: 0 0 4.5em 0; 835 | .title { 836 | line-height: 1.3; 837 | font-size: 2.4em; 838 | padding: 1em 0; 839 | text-align: center; 840 | } 841 | } 842 | .post_body{ 843 | width: 96%; 844 | margin: 0 auto; 845 | padding: 1em 0; 846 | 847 | p, h1, h2, h3, h4, h5, h6, ul, ol, blockquote { 848 | margin-left: 15px; 849 | margin-right: 15px; 850 | } 851 | p.img_inside { 852 | margin-left: 0; 853 | margin-right: 0; 854 | } 855 | img { 856 | width: 100%; 857 | } 858 | } 859 | .info{ 860 | width: 90%; 861 | margin: 0 auto; 862 | } 863 | } 864 | 865 | .list { 866 | .post{ 867 | .info{ 868 | margin-top: 1.5em; 869 | } 870 | } 871 | } 872 | 873 | .list_with_title{ 874 | padding: 1.5em 1em; 875 | } 876 | 877 | #main{ 878 | padding: 0; 879 | margin: 0; 880 | #fb_comments_container{ 881 | 882 | #fb_comments, #fb_new_comment{ 883 | padding: 0 1em; 884 | } 885 | 886 | } 887 | } 888 | } -------------------------------------------------------------------------------- /template/categories.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | block content 3 | 4 | ul.categories#categories 5 | for category in get_data(type='folder', level=[1,2], min_posts_count=1) 6 | li 7 | a(href="/category/{{ category.path }}") 8 | h3= category.title 9 | if category.posts_count == 1 10 | span {{ category.posts_count }} post 11 | else 12 | span {{ category.posts_count }} posts 13 | 14 | script 15 | function auto_margin(){ 16 | var e = document.getElementById('categories'); 17 | e.style.marginLeft = 0; 18 | var width = e.offsetWidth; 19 | if (e && width > 300){ 20 | e.style.marginLeft = ( width% 302) /2 + 'px'; 21 | } 22 | } 23 | auto_margin(); 24 | window.onresize = auto_margin; -------------------------------------------------------------------------------- /template/feed.jade: -------------------------------------------------------------------------------- 1 | doctype xml 2 | +set_content_type('application/xml') 3 | feed(xmlns="http://www.w3.org/2005/Atom") 4 | title= site.title 5 | description= site.content.escaped 6 | link(href="http://{{ request.host }}/") 7 | link(ref="self", href="http://{{ request.host }}/feed") 8 | id= site._id 9 | if posts 10 | updated= posts[0]['date'].strftime('%Y-%m-%dT%H:%M:%SZ') 11 | for post in posts 12 | entry 13 | post_url = 'http://' + request.host + post.url.escaped 14 | title= post.title.escaped 15 | link(href=post_url, rel="alternate") 16 | updated= post.date.strftime('%Y-%m-%dT%H:%M:%SZ') 17 | id= post.url_path.escaped 18 | author 19 | name= site.configs.author or site.title 20 | summary(type="html")= post.content.escaped -------------------------------------------------------------------------------- /template/include/comments.jade: -------------------------------------------------------------------------------- 1 | #fb_comments_container 2 | if site.configs.disqus 3 | #disqus_thread 4 | script(type="text/javascript") 5 | var disqus_shortname = '{{ site.configs.disqus }}'; 6 | (function() { 7 | var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; 8 | dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; 9 | (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); 10 | })(); 11 | elif site.configs.duoshuo 12 | .ds-thread 13 | script(type="text/javascript") 14 | var duoshuoQuery = {short_name:"{{ site.configs.duoshuo }}"}; 15 | (function() { var ds = document.createElement('script'); ds.type = 'text/javascript';ds.async = true; 16 | ds.src = '//static.duoshuo.com/embed.js';ds.charset = 'UTF-8'; 17 | (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ds); 18 | })(); 19 | else 20 | if not site.configs.scripts_for_doc and not site.comment_script and is_comment_allowed(context_doc) 21 | link(rel="stylesheet", href="{{ resource_host }}/css/comment.css?version={{ resource_version }}") 22 | ul#fb_comments 23 | for comment in comments 24 | li.comment 25 | .avatar 26 | img(src="/service/static_3rd/avatar/{{ comment.email_md5 }}") 27 | .c_content.comment_body 28 | .author 29 | a(href="{{comment.site or '#'}}", rel="external nofollow") 30 | b= comment.author or 'Anonymous' 31 | small= comment.date 32 | .comment_content= comment.content 33 | form#fb_new_comment(method="post", action="#fb_new_comment") 34 | textarea(name="content")= raw_comment.content 35 | if raw_comment.error 36 | span.comment_error= raw_comment.error 37 | .input_body 38 | ul 39 | if not request.is_login 40 | li 41 | label Name: 42 | input(type="text", name="author", value=raw_comment.author) 43 | li 44 | label Email: 45 | input(type="text", name="email", value=raw_comment.email) 46 | li 47 | label Site: 48 | input(type="text", name="site", value=raw_comment.site) 49 | li 50 | input#c_submit(type="submit", value="Comment", class="c_button") 51 | elif site.comment_script 52 | |{{ site.comment_script }} 53 | else 54 | //scripts_for_doc -------------------------------------------------------------------------------- /template/include/paginator.jade: -------------------------------------------------------------------------------- 1 | if paginator.has_pre or paginator.has_next 2 | .clear 3 | .pager 4 | if paginator.has_pre 5 | a.round.pre(href=paginator.pre_url) Pre 6 | if paginator.has_next 7 | a.round.next(href=paginator.next_url) Next -------------------------------------------------------------------------------- /template/index+category+tag.jade: -------------------------------------------------------------------------------- 1 | 2 | mixin make_posts(posts) 3 | for post in posts 4 | .post 5 | .content 6 | h1.title 7 | a(href=post.url)= post.title 8 | .post_body= post.content.opening or post.content.limit(300) 9 | .info.clearfix 10 | span.date 11 | i.fa.fa-calendar 12 | span= post.date.format('%Y-%m-%d') 13 | span.visitors 14 | i.fa.fa-bookmark-o 15 | span= post.visits or 0 16 | if post.tags 17 | span.tags 18 | for tag in post.tags 19 | a(href="/tag/{{tag}}")= tag 20 | 21 | 22 | if request.args.pjax=='true' 23 | +make_posts(posts) 24 | else 25 | extends base 26 | block title 27 | title= request.args.s or tags.join('+') or category.title or site.title 28 | block content 29 | if request.path.strip('/') == 'category' 30 | +redirect('/categories') 31 | else 32 | .list.clearfix 33 | +make_posts(posts) 34 | include include/paginator 35 | 36 | #on_loading.hidden 37 | i.fa.fa-spinner.fa-spin -------------------------------------------------------------------------------- /template/interface.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Site", 4 | "title_zh_cn": "网站设置", 5 | "title_zh_tw": "網站設定", 6 | "groups": [ 7 | { 8 | "title": "Site Info", 9 | "title_zh_cn": "网站信息", 10 | "title_zh_tw": "網站訊息", 11 | "cells": [ 12 | { 13 | "parts_per_line": 3, 14 | "parts": [ 15 | { 16 | "key": "title", 17 | "title": "Site Title", 18 | "title_zh_cn": "网站标题", 19 | "title_zh_tw": "網站標題", 20 | "default_value": "My Site", 21 | "model": "text" 22 | }, 23 | { 24 | "title": "Site Keywords", 25 | "title_zh_cn": "网站关键词", 26 | "title_zh_tw": "網站關鍵詞", 27 | "key": "keywords", 28 | "model":"text" 29 | }, 30 | { 31 | "key": "avatar", 32 | "title": "Site Avatar (drag image here to replace)", 33 | "title_zh_cn": "网站头像 (拖曳圖片更換)", 34 | "title_zh_tw": "網站頭像 (拖曳圖片更換)", 35 | "value": "/_image/avatar.jpg", 36 | "model": "image", 37 | "width": 120, 38 | "height": 120 39 | } 40 | ] 41 | }, 42 | 43 | { 44 | "parts_per_line": 1, 45 | "parts":[ 46 | { 47 | "title": "Site Description", 48 | "title_zh_cn": "网站描述", 49 | "title_zh_tw": "網站描述", 50 | "key": "raw_content", 51 | "model":"textarea" 52 | }, 53 | { 54 | "title": "Site's Cover (drag image here to replace)", 55 | "title_zh_cn": "网站封面 (拖入图片替换)", 56 | "title_zh_tw": "網站封面 (拖入圖片替換)", 57 | "key": "cover", 58 | "model":"image", 59 | "value": "/_image/cover.jpg", 60 | "height": 120 61 | } 62 | ] 63 | } 64 | ] 65 | }, 66 | { 67 | "title": "Contents Display", 68 | "title_zh_cn": "内容显示", 69 | "title_zh_tw": "內容顯示", 70 | "cells":[ 71 | { 72 | "parts_per_line": 3, 73 | "parts":[ 74 | { 75 | "key": "posts_per_page", 76 | "title": "Posts per Page", 77 | "title_zh_cn": "每页显示日志数", 78 | "title_zh_tw": "每頁顯示文章數", 79 | "default_value": 3 80 | }, 81 | { 82 | "key": "post_content_type", 83 | "title": "Post Content Type", 84 | "title_zh_cn": "正文格式", 85 | "title_zh_tw": "正文格式", 86 | "model": "select", 87 | "default_value": "markdown", 88 | "options": [ 89 | { 90 | "value": "markdown", 91 | "title": "Markdown" 92 | }, 93 | { 94 | "value": "plain", 95 | "title": "Plain Text with Images" 96 | } 97 | ], 98 | "options_zh_cn": [ 99 | { 100 | "value": "markdown", 101 | "title": "Markdown" 102 | }, 103 | { 104 | "value": "plain", 105 | "title": "普通文本+图片" 106 | } 107 | ], 108 | "options_zh_tw": [ 109 | { 110 | "value": "markdown", 111 | "title": "Markdown" 112 | }, 113 | { 114 | "value": "plain", 115 | "title": "一般文本+圖片" 116 | } 117 | ] 118 | }, 119 | { 120 | "key": "post_paragraph_indent", 121 | "title": "Paragraph Text Indent", 122 | "title_zh_cn": "段落首行缩进", 123 | "title_zh_tw": "段落首行縮進", 124 | "model": "select", 125 | "default_value": "no", 126 | "options": [ 127 | { 128 | "value": "no", 129 | "title": "No indent" 130 | }, 131 | { 132 | "value": "hard", 133 | "title": "2 Letters Indent (Natural Mode)" 134 | }, 135 | { 136 | "value": "yes", 137 | "title": "2 Letters Indent (P tag in HTML)" 138 | } 139 | 140 | ], 141 | "options_zh_cn": [ 142 | { 143 | "value": "no", 144 | "title": "不缩进" 145 | }, 146 | { 147 | "value": "hard", 148 | "title": "缩进2字(自然模式)" 149 | }, 150 | { 151 | "value": "yes", 152 | "title": "缩进2字 (HTML的P标签缩进)" 153 | } 154 | ], 155 | "options_zh_tw": [ 156 | { 157 | "value": "no", 158 | "title": "不縮進" 159 | }, 160 | { 161 | "value": "hard", 162 | "title": "縮進2字(自然模式)" 163 | }, 164 | { 165 | "value": "yes", 166 | "title": "每段縮進2字 (HTML的P標簽縮進)" 167 | } 168 | ] 169 | }, 170 | { 171 | "key": "file_path", 172 | "title": "Albums Root Path", 173 | "title_zh_cn": "相册根目录", 174 | "title_zh_tw": "相簿位置", 175 | "model": "select", 176 | "options": "root" 177 | }, 178 | { 179 | "key": "show_toc", 180 | "title": "Table of Contents", 181 | "title_zh_cn": "文章自动目录", 182 | "title_zh_tw": "文章自動目錄", 183 | "model": "select", 184 | "default_value": "no", 185 | "options": [ 186 | { 187 | "value": "no", 188 | "title": "Not Display" 189 | }, 190 | { 191 | "value": "yes", 192 | "title": "Display" 193 | } 194 | ], 195 | "options_zh_cn": [ 196 | { 197 | "value": "no", 198 | "title": "不显示" 199 | }, 200 | { 201 | "value": "yes", 202 | "title": "显示" 203 | } 204 | ], 205 | "options_zh_tw": [ 206 | { 207 | "value": "no", 208 | "title": "不顯示" 209 | }, 210 | { 211 | "value": "yes", 212 | "title": "顯示" 213 | } 214 | ] 215 | }, 216 | { 217 | "key": "mathjax", 218 | "title": "Use MathJax", 219 | "title_zh_cn": "使用MathJax", 220 | "title_zh_tw": "使用MathJax", 221 | "model": "select", 222 | "default_value": "no", 223 | "options": [ 224 | { 225 | "value": "no", 226 | "title": "No" 227 | }, 228 | { 229 | "value": "yes", 230 | "title": "Yes" 231 | } 232 | ], 233 | "options_zh_cn": [ 234 | { 235 | "value": "no", 236 | "title": "不" 237 | }, 238 | { 239 | "value": "yes", 240 | "title": "使用" 241 | } 242 | ], 243 | "options_zh_tw": [ 244 | { 245 | "value": "no", 246 | "title": "不" 247 | }, 248 | { 249 | "value": "yes", 250 | "title": "使用" 251 | } 252 | ] 253 | } 254 | ] 255 | } 256 | ] 257 | 258 | }, 259 | { 260 | "title": "Social Comments", 261 | "title_zh_cn": "第三方评论", 262 | "title_zh_tw": "第三方評論", 263 | "cells":[ 264 | { 265 | "parts_per_line": 2, 266 | "parts": [ 267 | { 268 | "key": "disqus", 269 | "title": "Disqus", 270 | "title_zh_cn": "Disqus", 271 | "model": "text", 272 | "default_value": "Disqus Shortname" 273 | }, 274 | { 275 | "key": "duoshuo", 276 | "title": "DuoShuo", 277 | "title_zh_cn": "多说", 278 | "model": "text", 279 | "default_value": "DuoShuo ID" 280 | } 281 | ] 282 | } 283 | ] 284 | } 285 | ] 286 | }, 287 | { 288 | "title": "TemplateEngine", 289 | "title_zh_cn": "模板引擎", 290 | "title_zh_tw": "模板引擎", 291 | "groups":[ 292 | { 293 | "title":"Runtime", 294 | "title_zh_cn":"运行", 295 | "title_zh_tw":"運行", 296 | "cells":[ 297 | { 298 | "parts_per_line": 3, 299 | "parts":[ 300 | { 301 | "key": "template_priority", 302 | "title": "Template Priority", 303 | "title_zh_cn": "模板优先级", 304 | "title_zh_tw": "模板優先級", 305 | "model": "select", 306 | "default_value": "auto", 307 | "options": [ 308 | { 309 | "value": "auto", 310 | "title": "Auto" 311 | }, 312 | { 313 | "value": "self", 314 | "title": "Custom Template" 315 | }, 316 | { 317 | "value": "default", 318 | "title": "Default Template" 319 | } 320 | ], 321 | "options_zh_cn": [ 322 | { 323 | "value": "auto", 324 | "title": "自动" 325 | }, 326 | { 327 | "value": "self", 328 | "title": "自定义模板" 329 | }, 330 | { 331 | "value": "default", 332 | "title": "默认模板" 333 | } 334 | ], 335 | "options_zh_tw": [ 336 | { 337 | "value": "auto", 338 | "title": "自動" 339 | }, 340 | { 341 | "value": "self", 342 | "title": "自定義模板" 343 | }, 344 | { 345 | "value": "default", 346 | "title": "預設模板" 347 | } 348 | ] 349 | }, 350 | { 351 | "key": "template_inherit", 352 | "title": "Base Template Inherit", 353 | "title_zh_cn": "基础模板继承", 354 | "title_zh_tw": "基礎模板引用", 355 | "model": "select", 356 | "default_value": "yes", 357 | "options": [ 358 | { 359 | "value": "yes", 360 | "title": "Inherits Default Template" 361 | }, 362 | { 363 | "value": "no", 364 | "title": "No Inherit" 365 | } 366 | ], 367 | "options_zh_cn": [ 368 | { 369 | "value": "yes", 370 | "title": "引用預設模板" 371 | }, 372 | { 373 | "value": "no", 374 | "title": "不继承" 375 | } 376 | ], 377 | "options_zh_tw": [ 378 | { 379 | "value": "yes", 380 | "title": "從默認模板繼承" 381 | }, 382 | { 383 | "value": "no", 384 | "title": "不繼承" 385 | } 386 | ] 387 | }, 388 | { 389 | "key": "template_clone_allowed", 390 | "title": "Allow Template Clone", 391 | "title_zh_cn": "允许模板克隆", 392 | "title_zh_tw": "允許複製模板", 393 | "model": "select", 394 | "default_value": "yes", 395 | "options": [ 396 | { 397 | "value": "yes", 398 | "title": "Allow Others Clone Template!" 399 | }, 400 | { 401 | "value": "no", 402 | "title": "No Allowed" 403 | } 404 | ], 405 | "options_zh_cn": [ 406 | { 407 | "value": "yes", 408 | "title": "允许别人克隆模板" 409 | }, 410 | { 411 | "value": "no", 412 | "title": "不允许" 413 | } 414 | ], 415 | "options_zh_tw": [ 416 | { 417 | "value": "yes", 418 | "title": "允許別人複製模板" 419 | }, 420 | { 421 | "value": "no", 422 | "title": "不允許" 423 | } 424 | ] 425 | }, 426 | { 427 | "key": "autoreload", 428 | "title": "AutoReload", 429 | "title_zh_cn": "AutoReload", 430 | "model": "select", 431 | "default_value": "auto", 432 | "options": [ 433 | { 434 | "value": "auto", 435 | "title": "Auto" 436 | }, 437 | { 438 | "value": "admin", 439 | "title": "For Yourself Only" 440 | }, 441 | { 442 | "value": "yes", 443 | "title": "For Everyone" 444 | }, 445 | { 446 | "value": "no", 447 | "title": "Not Allowed" 448 | } 449 | ], 450 | "options_zh_cn": [ 451 | { 452 | "value": "auto", 453 | "title": "自动" 454 | }, 455 | { 456 | "value": "admin", 457 | "title": "仅自己" 458 | }, 459 | { 460 | "value": "yes", 461 | "title": "每个访客都有效" 462 | }, 463 | { 464 | "value": "no", 465 | "title": "不允许" 466 | } 467 | ], 468 | "options_zh_tw": [ 469 | { 470 | "value": "auto", 471 | "title": "自動" 472 | }, 473 | { 474 | "value": "admin", 475 | "title": "僅自己" 476 | }, 477 | { 478 | "value": "yes", 479 | "title": "每個訪客都有效" 480 | }, 481 | { 482 | "value": "no", 483 | "title": "不允許" 484 | } 485 | ] 486 | }, 487 | { 488 | "key": "post_url_format", 489 | "title": "Post URL Format", 490 | "title_zh_cn": "日志URL格式", 491 | "title_zh_tw": "日誌URL格式", 492 | "model": "select", 493 | "default_value": "normal", 494 | "options": [ 495 | { 496 | "value": "normal", 497 | "title": "/post/" 498 | }, 499 | { 500 | "value": "no_prefix", 501 | "title": "/" 502 | } 503 | ] 504 | } 505 | 506 | ] 507 | } 508 | ] 509 | }, 510 | { 511 | "title": "Insert Scripts", 512 | "title_zh_cn":"插入脚本", 513 | "title_zh_tw":"插入腳本", 514 | "cells":[ 515 | { 516 | "parts_per_line": 1, 517 | "parts":[ 518 | { 519 | "key": "scripts_per_page", 520 | "title": "Insert following code into every HTML page.", 521 | "title_zh_cn": "将下面的代码插入到每个HTML页面中", 522 | "title_zh_tw": "將下面的程式碼插入到每個HTML頁面中", 523 | "model":"textarea" 524 | }, 525 | { 526 | "key": "scripts_for_doc", 527 | "title": "Insert following code into Doc(post/image) page.", 528 | "title_zh_cn": "将下面的代码插入到文档(日志或图片)详细页中", 529 | "title_zh_tw": "將下面程式碼插入文件(文章或圖片)頁面內", 530 | "model":"textarea" 531 | } 532 | ] 533 | } 534 | ] 535 | } 536 | 537 | ] 538 | }, 539 | { 540 | "title": "Advanced", 541 | "title_zh_cn": "高级", 542 | "title_zh_tw": "高級", 543 | "groups":[ 544 | { 545 | "title": "Feature", 546 | "title_zh_cn": "功能", 547 | "title_zh_tw": "功能", 548 | "cells":[ 549 | { 550 | "parts_per_line": 1, 551 | "parts":[ 552 | { 553 | "key": "auto_image", 554 | "title": "beta Make Images Vivider Automatically. demo ", 555 | "title_zh_cn": "beta 自动美化照片. demo ", 556 | "model":"check", 557 | "default_value": "no" 558 | } 559 | ] 560 | } 561 | ] 562 | }, 563 | { 564 | "title": "Data", 565 | "title_zh_cn": "数据", 566 | "title_zh_tw": "數據", 567 | "cells":[ 568 | { 569 | "parts_per_line": 1, 570 | "parts":[ 571 | { 572 | "key": "comment", 573 | "title": "Comments Allowed (if not, visitors can't comment on your posts or images).", 574 | "title_zh_cn": "允许评论(如不勾选,则访客无法评论你的文章、图片)", 575 | "title_zh_tw": "允許評論(如不勾選,則訪客無法評論你的文章、圖片)", 576 | "model":"check", 577 | "default_value": "yes" 578 | }, 579 | { 580 | "key": "sync_by_3rd", 581 | "title": "Other people put data into your site will be allowed (in the `share` folder).", 582 | "title_zh_cn": "允许其他人推送内容到你的网站(`share`目录下)", 583 | "title_zh_tw": "允許其他人推送內容倒你的網站(`share`目錄下)", 584 | "model":"check", 585 | "default_value": "no" 586 | }, 587 | { 588 | "key": "sync_by_3rd_public", 589 | "title": "No need to login when other people put data into your site.", 590 | "title_zh_cn": "其他人推送内容时,无需登录", 591 | "title_zh_tw": "其他人推送內容時,無需登錄", 592 | "model":"check", 593 | "default_value": "no" 594 | } 595 | ] 596 | } 597 | ] 598 | }, 599 | { 600 | "title":"Data Mining", 601 | "title_zh_cn":"数据挖掘", 602 | "title_zh_tw":"资料探勘", 603 | "cells":[ 604 | { 605 | "parts_per_line": 3, 606 | "parts":[ 607 | { 608 | "key": "score_degree", 609 | "title": "Hottest Sort Degree(days)", 610 | "title_zh_cn": "热门排序维度(天)", 611 | "title_zh_tw": "熱門排序維度(天)", 612 | "default_value": 0.5 613 | } 614 | ] 615 | } 616 | ] 617 | }, 618 | 619 | { 620 | "title": "URLs Forbidden", 621 | "title_zh_cn":"URLs禁止访问", 622 | "title_zh_tw":"URLs禁止訪問", 623 | "cells":[ 624 | { 625 | "parts_per_line": 1, 626 | "parts":[ 627 | { 628 | "key": "hidden_urls", 629 | "title": "URL Paths to be forbidden, one record one line, `the_path` or `the_path/*`", 630 | "title_zh_cn": "如果访问路径为以下所示,将禁止访问,每行一条记录,`the_path`或`the_path/*`", 631 | "title_zh_tw": "如果訪問路徑為以下所示,將禁止訪問,每行一條記錄,`the_path`或`the_path/*`", 632 | "model":"textarea", 633 | "is_list": true 634 | } 635 | ] 636 | } 637 | ] 638 | }, 639 | 640 | { 641 | "title": "Passwords for Visitors", 642 | "title_zh_cn":"访问密码", 643 | "title_zh_tw":"訪問密碼", 644 | "cells":[ 645 | { 646 | "parts_per_line": 1, 647 | "parts":[ 648 | { 649 | "key": "users", 650 | "title": "Visitors must input the username and password to access, format is 'username@password', one record one line", 651 | "title_zh_cn": "访客须输入用户名、密码才能访问,格式为'username@password', 每行一条记录 ", 652 | "title_zh_tw": "訪客須輸入用戶名、密碼才能訪問,格式為'username@password', 每行一條記錄 ", 653 | "model":"textarea", 654 | "is_list": true 655 | } 656 | ] 657 | } 658 | ] 659 | } 660 | 661 | ] 662 | 663 | }, 664 | { 665 | "title": "Bind", 666 | "title_zh_cn": "域名", 667 | "title_zh_tw": "域名", 668 | "groups":[ 669 | { 670 | "title": "Bind Domain", 671 | "title_zh_cn": "域名绑定", 672 | "title_zh_tw": "域名綁定", 673 | "cells": [ 674 | { 675 | "parts_per_line": 2, 676 | "parts": [ 677 | { 678 | "key": "domain", 679 | "placeholder": "xxx.farbox.com", 680 | "title": "Domain need help?", 681 | "title_zh_cn": "域名 need help?", 682 | "title_zh_tw": "域名 need help?" 683 | } 684 | ] 685 | } 686 | ] 687 | }, 688 | { 689 | "title": "Multi Domains Binding", 690 | "title_zh_cn":"多域名绑定", 691 | "title_zh_tw":"多域名綁定", 692 | "cells":[ 693 | { 694 | "parts_per_line": 1, 695 | "parts":[ 696 | { 697 | "key": "domains", 698 | "title": "one line one domain which needed to bind, no more than 10 lines.", 699 | "title_zh_cn": "每行一条需绑定的域名,最多不超过10条", 700 | "title_zh_tw": "每行一條需綁定的域名,最多不超過10條", 701 | "model":"textarea", 702 | "is_list": true 703 | } 704 | ] 705 | } 706 | ] 707 | }, 708 | { 709 | "title": "Customize DNS Records", 710 | "title_zh_cn":"自定义DNS记录", 711 | "title_zh_tw":"自定義DNS記錄", 712 | "cells":[ 713 | { 714 | "parts_per_line": 1, 715 | "parts":[ 716 | { 717 | "key": "dns", 718 | "placeholder": "xxxxxxxxxxxx@TXT\nxxxxxxxxxxxx@MX#5\nxxxxxxxxxxxx@MX#10", 719 | "title": "one line one record, no more than 20 lines, supports TXT/MX only", 720 | "title_zh_cn": "每行一条记录,最多不超过20条,仅支持TXT/MX", 721 | "title_zh_tw": "每行壹條記錄,最多不超過20條,僅支持TXT/MX", 722 | "model":"textarea", 723 | "is_list": true 724 | } 725 | ] 726 | } 727 | ] 728 | } 729 | 730 | ] 731 | } 732 | ] -------------------------------------------------------------------------------- /template/markdown+pages.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | if request.path.startswith('/pages') 3 | post = get_doc(path2+'.md') or get_doc(path2+'.txt') or get_doc(path2+'.mk') or get_doc(path2+'.markdown') 4 | block title 5 | title= post.title 6 | block content 7 | .detail.markdown_container 8 | .post 9 | .content 10 | .post_body.markdown_body= post.content -------------------------------------------------------------------------------- /template/post.jade: -------------------------------------------------------------------------------- 1 | extends base 2 | block title 3 | title= post.title + " - " + site.title 4 | block content 5 | 6 | .detail 7 | .post 8 | .content 9 | h1.title= post.title 10 | .post_body= post.content 11 | .info.clearfix 12 | span.date 13 | i.fa.fa-calendar 14 | span= post.date.format('%Y-%m-%d %H:%M') 15 | span.visitors 16 | i.fa.fa-bookmark-o 17 | span= post.visits or 0 18 | a.show_raw(href="{{post.url}}?action=show_raw", target=="_blank") 19 | i.fa.fa-code 20 | a.go_editor(href="{{post.url}}?action=edit", target=="_blank") 21 | i.fa.fa-edit 22 | if post.tags 23 | span.tags 24 | i.fa.fa-tags 25 | for tag in post.tags 26 | a(href="/tag/{{tag}}")= tag 27 | .other_posts 28 | if pre_one 29 | a.pre(href='{{pre_one.url}}#main') 30 | span < {{ pre_one.title }} 31 | if next_one 32 | a.next(href='{{next_one.url}}#main') 33 | span {{ next_one.title }} > 34 | 35 | include include/comments.jade -------------------------------------------------------------------------------- /template/template.jade: -------------------------------------------------------------------------------- 1 | html 2 | head 3 | title FarBox Tempalte Engine 4 | load('jquery') 5 | body 6 | style(type="text/css") 7 | body { 8 | font-size: 14px; 9 | font-family: "Hiragino Sans GB", "Microsoft YaHei", sans-serif; 10 | color: #555555; 11 | width: 1000px; 12 | margin: 0 auto; 13 | -webkit-text-size-adjust:none; 14 | } 15 | 16 | a{ 17 | text-decoration: none; 18 | } 19 | 20 | #clone_template{ 21 | margin: 120px auto; 22 | text-align: center; 23 | } 24 | 25 | #clone_template input{ 26 | height:35px; 27 | width:250px; 28 | margin-right: 30px; 29 | font-size:18px; 30 | color:#1c87ff; 31 | padding: 2px 5px; 32 | } 33 | 34 | #clone_template .checkbox{ 35 | display: inline; 36 | margin-top: -30px; 37 | margin-left: -20px; 38 | } 39 | 40 | #clone_template .checkbox input{ 41 | width: 30px; 42 | margin: 0; 43 | height: 12px; 44 | padding: 0; 45 | } 46 | 47 | #clone_template .checkbox label{ 48 | padding: 0; 49 | margin-left: -13px; 50 | font-size: 10px; 51 | color: #888; 52 | } 53 | 54 | #clone_template a:hover { 55 | background: #1c87ff; 56 | color: #fff; 57 | text-decoration: none; 58 | } 59 | 60 | #clone_template .button { 61 | display: inline-block; 62 | font-family: "Hiragino Sans GB", "Microsoft YaHei", sans-serif; 63 | font-size: 1.5em; 64 | letter-spacing: 1px; 65 | padding: 5px 30px; 66 | background: #E60900; 67 | color: #fff; 68 | margin: 55px 0 0 45px; 69 | border-radius: 5px; 70 | border: 1px solid #e6e6e6; 71 | } 72 | 73 | 74 | 75 | #clone_template 76 | label(for='site') Your Site Domain: 77 | input#site(type="text") 78 | 79 | .checkbox 80 | input#auto_update(type="checkbox") 81 | label Auto Update 82 | 83 | a#redirect.button(href="#", onclick="clone_template();return false;") Clone Template 84 | 85 | script(type="text/javascript") 86 | $(document).ready(function(){ 87 | $('#auto_update').mousedown(function() { 88 | if (!$(this).is(':checked')) { 89 | this.checked = confirm("If you select `Auto Upgrade`, make sure you trust the owner of the template!"); 90 | $(this).trigger("change"); 91 | } 92 | }); 93 | $('#site').focus(); 94 | 95 | }); 96 | 97 | function clone_template(){ 98 | var domain = $.trim($('#site').val()).toLowerCase(); 99 | if (domain.indexOf('http://')==-1){ 100 | domain = 'http://' + domain; 101 | } 102 | var url = domain.replace(/\/$/g, '') + '/?template_site_id={{ site._id }}'; 103 | url += '&auto_update='+ $('#auto_update').is(':checked'); 104 | window.location.href = url; 105 | } 106 | 107 | --------------------------------------------------------------------------------