Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Moved helpers into their own directory, and finished implementing the…

… contentTag helper

and the imageLink helper, created new string utils method called dasherize that will create a dashed string
from camelCase and snake_case string. Also create a new util called Object and made a merge method for it
the merge method merges to objects together, and supports deep merging.
  • Loading branch information...
commit e8fa5864f5cb150614ef7ffdb605110010465279 1 parent ef3a6eb
@larzconwell larzconwell authored
View
106 lib/template/helpers.js
@@ -1,106 +0,0 @@
-/*
- * Geddy JavaScript Web development framework
- * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
-*/
-var utils = require('../utils')
-
-exports.linkTo = {
- name: 'linkTo',
- action: function() {}
-};
-
-exports.scriptLink = {
- name: 'scriptLink',
- action: function() {}
-};
-
-exports.styleLink = {
- name: 'styleLink',
- action: function() {}
-};
-
-/*
- * truncate(string<String>, options<Object>, callback[Function])
- *
- * Truncates a given `string` after a specified `length` if `string` is longer than
- * `length`. The last characters will be replaced with an `omission` for a total length not
- * exceeding `length`. If `callback` is given it will fire if `string` is truncated.
- *
- * Options:
- * length <Integer> Length the output string will be(default: 30)
- * len <Integer> Alias for length
- * omission <String> Replace last letters with an omission(default: '...')
- * ellipsis <String> Alias for omission
- * seperator <String>/<RegExp> Break the truncated text at the nearest `seperator`
- *
- * Warnings:
- * Please be aware that truncating HTML tags or entities may result in malformed HTML returned
- *
- * Examples:
- * truncate('Once upon a time in a world', { length: 10 })
- * => 'Once up...''
- *
- * truncate('Once upon a time in a world', { length: 20, omission: '...(continued)' })
- * => 'Once u...(continued)'
- *
- * truncate('Once upon a time in a world', { length: 15, seperator: /\s/ })
- * => 'Once upon a...'
- * Normal Output: => 'Once upon a ...'
- *
- * truncate('Once upon a time in a world', { length: 15, seperator: ' ' })
- * => 'Once upon a...'
- * Normal Output: => 'Once upon a ...'
- *
- * truncate('<p>Once upon a time</p>', { length: 20 })
- * => '<p>Once upon a ti...'
- */
-exports.truncate = {
- name: 'truncate',
- action: utils.string.truncate
-};
-
-/*
- * truncateHTML(string<String>, options<Object>, callback[Function])
- *
- * Truncates a given `string` inside HTML tags after a specified `length` if `string` is longer than
- * `length`. The last characters will be replaced with an `omission` for a total length not
- * exceeding `length`. If `callback` is given it will fire if `string` is truncated. If `once` is
- * true only the first string in the first HTML tags will be truncated leaving the others as they
- * were
- *
- * Options:
- * once <Boolean> If true it will only truncate the first text found in the first
- * set of HTML tags(default: false)
- *
- * Notes:
- * * All options available in the `truncate` helper are also available in `truncateHTML`
- * * HTML tags will not be truncated, so return value will always be safe for rendering
- *
- * Examples:
- * truncateHTML('<p>Once upon a time in a world</p>', { length: 10 })
- * => '<p>Once up...</p>'
- *
- * truncateHTML('<p>Once upon a time <small>in a world</small></p>', { length: 10 })
- * => '<p>Once up...<small>in a wo...</small></p>'
- *
- * truncateHTML('<p>Once upon a time <small>in a world</small></p>', { length: 10, once: true })
- * => '<p>Once up...<small>in a world</small></p>'
- */
-exports.truncateHTML = {
- name: 'truncateHTML',
- //altName: 'truncate_HTML',
- action: utils.string.truncateHTML
-};
View
210 lib/template/helpers/index.js
@@ -0,0 +1,210 @@
+/*
+ * Geddy JavaScript Web development framework
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://http://apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+var utils = require('../../utils')
+ , helperUtils = require('./utils')
+ , Data;
+
+// Copy a data object so it can be used in helpers
+exports.registerData = {
+ name: 'registerData',
+ action: function(data) { Data = data; }
+}
+
+exports.urlFor = {
+ name: 'urlFor',
+ action: function(options) {}
+}
+
+/*
+ * contentTag(tag<String>, content<String>, htmlOptions<Object>)
+ *
+ * Returns an HTML element from a given `tag` and includes the `content` and
+ * all HTML attributes from `htmlOptions`
+ *
+ * Examples:
+ * contentTag('p', 'this is some content')
+ * => '<p>this is some content</p>'
+ *
+ * contentTag('input', 'sample value')
+ * => '<input value="sample value" />'
+ *
+ * contentTag('input', 'sample value', { value: 'override sample value' })
+ * => '<input value="override sample value" />'
+ *
+ * contentTag('input', 'sample value', { type: 'text', autofocus: true })
+ * => '<input autofocus="autofocus" type="text" value="sample value" />'
+ *
+ * contentTag('a', 'http://google.com')
+ * => '<a href="http://google.com">http://google.com</a>'
+ *
+ * contentTag('a', 'hey there', { href: 'http://google.com' })
+ * => '<a href="http://google.com">hey there</a>'
+ *
+ * contentTag('a', 'hey there', { href: 'http://google.com', data: { goTo: 'http://google.com' } })
+ * => '<a data-go-to="http://google.com" href="http://google.com">hey there</a>'
+ *
+ * contentTag('a', 'hey there', { href: 'http://google.com', data_go_to: 'http://google.com' })
+ * => '<a data-go-to="http://google.com" href="http://google.com">hey there</a>'
+*/
+exports.contentTag = {
+ name: 'contentTag',
+ action: function(tag, content, htmlOptions) {
+ return helperUtils.contentTagString(tag, content, htmlOptions);
+ }
+}
+
+exports.linkTo = {
+ name: 'linkTo',
+ action: function() {
+ }
+};
+
+exports.scriptLink = {
+ name: 'scriptLink',
+ action: function() {}
+};
+
+exports.styleLink = {
+ name: 'styleLink',
+ action: function() {}
+};
+
+/*
+ * imageLink(source<String>, link<String>, imageOptions<Object>, linkOptions<Object>)
+ *
+ * Returns a anchor tag to a `link` and include the img tag at `source`, including the given
+ * `htmlOptions`
+ *
+ * Custom HTML Options:
+ * size <String> This takes a string including the width and height "{width}x{height}"(e,g: '40x50')
+ * or it can take a single string including a integer "{size}"(e,g: '40')
+ * The first once parse will output "height='50' width='40'"
+ * The second parses to "height='40' width='40'"
+ * Note: If the format doesn't comply with either of those, it will be ignored
+ *
+ * Examples:
+ * imageLink('images/google.png', 'http://google.com')
+ * => '<a href="http://google.com"><img alt="images/google.png" src="images/google.png" /></a>'
+ *
+ * imageLink('images/google.png', 'http://google.com', { alt: '' })
+ * => '<a href="http://google.com"><img alt="" src="images/google.png" /></a>'
+ *
+ * imageLink('images/google.png', 'http://google.com', { alt: '', size: '40x50' })
+ * => '<a href="http://google.com"><img alt="" height="50" src="images/google.png" width="40" /></a>'
+*/
+exports.imageLink = {
+ name: 'imageLink',
+ action: function(source, link, imageOptions, linkOptions) {
+ // If size option is included
+ if('size' in imageOptions) {
+ var size = imageOptions.size
+ , pat = /([0-9]+x[0-9]*|[0-9]+)/;
+
+ if(size.match(pat)) {
+ delete imageOptions.size;
+
+ if(size.match(/[0-9]+x[0-9]*/)) {
+ // It's seperate width and height
+ imageOptions.width = imageOptions.width || size.replace(/x[0-9]*/, '');
+ imageOptions.height = imageOptions.height || size.replace(/[0-9]+x/, '');
+ } else {
+ // Same size width and height
+ imageOptions.width = imageOptions.width || size;
+ imageOptions.height = imageOptions.height || size;
+ }
+ } else delete imageOptions.size;
+ }
+ linkOptions = geddy.utils.object.merge(linkOptions, { href: link });
+
+ var imageTag = helperUtils.contentTagString('img', source, imageOptions);
+ return helperUtils.contentTagString('a', imageTag, linkOptions);
+ }
+}
+
+/*
+ * truncate(string<String>, options<Object>, callback[Function])
+ *
+ * Truncates a given `string` after a specified `length` if `string` is longer than
+ * `length`. The last characters will be replaced with an `omission` for a total length not
+ * exceeding `length`. If `callback` is given it will fire if `string` is truncated.
+ *
+ * Options:
+ * length <Integer> Length the output string will be(default: 30)
+ * len <Integer> Alias for length
+ * omission <String> Replace last letters with an omission(default: '...')
+ * ellipsis <String> Alias for omission
+ * seperator <String>/<RegExp> Break the truncated text at the nearest `seperator`
+ *
+ * Warnings:
+ * Please be aware that truncating HTML tags or entities may result in malformed HTML returned
+ *
+ * Examples:
+ * truncate('Once upon a time in a world', { length: 10 })
+ * => 'Once up...''
+ *
+ * truncate('Once upon a time in a world', { length: 20, omission: '...(continued)' })
+ * => 'Once u...(continued)'
+ *
+ * truncate('Once upon a time in a world', { length: 15, seperator: /\s/ })
+ * => 'Once upon a...'
+ * Normal Output: => 'Once upon a ...'
+ *
+ * truncate('Once upon a time in a world', { length: 15, seperator: ' ' })
+ * => 'Once upon a...'
+ * Normal Output: => 'Once upon a ...'
+ *
+ * truncate('<p>Once upon a time</p>', { length: 20 })
+ * => '<p>Once upon a ti...'
+ */
+exports.truncate = {
+ name: 'truncate',
+ action: utils.string.truncate
+};
+
+/*
+ * truncateHTML(string<String>, options<Object>, callback[Function])
+ *
+ * Truncates a given `string` inside HTML tags after a specified `length` if `string` is longer than
+ * `length`. The last characters will be replaced with an `omission` for a total length not
+ * exceeding `length`. If `callback` is given it will fire if `string` is truncated. If `once` is
+ * true only the first string in the first HTML tags will be truncated leaving the others as they
+ * were
+ *
+ * Options:
+ * once <Boolean> If true it will only truncate the first text found in the first
+ * set of HTML tags(default: false)
+ *
+ * Notes:
+ * * All options available in the `truncate` helper are also available in `truncateHTML`
+ * * HTML tags will not be truncated, so return value will always be safe for rendering
+ *
+ * Examples:
+ * truncateHTML('<p>Once upon a time in a world</p>', { length: 10 })
+ * => '<p>Once up...</p>'
+ *
+ * truncateHTML('<p>Once upon a time <small>in a world</small></p>', { length: 10 })
+ * => '<p>Once up...<small>in a wo...</small></p>'
+ *
+ * truncateHTML('<p>Once upon a time <small>in a world</small></p>', { length: 10, once: true })
+ * => '<p>Once up...<small>in a world</small></p>'
+ */
+exports.truncateHTML = {
+ name: 'truncateHTML',
+ //altName: 'truncate_HTML',
+ action: utils.string.truncateHTML
+};
View
136 lib/template/helpers/utils.js
@@ -0,0 +1,136 @@
+
+// List of HTML attributes that take boolean values
+exports.boolAttributes = [
+ 'disabled', 'readonly', 'multiple', 'checked',
+ 'autobuffer', 'autoplay', 'controls',
+ 'loop', 'selected', 'hidden', 'scoped',
+ 'async', 'defer', 'reversed', 'ismap', 'seemless',
+ 'muted', 'required', 'autofocus', 'novalidate',
+ 'open', 'pubdate', 'itemscope', 'formnovalidate'
+];
+
+// List of HTML tags that are self closing,
+// Each item has a value that is the attribute that
+// holds the content that's probably the one that should
+// be default for content
+exports.selfClosingTags = {
+ 'link': {name: 'link', content: 'src'},
+ 'meta': {name: 'meta', content: 'content'},
+ 'img': {name: 'img', content: 'src'},
+ // Could be `placeholder` but not all browser support it
+ 'input': {name: 'input', content: 'value'},
+ 'area': {name: 'area', content: 'href'},
+ 'base': {name: 'link', content: 'href'},
+ 'col': {name: 'col', content: 'title'},
+ 'frame': {name: 'frame', content: 'src'},
+ 'param': {name: 'param', content: 'value'}
+};
+
+// List of tags that may need some pre content data
+exports.preContentStrings = {
+ textarea: '\n'
+}
+
+/*
+ * contentTagString(tag<String>, content<String>, htmlOptions<Object>)
+ *
+ * Returns a HTML element created from the tag, content and
+ * html attributes given
+*/
+exports.contentTagString = function(tag, content, htmlOptions) {
+ if(!tag) return;
+ if(!content) content = '';
+ htmlOptions = htmlOptions || {};
+
+ var selfClosing = tag in exports.selfClosingTags ? exports.selfClosingTags[tag] : undefined
+ , tagOptions;
+
+ if(selfClosing) htmlOptions[selfClosing.content] = htmlOptions[selfClosing.content] || content || false;
+ if(tag === 'a') htmlOptions.href = htmlOptions.href || content;
+ if(tag === 'img') htmlOptions.alt = htmlOptions.alt === '' ? htmlOptions.alt : htmlOptions.alt || content;
+ tagOptions = exports.tagOptions(htmlOptions);
+
+ content = exports.preContentStrings[tag] ?
+ exports.preContentStrings[tag] + content :
+ content;
+
+ if(selfClosing) {
+ return '<'+tag+tagOptions+' />';
+ } else {
+ return '<'+tag+tagOptions+'>' + content + '</'+tag+'>';
+ }
+};
+
+/*
+ * tagOptions(options<Object>)
+ *
+ * Returns a string of parsed HTML options sorted alphabetically
+*/
+exports.tagOptions = function(options) {
+ if(!options) return;
+
+ var attrs = []
+ , key
+ , value
+ , i, k;
+
+ // Loop through each option
+ for(i in options) {
+ key = geddy.utils.string.dasherize(i);
+ value = options[i];
+
+ // If it's a data key that has an object
+ // loop through each of the values in the object
+ // and create data attributes out of them
+ if(key === 'data' && typeof value === 'object') {
+ for(k in value) {
+ attrs.push(exports.dataAttribute(k, value[k]));
+ }
+ }
+ // If the attribute is a boolean attribute
+ // parse it as a bool attribute
+ else if(geddy.utils.array.included(key, exports.boolAttributes)) {
+ if(value) attrs.push(exports.boolAttribute(key));
+ }
+ // Just a normal attribute, parse it normally
+ else if(value || value === '') {
+ attrs.push(exports.tagAttribute(key, value))
+ }
+ }
+
+ if(attrs.length > 0) {
+ return ' ' + attrs.sort().join(' ');
+ } else return '';
+};
+
+/*
+ * dataAttribute(attribute<String>, value<String/Boolean>)
+ *
+ * Returns a parsed HTML data attribute
+*/
+exports.dataAttribute = function(attribute, value) {
+ attribute = 'data-' + geddy.utils.string.dasherize(attribute);
+
+ return exports.tagAttribute(attribute, value);
+};
+
+/*
+ * boolAttribute(attribute<String>)
+ *
+ * Returns a parsed HTML boolean attribute
+*/
+exports.boolAttribute = function(attribute) {
+ return attribute + '="' + attribute + '"';
+};
+
+/*
+ * tagAttribute(attribute<String>)
+ *
+ * Returns a parsed HTML attribute
+*/
+exports.tagAttribute = function(attribute, value) {
+ // Only allow Strings to be an object
+ if(typeof value === 'object') value = value.join(' ');
+
+ return attribute + '="' + value + '"';
+};
View
11 lib/template/index.js
@@ -50,15 +50,18 @@ Templater.prototype.render = function(data, config) {
var templateFileName;
// Register all the helper functions to Templato
+ Helpers.registerData.action(data);
var i, helper;
for(i in Helpers) {
helper = Helpers[i];
- // Create alternative helper name with opposite case style
- helper.altName = helper.altName || geddy.utils.string.snakeize(helper.name);
+ if(helper.name !== 'registerData') {
+ // Create alternative helper name with opposite case style
+ helper.altName = helper.altName || geddy.utils.string.snakeize(helper.name);
- Templato.registerHelper(helper.altName, helper.action);
- Templato.registerHelper(helper.name, helper.action);
+ Templato.registerHelper(helper.altName, helper.action);
+ Templato.registerHelper(helper.name, helper.action);
+ }
}
// Rendering a template in a layout
View
12 lib/utils/array.js
@@ -25,6 +25,18 @@ var array = new (function () {
return array + ' and ' + last;
};
+ this.included = function(item, array) {
+ if(!item) return undefined;
+ // Check if an `item` is included in an `array`
+ // If the `item` is found, it'll return and object with the key and value,
+ // - otherwise return undefined
+ var result = array.indexOf(item);
+
+ if(result === -1) {
+ return undefined;
+ } else return { key: result, value: array[result] };
+ };
+
})();
exports.array = array;
View
2  lib/utils/index.js
@@ -20,6 +20,7 @@ var geddy
, async = require('./async').async
, uri = require('./uri').uri
, array = require('./array').array
+ , object = require('./object').object
, date = require('./date').date
, request = require('./request').request
, EventBuffer = require('./event_buffer').EventBuffer
@@ -115,6 +116,7 @@ utils.string = string;
utils.async = async;
utils.uri = uri;
utils.array = array;
+utils.object = object;
utils.date = date;
utils.request = request;
utils.SortedCollection = SortedCollection;
View
45 lib/utils/object.js
@@ -0,0 +1,45 @@
+/*
+ * Geddy JavaScript Web development framework
+ * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+var object = new (function () {
+
+ this.merge = function(object, otherObject) {
+ // Merges two objects together, then returns the final object
+ // - if no matching value is found it will create a new one otherwise it will overwrite the old
+ // - one, also supports deep merging automatically
+ object = object || {};
+ otherObject = otherObject || {};
+ var i, key, value;
+
+ for(i in otherObject) {
+ key = i, value = otherObject[key];
+
+ try {
+ // Update value of object to the one from otherObject
+ object[key] = typeof value === 'object' ? merge(object[key], value) : value;
+ } catch(err) {
+ // Object isn't set so set it
+ object[key] = value;
+ }
+ }
+ return object;
+ };
+
+})();
+
+exports.object = object;
View
10 lib/utils/string.js
@@ -279,6 +279,16 @@ var string = new (function () {
return s.substr(0, 1).toUpperCase() + s.substr(1);
};
+ this.dasherize = function(s) {
+ // Todo: Make this simpler, but it needs to change work for the following types:
+ // `example_text` => `example-text`
+ // `example_Text` => `example-text`
+ // `exampleText` => `example-text`
+ return s.replace(/.(_([a-z]|[A-Z])|[A-Z]){1}/g, function(s) {
+ return s.replace(/(_([a-z]|[A-Z])|[A-Z])/, s.replace(/[a-z]_?/, '-').toLowerCase());
+ });
+ };
+
// From Math.uuid.js, http://www.broofa.com/Tools/Math.uuid.js
// Robert Kieffer (robert@broofa.com), MIT license
this.uuid = function (len, rad) {
Please sign in to comment.
Something went wrong with that request. Please try again.