npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

paperclip-fast

v0.6.15

Published

controller logic:

Downloads

1

Readme

Paperclip is a very fast template engine for JavaScript.

Features

  • inline javascript
  • compiled templates
  • explicit data-binding (one-way, two-way, unbound operators)
  • works with older browsers (IE 8+ tested)
  • accepts vanilla objects
  • works with NodeJS
  • supports custom pollyfills

Examples

Performance

Paperclip templates are translated from HTML, straight to JavaScript - this also includes data-bindings. For example, here's a template:

hello {{name}}!

Here's the templated translated to JavaScript:

module.exports = (function(fragment, block, element, text, comment, parser, modifiers) {
  return fragment([text("hello "), block({
    'value': {
      run: function() {
          return this.context.name;
      },
      refs: [ ["name"] ]
    }
  })]);
});

Pretty clear what's going on. Here's what we know at a glance:

  1. Generated DOM is identical to the HTML templates. No weird manipulations here.
  2. Data-bindings are identified as the template is created. Note that this happens once for every template. Paperclip takes each translated template, caches them, and uses the browser's native cloneNode() whenever a template is used.
  3. JavaScript references within the templates are identified at translation time, and cached in the data-binding.

As it turns out, the method above for generating templates is very efficient. Essentially, paperclip does the least amount of work necessary to update the DOM since it know where everything is.

Paperclip will also lazily batch DOM changes together into one update, and run them on requestAnimationFrame. This kind of optimization is similar to how layout engines work, and helps prevent unnecessary performance penalties in the browser.

Installation

npm install paperclip --save-exact

Basic API

template template(source)

Creates a new template

var pc = require("paperclip");
var template = pc.template("hello {{name}}!");

template.bind(context).render()

context - Object, or BindableObject

binds the template to a context, and returns a document fragment

var pc = require("paperclip");
var template = pc.template("hello {{name}}!");
var view = template.bind({ name: "Bull Murray" });
document.body.appendChild(view.render()); // will show "hello Bill Murray"

paperclip.modifier(modifierName, modifier)

registers a new modifier. Here's a markdown example:

var pc = require("paperclip");
pc.modifier("markdown", require("marked"));
var template = pc.template("{{ content | markdown }}");
document.body.appendChild(template.bind({
  content: "hello **world**!"
}).render());

Template Syntax

{{ blocks }}

Variable blocks as placeholders for information that might change. For example:

hello {{ name.first }} {{ name.last }}!

You can also specify blocks within attributes.

my favorite color is <span style="color: {{color}}">{{color}}</span>

Paperclip also supports inline javascript. For example:

hello {{ message || "World" }}! <br />
inline-json {{ {'5+10 is':5+10, 'message is defined?' : message ? 'yes' : 'no' } | json }}

Modifiers

Modifiers format data in a variable block. A good example of this might be presenting data to the user depending on their locale, or parsing data into markdown. Here are a few examples of how you can use modifiers:


Converting content to markdown:

{{ html: content | markdown }}

Uppercasing & converting to markdown:

{{ html: content | uppercase | markdown }}

Modifiers with parameters:

A human that is {{age}} years old is like a {{ age | divide(5.6) }} year old dog!

Binding Operators

Paperclip comes with various binding operators that give you full control over how references are handled. You can easily specify whether to bind one way, two ways, or not at all. Here's the basic syntax:

Two-way binding:
<input class="form-control" data-bind="{{ model: <~>fullName }}" />

Bind input value to fullName only:
<input class="form-control" data-bind="{{ model: ~>fullName }}" />

Bind fullName to input value only:

<input class="form-control" data-bind="{{ model: <~fullName }}" />

Unbound helper - don't watch for any changes:
{{ ~fullName }}

Built-in components

{{ html: content }}

Similar to escaping content in mustache ({{{content}}}). Good for security.

Unsafe:
{{ html: content }} <br />

Safe:
{{ content }} <br />

{{ #if: condition }}

Conditional block helper

<input type="text" class="form-control" placeholder="What's your age?" data-bind="{{ model: <~>age }}"></input>
{{#if: age >= 18 }}
  You're legally able to vote in the U.S.
{{/elseif: age > 16 }}
  You're almost old enough to vote in the U.S.
{{/else}}
  You're too young to vote in the U.S.
{{/}}

data-bind attributes

data-bind attributes are inspired by knockout.js. This is useful if you want to attach behavior to any DOM element.

{{ model: context }}

Input data-binding

<input type="text" class="form-control" placeholder="Type in a message" data-bind="{{ model: <~>message }}"></input>
<h3>{{message}}</h3>

Notice the <~> operator. This tells paperclip to bind both ways. See binding operators for more info.

{{ event: expression }}

Executed when an event is fired on the DOM element. Here are all the available events:

  • onChange - called when an element changes
  • onClick - called when an element is clicked
  • onLoad - called when an element loads - useful for &lt;img /&gt;
  • onSubmit - called on submit - useful for &lt;form /&gt;
  • onMouseDown - called on mouse down
  • onMouseUp - called on mouse up
  • onMouseOver - called on mouse over
  • onMouseOut - called on mouse out
  • onKeyDown - called on key down
  • onKeyUp - called on key up
  • onEnter - called on enter key up
  • onDelete - called on delete key up
<input type="text" class="form-control" placeholder="Type in a message" data-bind="{{ onEnter: enterPressed = true, focus: true }}"></input>

{{#if: enterPressed }}
  enter pressed
{{/}}

{{ show: bool }}

Toggles the display mode of a given element. This is similar to the {{if:expression}} conditional helper.

{{ css: styles }}

Sets the css of a given element. For example:

how hot is it (fahrenheit)?: <input type="text" class="form-control" data-bind="{{ model: <~>temp }}"></input> <br />

<style type="text/css">
.cool { color: blue;   }
.warm { color: yellow; }
.hot  { color: red;    }
</style>

<strong data-bind="{{
  css: {
    cool    : temp > 0 || !temp,
    warm    : temp > 60,
    hot     : temp > 90
  }
}}">
  {{
    temp > 60 ?
    temp > 90 ? "it's hot" : "it's warm" :
    "it's cool"
  }}
</strong>

{{ style: styles }}

Sets the style of a given element.

color: <input type="text" data-bind="{{ model: <~>color }}" class="form-control"></input> <br />
size: <input type="text" data-bind="{{ model: <~>size }}" class="form-control"></input> <br />
<span data-bind="{{
  style: {
    color       : color,
    'font-size' : size
  }
}}">Hello World</span>

{{ disable: bool }}

Toggles the enabled state of an element.

<button data-bind={{ disable: !formIsValid }}>Sign Up</button>

{{ focus: bool }}

Focuses cursor on an element.

<input data-bind={{ focus: true }}></input>

Advanced API

paperclip.blockBinding(name, blockBindingClass)

Registers a new block binding class. Block bindings allow you to modify how templates behave. Some examples include the {{#if:condition}}{{/}}, and {{html:content}}.

BaseBlockBinding(options)

Base class to extend when creating custom block bindings. Here's an example for a components binding:

var pc = require("paperclip");

var ComponentBlockBinding = pc.BaseBlockBinding.extend({
  bind: function (context) {
    this.view = this.template.bind();
    this.section.appendChild(this.view.render());
    pc.BaseBlockBinding.prototype.bind.call(this, context);
  },
  _onChange: function (properties) {
    this.view.context.setProperties(properties);
  }
});

pc.blockBinding("hello", ComponentBlockBinding.extend({
  hello: pc.template("hello <strong>{{message}}</strong>!")
});

template:

{{ hello: { message: "world" }}}

override bind(context)

Called when the block is added, and bound to the DOM. This is where you initialize your binding. Be sure to call paperclip.BaseBlockBinding.prototype.bind.call(this, context) if you override. this method

override unbind()

Called when the block is removed from the DOM. This is a cleanup method.

override _onChange(context)

Called whenever the properties change for the block binding. These properties are defined in the template. Here's the syntax:

{{blockName: blockProperties }}

nodeFactory

the node factory for creating elements. Use this to make your block binding compatible with the NodeJS and the browser.

scriptName

the name registered for the block binding

section

the document section which contains all the elements

contentTemplate

the content template - this might be undefined if your block binding doesn't have {{#block:properties}}content{{/}}.

childBlockTemplate

The child block template. Used in the conditional block.