jekyll-webmention_io 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,134 @@
1
+ var Liquid={author:'Matt McCray <darthapo@gmail.com>',version:'1.3.2',readTemplateFile:function(path){throw("This liquid context does not allow includes.");},registerFilters:function(filters){Liquid.Template.registerFilter(filters);},parse:function(src){return Liquid.Template.parse(src);}};Liquid.extensions={};Liquid.extensions.object={};Liquid.extensions.object.update=function(newObj){for(var p in newObj){this[p]=newObj[p];}
2
+ return this;};Liquid.extensions.object.hasKey=function(arg){return!!this[arg];};Liquid.extensions.object.hasValue=function(arg){for(var p in this){if(this[p]==arg)return true;}
3
+ return false;};Liquid.extensions.object.isEmpty=function(obj){if(!obj||Liquid.extensions.stringTools.strip(obj.toString())==="")return true;if(obj.length&&obj.length>0)return false;if(typeof obj==='number')return false;for(var prop in obj)if(obj[prop])return false;return true;};Liquid.extensions.stringTools={};Liquid.extensions.stringTools.capitalize=function(str){return str.charAt(0).toUpperCase()+str.substring(1).toLowerCase();};Liquid.extensions.stringTools.strip=function(str){return str.replace(/^\s+/,'').replace(/\s+$/,'');};Liquid.extensions.arrayTools={};Liquid.extensions.arrayTools.last=function(array){return array[array.length-1];};Liquid.extensions.arrayTools.indexOf=function(array,obj){for(var i=0;i<array.length;i++){if(array[i]==obj)return i;}
4
+ return-1;};Liquid.extensions.arrayTools.map=function(obj,fun){var len=obj.length;if(typeof fun!="function")
5
+ throw'Liquid.extensions.arrayTools.map requires first argument to be a function';var res=new Array(len);var thisp=arguments[2];for(var i=0;i<len;i++){if(i in obj)
6
+ res[i]=fun.call(thisp,obj[i],i,obj);}
7
+ return res;};Liquid.extensions.arrayTools.flatten=function(array){var len=array.length;var arr=[];for(var i=0;i<len;i++){if(array[i]instanceof Array){arr=arr.concat(array[i]);}else{arr.push(array[i]);}}
8
+ return arr;};Liquid.extensions.arrayTools.each=function(obj,fun){var len=obj.length;if(typeof fun!="function"){throw'Liquid.extensions.arrayTools.each requires first argument to be a function';}
9
+ var thisp=arguments[2];for(var i=0;i<len;i++){if(i in obj){fun.call(thisp,obj[i],i,obj);}}
10
+ return null;};Liquid.extensions.arrayTools.include=function(array,arg){var len=array.length;return Liquid.extensions.arrayTools.indexOf(array,arg)>=0;for(var i=0;i<len;i++){if(arg==array[i])return true;}
11
+ return false;};(function(){var initializing=false,fnTest=/xyz/.test(function(){xyz;})?/\b_super\b/:/.*/;this.Class=function(){};this.Class.extend=function(prop){var _super=this.prototype;initializing=true;var prototype=new this();initializing=false;for(var name in prop){prototype[name]=typeof prop[name]=="function"&&typeof _super[name]=="function"&&fnTest.test(prop[name])?(function(name,fn){return function(){var tmp=this._super;this._super=_super[name];var ret=fn.apply(this,arguments);this._super=tmp;return ret;};})(name,prop[name]):prop[name];}
12
+ function Class(){if(!initializing&&this.init)
13
+ this.init.apply(this,arguments);}
14
+ Class.prototype=prototype;Class.prototype.constructor=Class;Class.extend=arguments.callee;return Class;};}).call(Liquid);Liquid.Tag=Liquid.Class.extend({init:function(tagName,markup,tokens){this.tagName=tagName;this.markup=markup;this.nodelist=this.nodelist||[];this.parse(tokens);},parse:function(tokens){},render:function(context){return'';}});Liquid.Block=Liquid.Tag.extend({init:function(tagName,markup,tokens){this.blockName=tagName;this.blockDelimiter="end"+this.blockName;this._super(tagName,markup,tokens);},parse:function(tokens){if(!this.nodelist)this.nodelist=[];this.nodelist.length=0;var token=tokens.shift();tokens.push('');while(tokens.length){if(/^\{\%/.test(token)){var tagParts=token.match(/^\{\%\s*(\w+)\s*(.*)?\%\}$/);if(tagParts){if(this.blockDelimiter==tagParts[1]){this.endTag();return;}
15
+ if(tagParts[1]in Liquid.Template.tags){this.nodelist.push(new Liquid.Template.tags[tagParts[1]](tagParts[1],tagParts[2],tokens));}else{this.unknownTag(tagParts[1],tagParts[2],tokens);}}else{throw("Tag '"+token+"' was not properly terminated with: %}");}}else if(/^\{\{/.test(token)){this.nodelist.push(this.createVariable(token));}else{this.nodelist.push(token);}
16
+ token=tokens.shift();}
17
+ this.assertMissingDelimitation();},endTag:function(){},unknownTag:function(tag,params,tokens){switch(tag){case'else':throw(this.blockName+" tag does not expect else tag");break;case'end':throw("'end' is not a valid delimiter for "+this.blockName+" tags. use "+this.blockDelimiter);break;default:throw("Unknown tag: "+tag);}},createVariable:function(token){var match=token.match(/^\{\{(.*)\}\}$/);if(match){return new Liquid.Variable(match[1]);}
18
+ else{throw("Variable '"+token+"' was not properly terminated with: }}");}},render:function(context){return this.renderAll(this.nodelist,context);},renderAll:function(list,context){return Liquid.extensions.arrayTools.map((list||[]),function(token,i){var output='';try{output=(token['render'])?token.render(context):token;}catch(e){output=context.handleError(e);}
19
+ return output;});},assertMissingDelimitation:function(){throw(this.blockName+" tag was never closed");}});Liquid.Document=Liquid.Block.extend({init:function(tokens){this.blockDelimiter=[];this.parse(tokens);},assertMissingDelimitation:function(){}});Liquid.Strainer=Liquid.Class.extend({init:function(context){this.context=context;},respondTo:function(methodName){methodName=methodName.toString();if(methodName.match(/^__/))return false;if(Liquid.extensions.arrayTools.include(Liquid.Strainer.requiredMethods,methodName))return false;return(methodName in this);}});Liquid.Strainer.filters={};Liquid.Strainer.globalFilter=function(filters){for(var f in filters){Liquid.Strainer.filters[f]=filters[f];}}
20
+ Liquid.Strainer.requiredMethods=['respondTo','context'];Liquid.Strainer.create=function(context){var strainer=new Liquid.Strainer(context);for(var f in Liquid.Strainer.filters){strainer[f]=Liquid.Strainer.filters[f];}
21
+ return strainer;}
22
+ Liquid.Context=Liquid.Class.extend({init:function(assigns,registers,rethrowErrors){this.scopes=[assigns?assigns:{}];this.registers=registers?registers:{};this.errors=[];this.rethrowErrors=rethrowErrors;this.strainer=Liquid.Strainer.create(this);},get:function(varname){return this.resolve(varname);},set:function(varname,value){this.scopes[0][varname]=value;},hasKey:function(key){return(this.resolve(key))?true:false;},push:function(){var scpObj={};this.scopes.unshift(scpObj);return scpObj},merge:function(newScope){return Liquid.extensions.object.update.call(this.scopes[0],newScope);},pop:function(){if(this.scopes.length==1){throw"Context stack error";}
23
+ return this.scopes.shift();},stack:function(lambda,bind){var result=null;this.push();try{result=lambda.apply(bind?bind:this.strainer);}finally{this.pop();}
24
+ return result;},invoke:function(method,args){if(this.strainer.respondTo(method)){var result=this.strainer[method].apply(this.strainer,args);return result;}else{return(args.length==0)?null:args[0];}},resolve:function(key){switch(key){case null:case'nil':case'null':case'':return null;case'true':return true;case'false':return false;case'blank':case'empty':return'';default:if((/^'(.*)'$/).test(key))
25
+ {return key.replace(/^'(.*)'$/,'$1');}
26
+ else if((/^"(.*)"$/).test(key))
27
+ {return key.replace(/^"(.*)"$/,'$1');}
28
+ else if((/^(\d+)$/).test(key))
29
+ {return parseInt(key.replace(/^(\d+)$/,'$1'));}
30
+ else if((/^(\d[\d\.]+)$/).test(key))
31
+ {return parseFloat(key.replace(/^(\d[\d\.]+)$/,'$1'));}
32
+ else if((/^\((\S+)\.\.(\S+)\)$/).test(key)){var range=key.match(/^\((\S+)\.\.(\S+)\)$/),left=parseInt(range[1]),right=parseInt(range[2]),arr=[];if(isNaN(left)){let varLeft=this.resolve(range[1]);left=parseInt(varLeft);if(isNaN(left)){throw new Error('Incorrect param for range: '+key);}}
33
+ if(isNaN(right)){let varRight=this.resolve(range[2]);right=parseInt(varRight);if(isNaN(right)){throw new Error('Incorrect param for range: '+key);}}
34
+ var limit=right-left+1;for(var i=0;i<limit;i++)arr.push(i+left);return arr;}else{var result=this.variable(key);return result;}}},findVariable:function(key){for(var i=0;i<this.scopes.length;i++){var scope=this.scopes[i];if(scope&&typeof(scope[key])!=='undefined'){var variable=scope[key];if(typeof(variable)=='function'){variable=variable.apply(this);scope[key]=variable;}
35
+ if(variable&&this._isObject(variable)&&('toLiquid'in variable)){variable=variable.toLiquid();}
36
+ if(variable&&this._isObject(variable)&&('setContext'in variable)){variable.setContext(self);}
37
+ return variable;}};return null;},variable:function(markup){if(typeof markup!='string'){return null;}
38
+ var parts=markup.match(/\[[^\]]+\]|(?:[\w\-]\??)+/g),firstPart=parts.shift(),squareMatch=firstPart.match(/^\[(.*)\]$/);if(squareMatch)
39
+ {firstPart=this.resolve(squareMatch[1]);}
40
+ var object=this.findVariable(firstPart),self=this;if(object){Liquid.extensions.arrayTools.each(parts,function(part){var squareMatch=part.match(/^\[(.*)\]$/);if(squareMatch){var part=self.resolve(squareMatch[1]);if(typeof(object[part])=='function'){object[part]=object[part].apply(this);}
41
+ object=object[part];if(self._isObject(object)&&('toLiquid'in object)){object=object.toLiquid();}}else{if((self._isObject(object)||typeof(object)=='hash')&&(part in object)){var res=object[part];if(typeof(res)=='function'){res=object[part]=res.apply(self);}
42
+ if(self._isObject(res)&&('toLiquid'in res)){object=res.toLiquid();}
43
+ else{object=res;}}
44
+ else if((/^\d+$/).test(part)){var pos=parseInt(part);if(typeof(object[pos])=='function'){object[pos]=object[pos].apply(self);}
45
+ if(self._isObject(object)&&self._isObject(object[pos])&&('toLiquid'in object[pos])){object=object[pos].toLiquid();}
46
+ else{object=object[pos];}}
47
+ else if(object&&typeof(object[part])=='function'&&Liquid.extensions.arrayTools.include(['length','size','first','last'],part)){object=object[part].apply(part);if('toLiquid'in object){object=object.toLiquid();}}
48
+ else{return object=null;}
49
+ if(self._isObject(object)&&('setContext'in object)){object.setContext(self);}}});}
50
+ return object;},addFilters:function(filters){filters=Liquid.extensions.arrayTools.flatten(filters);Liquid.extensions.arrayTools.each(filters,function(f){if(!this._isObject(f)){throw("Expected object but got: "+typeof(f))}
51
+ this.strainer.addMethods(f);});},handleError:function(err){this.errors.push(err);if(this.rethrowErrors){throw err;}
52
+ return"Liquid error: "+(err.message?err.message:(err.description?err.description:err));},_isObject:function(obj){return obj!=null&&typeof(obj)=='object';}});Liquid.Template=Liquid.Class.extend({init:function(){this.root=null;this.registers={};this.assigns={};this.errors=[];this.rethrowErrors=false;},parse:function(src){this.root=new Liquid.Document(Liquid.Template.tokenize(src));return this;},render:function(){if(!this.root){return'';}
53
+ var args={ctx:arguments[0],filters:arguments[1],registers:arguments[2]}
54
+ var context=null;if(args.ctx instanceof Liquid.Context){context=args.ctx;this.assigns=context.assigns;this.registers=context.registers;}else{if(args.ctx){Liquid.extensions.object.update.call(this.assigns,args.ctx);}
55
+ if(args.registers){Liquid.extensions.object.update.call(this.registers,args.registers);}
56
+ context=new Liquid.Context(this.assigns,this.registers,this.rethrowErrors)}
57
+ if(args.filters){context.addFilters(arg.filters);}
58
+ try{return this.root.render(context).join('');}finally{this.errors=context.errors;}},renderWithErrors:function(){var savedRethrowErrors=this.rethrowErrors;this.rethrowErrors=true;var res=this.render.apply(this,arguments);this.rethrowErrors=savedRethrowErrors;return res;}});Liquid.Template.tags={};Liquid.Template.registerTag=function(name,klass){Liquid.Template.tags[name]=klass;}
59
+ Liquid.Template.registerFilter=function(filters){Liquid.Strainer.globalFilter(filters)}
60
+ Liquid.Template.tokenize=function(src){var tokens=src.split(/(\{\%.*?\%\}|\{\{.*?\}\}?)/);if(tokens[0]==''){tokens.shift();}
61
+ return tokens;}
62
+ Liquid.Template.parse=function(src){return(new Liquid.Template()).parse(src);}
63
+ Liquid.Variable=Liquid.Class.extend({init:function(markup){this.markup=markup;this.name=null;this.filters=[];var self=this;var match=markup.match(/\s*("[^"]+"|'[^']+'|[^\s,|]+)/);if(match){this.name=match[1];var filterMatches=markup.match(/\|\s*(.*)/);if(filterMatches){var filters=filterMatches[1].split(/\|/);Liquid.extensions.arrayTools.each(filters,function(f){var matches=f.match(/\s*(\w+)/);if(matches){var filterName=matches[1];var filterArgs=[];Liquid.extensions.arrayTools.each(Liquid.extensions.arrayTools.flatten((f.match(/(?:[:|,]\s*)("[^"]+"|'[^']+'|[^\s,|]+)/g)||[])),function(arg){var cleanupMatch=arg.match(/^[\s|:|,]*(.*?)[\s]*$/);if(cleanupMatch)
64
+ {filterArgs.push(cleanupMatch[1]);}});self.filters.push([filterName,filterArgs]);}});}}},render:function(context){if(this.name==null){return'';}
65
+ var output=context.get(this.name);Liquid.extensions.arrayTools.each(this.filters,function(filter){var filterName=filter[0],filterArgs=Liquid.extensions.arrayTools.map((filter[1]||[]),function(arg){return context.get(arg);});filterArgs.unshift(output);output=context.invoke(filterName,filterArgs);});return output;}});Liquid.Condition=Liquid.Class.extend({init:function(left,operator,right){this.left=left;this.operator=operator;this.right=right;this.childRelation=null;this.childCondition=null;this.attachment=null;},evaluate:function(context){context=context||new Liquid.Context();var result=this.interpretCondition(this.left,this.right,this.operator,context);switch(this.childRelation){case'or':return(result||this.childCondition.evaluate(context));case'and':return(result&&this.childCondition.evaluate(context));default:return result;}},or:function(condition){this.childRelation='or';this.childCondition=condition;},and:function(condition){this.childRelation='and';this.childCondition=condition;},attach:function(attachment){this.attachment=attachment;return this.attachment;},isElse:false,interpretCondition:function(left,right,op,context){if(!op)
66
+ {return context.get(left);}
67
+ left=context.get(left);right=context.get(right);op=Liquid.Condition.operators[op];if(!op)
68
+ {throw("Unknown operator "+op);}
69
+ var results=op(left,right);return results;},toString:function(){return"<Condition "+this.left+" "+this.operator+" "+this.right+">";}});Liquid.Condition.operators={'==':function(l,r){return(l==r);},'=':function(l,r){return(l==r);},'!=':function(l,r){return(l!=r);},'<>':function(l,r){return(l!=r);},'<':function(l,r){return(l<r);},'>':function(l,r){return(l>r);},'<=':function(l,r){return(l<=r);},'>=':function(l,r){return(l>=r);},'contains':function(l,r){if(Object.prototype.toString.call(l)==='[object Array]'){return Liquid.extensions.arrayTools.indexOf(l,r)>=0;}else{return l.match(r);}},'hasKey':function(l,r){return Liquid.extensions.object.hasKey.call(l,r);},'hasValue':function(l,r){return Liquid.extensions.object.hasValue.call(l,r);}}
70
+ Liquid.ElseCondition=Liquid.Condition.extend({isElse:true,evaluate:function(context){return true;},toString:function(){return"<ElseCondition>";}});Liquid.Drop=Liquid.Class.extend({setContext:function(context){this.context=context;},beforeMethod:function(method){},invokeDrop:function(method){var results=this.beforeMethod();if(!results&&(method in this))
71
+ {results=this[method].apply(this);}
72
+ return results;},hasKey:function(name){return true;}});var hackObjectEach=function(fun){if(typeof fun!="function")
73
+ throw'Object.each requires first argument to be a function';var i=0;var thisp=arguments[1];for(var p in this){var value=this[p],pair=[p,value];pair.key=p;pair.value=value;fun.call(thisp,pair,i,this);i++;}
74
+ return null;};Liquid.Template.registerTag('assign',Liquid.Tag.extend({tagSyntax:/((?:\(?[\w\-\.\[\]]\)?)+)\s*=\s*(.+)/,init:function(tagName,markup,tokens){var parts=markup.match(this.tagSyntax);if(parts){this.to=parts[1];this.from=parts[2];}else{throw("Syntax error in 'assign' - Valid syntax: assign [var] = [source]");}
75
+ this._super(tagName,markup,tokens)},render:function(context){var value=new Liquid.Variable(this.from);Liquid.extensions.arrayTools.last(context.scopes)[this.to.toString()]=value.render(context);return'';}}));Liquid.Template.registerTag('cache',Liquid.Block.extend({tagSyntax:/(\w+)/,init:function(tagName,markup,tokens){var parts=markup.match(this.tagSyntax)
76
+ if(parts){this.to=parts[1];}else{throw("Syntax error in 'cache' - Valid syntax: cache [var]");}
77
+ this._super(tagName,markup,tokens);},render:function(context){var output=this._super(context);Liquid.extensions.arrayTools.last(context.scopes)[this.to]=Liquid.extensions.arrayTools.flatten([output]).join('');return'';}}));Liquid.Template.registerTag('capture',Liquid.Block.extend({tagSyntax:/(\w+)/,init:function(tagName,markup,tokens){var parts=markup.match(this.tagSyntax)
78
+ if(parts){this.to=parts[1];}else{throw("Syntax error in 'capture' - Valid syntax: capture [var]");}
79
+ this._super(tagName,markup,tokens);},render:function(context){var output=this._super(context);Liquid.extensions.arrayTools.last(context.scopes)[this.to.toString()]=Liquid.extensions.arrayTools.flatten([output]).join('');return'';}}));Liquid.Template.registerTag('case',Liquid.Block.extend({tagSyntax:/("[^"]+"|'[^']+'|[^\s,|]+)/,tagWhenSyntax:/("[^"]+"|'[^']+'|[^\s,|]+)(?:(?:\s+or\s+|\s*\,\s*)("[^"]+"|'[^']+'|[^\s,|]+.*))?/,init:function(tagName,markup,tokens){this.blocks=[];this.nodelist=[];var parts=markup.match(this.tagSyntax)
80
+ if(parts){this.left=parts[1];}else{throw("Syntax error in 'case' - Valid syntax: case [condition]");}
81
+ this._super(tagName,markup,tokens);},unknownTag:function(tag,markup,tokens){switch(tag){case'when':this.recordWhenCondition(markup);break;case'else':this.recordElseCondition(markup);break;default:this._super(tag,markup,tokens);}},render:function(context){var self=this,output=[],execElseBlock=true;context.stack(function(){for(var i=0;i<self.blocks.length;i++){var block=self.blocks[i];if(block.isElse){if(execElseBlock==true){output=Liquid.extensions.arrayTools.flatten([output,self.renderAll(block.attachment,context)]);}
82
+ return output;}else if(block.evaluate(context)){execElseBlock=false;output=Liquid.extensions.arrayTools.flatten([output,self.renderAll(block.attachment,context)]);}};});return output;},recordWhenCondition:function(markup){while(markup){var parts=markup.match(this.tagWhenSyntax);if(!parts){throw("Syntax error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %} ");}
83
+ markup=parts[2];var block=new Liquid.Condition(this.left,'==',parts[1]);this.blocks.push(block);this.nodelist=block.attach([]);}},recordElseCondition:function(markup){if(Liquid.extensions.stringTools.strip((markup||''))!=''){throw("Syntax error in tag 'case' - Valid else condition: {% else %} (no parameters) ")}
84
+ var block=new Liquid.ElseCondition();this.blocks.push(block);this.nodelist=block.attach([]);}}));Liquid.Template.registerTag('comment',Liquid.Block.extend({render:function(context){return'';}}));Liquid.Template.registerTag('cycle',Liquid.Tag.extend({tagSimpleSyntax:/"[^"]+"|'[^']+'|[^\s,|]+/,tagNamedSyntax:/("[^"]+"|'[^']+'|[^\s,|]+)\s*\:\s*(.*)/,init:function(tag,markup,tokens){var matches,variables;matches=markup.match(this.tagNamedSyntax);if(matches){this.variables=this.variablesFromString(matches[2]);this.name=matches[1];}else{matches=markup.match(this.tagSimpleSyntax);if(matches){this.variables=this.variablesFromString(markup);this.name="'"+this.variables.toString()+"'";}else{throw("Syntax error in 'cycle' - Valid syntax: cycle [name :] var [, var2, var3 ...]");}}
85
+ this._super(tag,markup,tokens);},render:function(context){var self=this,key=context.get(self.name),output='';if(!context.registers['cycle']){context.registers['cycle']={};}
86
+ if(!context.registers['cycle'][key]){context.registers['cycle'][key]=0;}
87
+ context.stack(function(){var iter=context.registers['cycle'][key],results=context.get(self.variables[iter]);iter+=1;if(iter==self.variables.length){iter=0;}
88
+ context.registers['cycle'][key]=iter;output=results;});return output;},variablesFromString:function(markup){return Liquid.extensions.arrayTools.map(markup.split(','),function(varname){var match=varname.match(/\s*("[^"]+"|'[^']+'|[^\s,|]+)\s*/);return(match[1])?match[1]:null});}}));Liquid.Template.registerTag('for',Liquid.Block.extend({tagSyntax:/(\w+)\s+in\s+((?:\(?[\w\-\.\[\]]\)?)+)/,init:function(tag,markup,tokens){var matches=markup.match(this.tagSyntax);if(matches){this.variableName=matches[1];this.collectionName=matches[2];this.name=this.variableName+"-"+this.collectionName;this.attributes={};var attrmarkup=markup.replace(this.tagSyntax,'');var attMatchs=markup.match(/(\w*?)\s*\:\s*("[^"]+"|'[^']+'|[^\s,|]+)/g);if(attMatchs){Liquid.extensions.arrayTools.each(attMatchs,function(pair){pair=pair.split(":");this.attributes[Liquid.extensions.stringTools.strip(pair[0])]=Liquid.extensions.stringTools.strip(pair[1]);},this);}}else{throw("Syntax error in 'for loop' - Valid syntax: for [item] in [collection]");}
89
+ this._super(tag,markup,tokens);},render:function(context){var self=this,output=[],collection=(context.get(this.collectionName)||[]),range=[0,collection.length];if(!context.registers['for']){context.registers['for']={};}
90
+ if(this.attributes['limit']||this.attributes['offset']){var offset=0,limit=0,rangeEnd=0,segment=null;if(this.attributes['offset']=='continue')
91
+ {offset=context.registers['for'][this.name];}
92
+ else
93
+ {offset=context.get(this.attributes['offset'])||0;}
94
+ limit=context.get(this.attributes['limit']);rangeEnd=(limit)?offset+limit+1:collection.length;range=[offset,rangeEnd-1];context.registers['for'][this.name]=rangeEnd;}
95
+ segment=collection.slice(range[0],range[1]);if(!segment||segment.length==0){return'';}
96
+ context.stack(function(){var length=segment.length;Liquid.extensions.arrayTools.each(segment,function(item,index){context.set(self.variableName,item);context.set('forloop',{name:self.name,length:length,index:(index+1),index0:index,rindex:(length-index),rindex0:(length-index-1),first:(index==0),last:(index==(length-1))});output.push((self.renderAll(self.nodelist,context)||[]).join(''));});});return Liquid.extensions.arrayTools.flatten([output]).join('');}}));Liquid.Template.registerTag('if',Liquid.Block.extend({tagSyntax:/("[^"]+"|'[^']+'|[^\s,|]+)\s*([=!<>a-z_]+)?\s*("[^"]+"|'[^']+'|[^\s,|]+)?/,init:function(tag,markup,tokens){this.nodelist=[];this.blocks=[];this.pushBlock('if',markup);this._super(tag,markup,tokens);},unknownTag:function(tag,markup,tokens){if(Liquid.extensions.arrayTools.include(['elsif','else'],tag)){this.pushBlock(tag,markup);}else{this._super(tag,markup,tokens);}},render:function(context){var self=this,output='';context.stack(function(){for(var i=0;i<self.blocks.length;i++){var block=self.blocks[i];if(block.evaluate(context)){output=self.renderAll(block.attachment,context);return;}};})
97
+ return Liquid.extensions.arrayTools.flatten([output]).join('');},pushBlock:function(tag,markup){var block;if(tag=='else'){block=new Liquid.ElseCondition();}else{var expressions=markup.split(/\b(and|or)\b/).reverse(),expMatches=expressions.shift().match(this.tagSyntax);if(!expMatches){throw("Syntax Error in tag '"+tag+"' - Valid syntax: "+tag+" [expression]");}
98
+ var condition=new Liquid.Condition(expMatches[1],expMatches[2],expMatches[3]);while(expressions.length>0){var operator=expressions.shift(),expMatches=expressions.shift().match(this.tagSyntax);if(!expMatches){throw("Syntax Error in tag '"+tag+"' - Valid syntax: "+tag+" [expression]");}
99
+ var newCondition=new Liquid.Condition(expMatches[1],expMatches[2],expMatches[3]);newCondition[operator](condition);condition=newCondition;}
100
+ block=condition;}
101
+ block.attach([]);this.blocks.push(block);this.nodelist=block.attachment;}}));Liquid.Template.registerTag('ifchanged',Liquid.Block.extend({render:function(context){var self=this,output='';context.stack(function(){var results=self.renderAll(self.nodelist,context).join('');if(results!=context.registers['ifchanged']){output=results;context.registers['ifchanged']=output;}});return output;}}));Liquid.Template.registerTag('include',Liquid.Tag.extend({tagSyntax:/((?:"[^"]+"|'[^']+'|[^\s,|]+)+)(\s+(?:with|for)\s+((?:"[^"]+"|'[^']+'|[^\s,|]+)+))?/,init:function(tag,markup,tokens){var matches=(markup||'').match(this.tagSyntax);if(matches){this.templateName=matches[1];this.templateNameVar=this.templateName.substring(1,this.templateName.length-1);this.variableName=matches[3];this.attributes={};var attMatchs=markup.match(/(\w*?)\s*\:\s*("[^"]+"|'[^']+'|[^\s,|]+)/g);if(attMatchs){Liquid.extensions.arrayTools.each(attMatchs,function(pair){pair=pair.split(":");this.attributes[Liquid.extensions.stringTools.strip(pair[0])]=Liquid.extensions.stringTools.strip(pair[1]);},this);}}else{throw("Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]");}
102
+ this._super(tag,markup,tokens);},render:function(context){var self=this,source=Liquid.readTemplateFile(context.get(this.templateName)),partial=Liquid.parse(source),variable=context.get((this.variableName||this.templateNameVar)),output='';context.stack(function(){self.attributes.each=hackObjectEach;self.attributes.each(function(pair){context.set(pair.key,context.get(pair.value));})
103
+ if(variable instanceof Array){output=Liquid.extensions.arrayTools.map(variable,function(variable){context.set(self.templateNameVar,variable);return partial.render(context);});}else{context.set(self.templateNameVar,variable);output=partial.render(context);}});output=Liquid.extensions.arrayTools.flatten([output]).join('');return output}}));Liquid.Template.registerTag('unless',Liquid.Template.tags['if'].extend({render:function(context){var self=this,output='';context.stack(function(){var block=self.blocks[0];if(!block.evaluate(context)){output=self.renderAll(block.attachment,context);return;}
104
+ for(var i=1;i<self.blocks.length;i++){var block=self.blocks[i];if(block.evaluate(context)){output=self.renderAll(block.attachment,context);return;}};})
105
+ return Liquid.extensions.arrayTools.flatten([output]).join('');}}));Liquid.Template.registerTag('raw',Liquid.Block.extend({parse:function(tokens){if(!this.nodelist)this.nodelist=[];this.nodelist.length=0;var token=tokens.shift();tokens.push('');while(tokens.length){if(/^\{\%/.test(token)){var tagParts=token.match(/^\{\%\s*(\w+)\s*(.*)?\%\}$/);if(tagParts){if(this.blockDelimiter==tagParts[1]){this.endTag();return;}}}
106
+ this.nodelist.push(token||'');token=tokens.shift();}
107
+ this.assertMissingDelimitation();},render:function(context){return this.nodelist.join('');}}));Liquid.Template.registerTag('increment',Liquid.Tag.extend({tagSyntax:/((?:\(?[\w\-\.\[\]]\)?)+)/,init:function(tagName,markup,tokens){var parts=markup.match(this.tagSyntax);console.log(tagName,markup,tokens);console.log(parts[1]);if(parts){this.name=parts[1];}else{throw("Syntax error in 'assign' - Valid syntax: increment [var]");}
108
+ this._super(tagName,markup,tokens)},render:function(context){var self=this,key=self.name,output='';if(!context.registers['increment']){context.registers['increment']={};}
109
+ if(!context.registers['increment'][key]){context.registers['increment'][key]=0;}
110
+ output=String(context.registers['increment'][key]);context.registers['increment'][key]++;return output;}}));Liquid.Template.registerTag('decrement',Liquid.Tag.extend({tagSyntax:/((?:\(?[\w\-\.\[\]]\)?)+)/,init:function(tagName,markup,tokens){var parts=markup.match(this.tagSyntax);console.log(tagName,markup,tokens);console.log(parts[1]);if(parts){this.name=parts[1];}else{throw("Syntax error in 'assign' - Valid syntax: decrement [var]");}
111
+ this._super(tagName,markup,tokens)},render:function(context){var self=this,key=self.name,output='';if(!context.registers['decrement']){context.registers['decrement']={};}
112
+ if(!context.registers['decrement'][key]){context.registers['decrement'][key]=-1;}
113
+ output=String(context.registers['decrement'][key]);context.registers['decrement'][key]--;return output;}}));Liquid.Template.registerFilter({_HTML_ESCAPE_MAP:{'&':'&amp;','>':'&gt;','<':'&lt;','"':'&quot;',"'":'&#39;'},size:function(iterable){return(iterable['length'])?iterable.length:0;},downcase:function(input){return input.toString().toLowerCase();},upcase:function(input){return input.toString().toUpperCase();},capitalize:function(input){return Liquid.extensions.stringTools.capitalize(input.toString());},escape:function(input){var self=this;return input.replace(/[&<>"']/g,function(chr){return self._HTML_ESCAPE_MAP[chr];});},h:function(input){var self=this;return input.replace(/[&<>"']/g,function(chr){return self._HTML_ESCAPE_MAP[chr];});},default:function(input,default_value){return Liquid.extensions.object.isEmpty(input)?default_value:input;},truncate:function(input,length,string){if(!input||input==''){return'';}
114
+ length=length||50;string=string||"...";var seg=input.slice(0,length);return(input.length>length?input.slice(0,length)+string:input);},truncatewords:function(input,words,string){if(!input||input==''){return'';}
115
+ words=parseInt(words||15);string=string||'...';var wordlist=input.toString().split(" "),l=Math.max((words),0);return(wordlist.length>l)?wordlist.slice(0,l).join(' ')+string:input;},truncate_words:function(input,words,string){if(!input||input==''){return'';}
116
+ words=parseInt(words||15);string=string||'...';var wordlist=input.toString().split(" "),l=Math.max((words),0);return(wordlist.length>l)?wordlist.slice(0,l).join(' ')+string:input;},strip_html:function(input){return input.toString().replace(/<.*?>/g,'');},strip_newlines:function(input){return input.toString().replace(/\n/g,'')},join:function(input,separator){separator=separator||' ';return input.join(separator);},split:function(input,separator){separator=separator||' ';return input.split(separator);},sort:function(input){return input.sort();},reverse:function(input){return input.reverse();},replace:function(input,string,replacement){replacement=replacement||'';return input.toString().replace(new RegExp(string,'g'),replacement);},replace_first:function(input,string,replacement){replacement=replacement||'';return input.toString().replace(new RegExp(string,""),replacement);},newline_to_br:function(input){return input.toString().replace(/\n/g,"<br/>\n");},date:function(input,format){var date;if(input instanceof Date){date=input;}
117
+ if(!(date instanceof Date)&&input=='now'){date=new Date();}
118
+ if(!(date instanceof Date)&&typeof(input)=='number'){date=new Date(input*1000);}
119
+ if(!(date instanceof Date)&&typeof(input)=='string'){date=new Date(Date.parse(input));}
120
+ if(!(date instanceof Date)){return input;}
121
+ return date.strftime(format);},first:function(input){return input[0];},last:function(input){input=input;return input[input.length-1];},minus:function(input,number){return(Number(input)||0)-(Number(number)||0);},plus:function(input,number){return(Number(input)||0)+(Number(number)||0);},times:function(input,number){return(Number(input)||0)*(Number(number)||0);},divided_by:function(input,number){return(Number(input)||0)/(Number(number)||0);},modulo:function(input,number){return(Number(input)||0)%(Number(number)||0);},map:function(input,property){input=input||[];var results=[];for(var i=0;i<input.length;i++){results.push(input[i][property]);}
122
+ return results;},escape_once:function(input){var self=this;return input.replace(/["><']|&(?!([a-zA-Z]+|(#\d+));)/g,function(chr){return self._HTML_ESCAPE_MAP[chr];});},remove:function(input,string){return input.toString().replace(new RegExp(string,'g'),'');},remove_first:function(input,string){return input.toString().replace(string,'');},prepend:function(input,string){return''+(string||'').toString()+(input||'').toString();},append:function(input,string){return''+(input||'').toString()+(string||'').toString();}});if(!(new Date()).strftime){(function(){Date.ext={};Date.ext.util={};Date.ext.util.xPad=function(x,pad,r){if(typeof(r)=="undefined"){r=10}for(;parseInt(x,10)<r&&r>1;r/=10){x=pad.toString()+x}return x.toString()};Date.prototype.locale="en-GB";if(document.getElementsByTagName("html")&&document.getElementsByTagName("html")[0].lang){Date.prototype.locale=document.getElementsByTagName("html")[0].lang}Date.ext.locales={};Date.ext.locales.en={a:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],x:"%d/%m/%y",X:"%T"};if(typeof JSON!='undefined'){Date.ext.locales['en-US']=JSON.parse(JSON.stringify(Date.ext.locales.en));}else{Date.ext.locales["en-US"]=Date.ext.locales.en;};Date.ext.locales["en-US"].c="%a %d %b %Y %r %Z";Date.ext.locales["en-US"].x="%D";Date.ext.locales["en-US"].X="%r";Date.ext.locales["en-GB"]=Date.ext.locales.en;Date.ext.locales["en-AU"]=Date.ext.locales["en-GB"];Date.ext.formats={a:function(d){return Date.ext.locales[d.locale].a[d.getDay()]},A:function(d){return Date.ext.locales[d.locale].A[d.getDay()]},b:function(d){return Date.ext.locales[d.locale].b[d.getMonth()]},B:function(d){return Date.ext.locales[d.locale].B[d.getMonth()]},c:"toLocaleString",C:function(d){return Date.ext.util.xPad(parseInt(d.getFullYear()/100,10),0)},d:["getDate","0"],e:["getDate"," "],g:function(d){return Date.ext.util.xPad(parseInt(Date.ext.util.G(d)/100,10),0)},G:function(d){var y=d.getFullYear();var V=parseInt(Date.ext.formats.V(d),10);var W=parseInt(Date.ext.formats.W(d),10);if(W>V){y++}else{if(W===0&&V>=52){y--}}return y},H:["getHours","0"],I:function(d){var I=d.getHours()%12;return Date.ext.util.xPad(I===0?12:I,0)},j:function(d){var ms=d-new Date(""+d.getFullYear()+"/1/1 GMT");ms+=d.getTimezoneOffset()*60000;var doy=parseInt(ms/60000/60/24,10)+1;return Date.ext.util.xPad(doy,0,100)},m:function(d){return Date.ext.util.xPad(d.getMonth()+1,0)},M:["getMinutes","0"],p:function(d){return Date.ext.locales[d.locale].p[d.getHours()>=12?1:0]},P:function(d){return Date.ext.locales[d.locale].P[d.getHours()>=12?1:0]},S:["getSeconds","0"],u:function(d){var dow=d.getDay();return dow===0?7:dow},U:function(d){var doy=parseInt(Date.ext.formats.j(d),10);var rdow=6-d.getDay();var woy=parseInt((doy+rdow)/7,10);return Date.ext.util.xPad(woy,0)},V:function(d){var woy=parseInt(Date.ext.formats.W(d),10);var dow1_1=(new Date(""+d.getFullYear()+"/1/1")).getDay();var idow=woy+(dow1_1>4||dow1_1<=1?0:1);if(idow==53&&(new Date(""+d.getFullYear()+"/12/31")).getDay()<4){idow=1}else{if(idow===0){idow=Date.ext.formats.V(new Date(""+(d.getFullYear()-1)+"/12/31"))}}return Date.ext.util.xPad(idow,0)},w:"getDay",W:function(d){var doy=parseInt(Date.ext.formats.j(d),10);var rdow=7-Date.ext.formats.u(d);var woy=parseInt((doy+rdow)/7,10);return Date.ext.util.xPad(woy,0,10)},y:function(d){return Date.ext.util.xPad(d.getFullYear()%100,0)},Y:"getFullYear",z:function(d){var o=d.getTimezoneOffset();var H=Date.ext.util.xPad(parseInt(Math.abs(o/60),10),0);var M=Date.ext.util.xPad(o%60,0);return(o>0?"-":"+")+H+M},Z:function(d){return d.toString().replace(/^.*\(([^)]+)\)$/,"$1")},"%":function(d){return"%"}};Date.ext.aggregates={c:"locale",D:"%m/%d/%y",h:"%b",n:"\n",r:"%I:%M:%S %p",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"};Date.ext.aggregates.z=Date.ext.formats.z(new Date());Date.ext.aggregates.Z=Date.ext.formats.Z(new Date());Date.ext.unsupported={};Date.prototype.strftime=function(fmt){if(!(this.locale in Date.ext.locales)){if(this.locale.replace(/-[a-zA-Z]+$/,"")in Date.ext.locales){this.locale=this.locale.replace(/-[a-zA-Z]+$/,"")}else{this.locale="en-GB"}}var d=this;while(fmt.match(/%[cDhnrRtTxXzZ]/)){fmt=fmt.replace(/%([cDhnrRtTxXzZ])/g,function(m0,m1){var f=Date.ext.aggregates[m1];return(f=="locale"?Date.ext.locales[d.locale][m1]:f)})}var str=fmt.replace(/%([aAbBCdegGHIjmMpPSuUVwWyY%])/g,function(m0,m1){var f=Date.ext.formats[m1];if(typeof(f)=="string"){return d[f]()}else{if(typeof(f)=="function"){return f.call(d,d)}else{if(typeof(f)=="object"&&typeof(f[0])=="string"){return Date.ext.util.xPad(d[f[0]](),f[1])}else{return m1}}}});d=null;return str};})();}
123
+ var split;split=split||function(undef){var nativeSplit=String.prototype.split,compliantExecNpcg=/()??/.exec("")[1]===undef,self;self=function(str,separator,limit){if(Object.prototype.toString.call(separator)!=="[object RegExp]"){return nativeSplit.call(str,separator,limit);}
124
+ var output=[],flags=(separator.ignoreCase?"i":"")+
125
+ (separator.multiline?"m":"")+
126
+ (separator.extended?"x":"")+
127
+ (separator.sticky?"y":""),lastLastIndex=0,separator=new RegExp(separator.source,flags+"g"),separator2,match,lastIndex,lastLength;str+="";if(!compliantExecNpcg){separator2=new RegExp("^"+separator.source+"$(?!\\s)",flags);}
128
+ limit=limit===undef?-1>>>0:limit>>>0;while(match=separator.exec(str)){lastIndex=match.index+match[0].length;if(lastIndex>lastLastIndex){output.push(str.slice(lastLastIndex,match.index));if(!compliantExecNpcg&&match.length>1){match[0].replace(separator2,function(){for(var i=1;i<arguments.length-2;i++){if(arguments[i]===undef){match[i]=undef;}}});}
129
+ if(match.length>1&&match.index<str.length){Array.prototype.push.apply(output,match.slice(1));}
130
+ lastLength=match[0].length;lastLastIndex=lastIndex;if(output.length>=limit){break;}}
131
+ if(separator.lastIndex===match.index){separator.lastIndex++;}}
132
+ if(lastLastIndex===str.length){if(lastLength||!separator.test("")){output.push("");}}else{output.push(str.slice(lastLastIndex));}
133
+ return output.length>limit?output.slice(0,limit):output;};String.prototype.split=function(separator,limit){return self(this,separator,limit);};return self;}();if(typeof exports!=='undefined'){if(typeof module!=='undefined'&&module.exports){exports=module.exports=Liquid;}
134
+ exports.Liquid=Liquid;}
@@ -0,0 +1,48 @@
1
+ ;(function(window, document){
2
+ 'use strict';
3
+
4
+ // prerequisites
5
+ if ( ! ( 'querySelectorAll' in document ) ){ return; }
6
+
7
+ if ( ! ( 'JekyllWebmentionIO' in window ) ){ window.JekyllWebmentionIO = {}; }
8
+
9
+ var $webmention_counts = document.querySelectorAll( '.webmention-count' ),
10
+ event_name = 'JekyllWebmentionIO:update_counters';
11
+
12
+ function updateCounts(){
13
+ var w = $webmention_counts.length,
14
+ $counter,
15
+ types, t, type,
16
+ count;
17
+
18
+ while ( w-- )
19
+ {
20
+ $counter = $webmention_counts[w];
21
+ // limited scope?
22
+ if ( 'dataset' in $counter &&
23
+ 'webmentionTypes' in $counter.dataset )
24
+ {
25
+ types = $counter.dataset.webmentionTypes.split(',');
26
+ t = types.length;
27
+ count = 0;
28
+ while ( t-- )
29
+ {
30
+ type = JekyllWebmentionIO.types[types[t]];
31
+ count += document.querySelectorAll( '.webmention.webmention--' + type ).length;
32
+ }
33
+ $counter.innerText = count;
34
+ }
35
+ else
36
+ {
37
+ $counter.innerText = document.querySelectorAll( '.webmention' ).length;
38
+ }
39
+ }
40
+ }
41
+
42
+ if ( $webmention_counts.length )
43
+ {
44
+ JekyllWebmentionIO.counter_update_event = new Event(event_name);
45
+ document.addEventListener(event_name, updateCounts, false);
46
+ }
47
+
48
+ }(this, this.document, this.JekyllWebmentionIO));
@@ -0,0 +1,34 @@
1
+ (function(window, document){
2
+
3
+ // prerequisites
4
+ if ( ! ( 'querySelectorAll' in document ) ){ return; }
5
+
6
+ if ( ! ( 'JekyllWebmentionIO' in window ) ){ window.JekyllWebmentionIO = {}; }
7
+
8
+ var targets = [],
9
+ $redirects = document.querySelector('meta[property="webmention:redirected_from"]'),
10
+ redirects,
11
+ base_url = window.location.origin,
12
+ $script;
13
+
14
+ targets.push( base_url + window.location.pathname );
15
+ if ( $redirects )
16
+ {
17
+ redirects = $redirects.getAttribute('content').split(',');
18
+ redirects.forEach(function( value ){
19
+ targets.push(
20
+ value.indexOf('//') < 0 ? base_url + value : value
21
+ );
22
+ });
23
+ redirects = false;
24
+ }
25
+
26
+ // Load up any unpublished webmentions on load
27
+ $script = document.createElement('script');
28
+ $script.async = true;
29
+ $script.src = 'https://webmention.io/api/mentions?' +
30
+ 'jsonp=window.JekyllWebmentionIO.processWebmentions&target[]=' +
31
+ targets.join( '&target[]=' );
32
+ document.querySelector('head').appendChild( $script );
33
+
34
+ }(this, this.document));
@@ -0,0 +1,21 @@
1
+ (function(window, JekyllWebmentionIO){
2
+ 'use strict';
3
+
4
+ // prerequisites
5
+ if ( ! ( 'WebSocket' in window ) ){ return; }
6
+
7
+ if ( ! ( 'JekyllWebmentionIO' in window ) ){ window.JekyllWebmentionIO = {}; }
8
+
9
+ var ws = new WebSocket('ws://webmention.io:8080');
10
+
11
+ ws.onopen = function(){
12
+ // Send the current window URL to the server to register to receive notifications about this URL
13
+ ws.send( window.location );
14
+ };
15
+
16
+ ws.onmessage = function( event ){
17
+ data = JSON.parse( event.data );
18
+ console.log( data );
19
+ };
20
+
21
+ }(this, window.JekyllWebmentionIO));
@@ -0,0 +1,74 @@
1
+ # (c) Aaron Gustafson
2
+ # https://github.com/aarongustafson/jekyll-webmention_io
3
+ # Licence : MIT
4
+ #
5
+ # This generator gathers webmentions of your pages
6
+ #
7
+
8
+ require 'uglifier'
9
+
10
+ module Jekyll
11
+ module WebmentionIO
12
+ using StringInflection
13
+ class CompileJS < Generator
14
+
15
+ safe true
16
+ priority :low
17
+
18
+ def generate(site)
19
+ if site.config['webmentions']['js'] == false
20
+ Jekyll::WebmentionIO::log 'info', 'Skipping JavaScript inclusion.'
21
+ return
22
+ end
23
+
24
+ config = {
25
+ 'destination' => "js",
26
+ 'uglify' => true
27
+ }
28
+ site_config = site.config['webmentions']['js'] || {}
29
+
30
+ config = config.merge(site_config)
31
+
32
+ source = File.join(File.dirname(File.expand_path(__FILE__)), '../assets/')
33
+
34
+ javascript = ''
35
+ Dir["#{source}/*.js"].each do |file|
36
+ handler = File.open(file, 'rb')
37
+ javascript << File.read(handler)
38
+ end
39
+
40
+ # Dump in types
41
+ js_types = []
42
+ Jekyll::WebmentionIO::types.each do |type|
43
+ js_types.push "'#{type}': '#{type.to_singular}'"
44
+ end
45
+ types_js = <<-EOF
46
+ ;(function(window,JekyllWebmentionIO){
47
+ if ( ! ( \'JekyllWebmentionIO\' in window ) ){ window.JekyllWebmentionIO = {}; }
48
+ JekyllWebmentionIO.types = { TYPES };
49
+ }(this, this.JekyllWebmentionIO));
50
+ EOF
51
+ javascript << types_js.sub( /TYPES/, js_types.join(',') )
52
+
53
+ unless config['uglify'] == false
54
+ uglify_config = {
55
+ :harmony => true
56
+ }
57
+ javascript = Uglifier.new(uglify_config).compile(javascript)
58
+ end
59
+
60
+ # Generate the file in the source folder
61
+ source_file_destination = "#{site.config['source']}/#{config['destination']}"
62
+ Dir.mkdir( source_file_destination ) unless File.exists?( source_file_destination )
63
+ file_name = 'JekyllWebmentionIO.js'
64
+ File.open("#{source_file_destination}/#{file_name}", 'w') { |f| f.write( javascript ) }
65
+
66
+ unless config['deploy'] == false
67
+ # Make sure Jekyll picks it up too
68
+ js_file = StaticFile.new(site, site.config['source'], config['destination'], file_name)
69
+ site.static_files << js_file
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end