• This repository has been archived on 26/Apr/2018
  • Stars
    star
    595
  • Rank 75,217 (Top 2 %)
  • Language
    CoffeeScript
  • License
    MIT License
  • Created almost 13 years ago
  • Updated about 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A simple and powerful client-side view framework that works in zero-gravity, no longer maintained.

SpacePen Build Status

🚨 This library is no longer maintained 🚨

Version 5.x of SpacePen is intended to be included as a direct dependency of 1.0-compatible Atom packages. If you're looking for SpacePen 3.x, used in Atom Core, check out the 3.x branch.

Write markup on the final frontier

SpacePen is a powerful but minimalistic client-side view framework for CoffeeScript. It combines the "view" and "controller" into a single jQuery object, whose markup is expressed with an embedded DSL similar to Markaby for Ruby.

Changes In Version 4

This version of SpacePen depends on HTML 5 custom elements to support lifecycle hooks that previously depended on all DOM manipulation being performed via jQuery. The afterAttach and beforeRemove hooks have been replaced with attached and detached and their semantics have been altered.

If you need to use SpacePen in an environment that doesn't support custom elements, consider using the previous major version or switching frameworks.

Basics

View objects extend from the View class and have a @content class method where you express their HTML contents with an embedded markup DSL:

class Spacecraft extends View
  @content: ->
    @div =>
      @h1 "Spacecraft"
      @ol =>
        @li "Apollo"
        @li "Soyuz"
        @li "Space Shuttle"

Views descend from jQuery's prototype, so when you construct one you can call jQuery methods on it just as you would a DOM fragment created with $(...).

view = new Spacecraft
view.find('ol').append('<li>Star Destroyer</li>')

view.on 'click', 'li', ->
  alert "They clicked on #{$(this).text()}"

But SpacePen views are more powerful than normal jQuery fragments because they let you define custom methods:

class Spacecraft extends View
  @content: -> ...

  addSpacecraft: (name) ->
    @find('ol').append "<li>#{name}</li>"


view = new Spacecraft
view.addSpacecraft "Enterprise"

You can also pass arguments on construction, which get passed to both the @content method and the view's constructor.

class Spacecraft extends View
  @content: (params) ->
    @div =>
      @h1 params.title
      @ol =>
        @li name for name in params.spacecraft

view = new Spacecraft(title: "Space Weapons", spacecraft: ["TIE Fighter", "Death Star", "Warbird"])

Methods from the jQuery prototype can be gracefully overridden using super:

class Spacecraft extends View
  @content: -> ...

  hide: ->
    console.log "Hiding Spacecraft List"
    super()

If you override the View class's constructor, ensure you call super. Alternatively, you can define an initialize method, which the constructor will call for you automatically with the constructor's arguments.

class Spacecraft extends View
  @content: -> ...

  initialize: (params) ->
    @title = params.title

Outlets and Events

SpacePen will automatically create named reference for any element with an outlet attribute. For example, if the ol element has an attribute outlet=list, the view object will have a list entry pointing to a jQuery wrapper for the ol element.

class Spacecraft extends View
  @content: ->
    @div =>
      @h1 "Spacecraft"
      @ol outlet: "list", =>
        @li "Apollo"
        @li "Soyuz"
        @li "Space Shuttle"

  addSpacecraft: (name) ->
    @list.append("<li>#{name}</li>")

Elements can also have event name attributes whose value references a custom method. For example, if a button element has an attribute click=launchSpacecraft, then SpacePen will invoke the launchSpacecraft method on the button's parent view when it is clicked:

class Spacecraft extends View
  @content: ->
    @div =>
      @h1 "Spacecraft"
      @ol =>
        @li click: 'launchSpacecraft', "Saturn V"

  launchSpacecraft: (event, element) ->
    console.log "Preparing #{element.name} for launch!"

Markup DSL Details

Tag Methods (@div, @h1, etc.)

As you've seen so far, the markup DSL is pretty straightforward. From the @content class method or any method it calls, just invoke instance methods named for the HTML tags you want to generate. There are 3 types of arguments you can pass to a tag method:

  • Strings: The string will be HTML-escaped and used as the text contents of the generated tag.

  • Hashes: The key-value pairs will be used as the attributes of the generated tag.

  • Functions (bound with =>): The function will be invoked in-between the open and closing tag to produce the HTML element's contents.

If you need to emit a non-standard tag, you can use the @tag(name, args...) method to name the tag with a string:

@tag 'bubble', type: "speech", => ...

Text Methods

  • @text(string): Emits the HTML-escaped string as text wherever it is called.

  • @raw(string): Passes the given string through unescaped. Use this when you need to emit markup directly that was generated beforehand.

Subviews

Subviews are a great way to make your view code more modular. The @subview(name, view) method takes a name and another view object. The view object will be inserted at the location of the call, and a reference with the given name will be wired to it from the parent view. A parentView reference will be created on the subview pointing at the parent.

class Spacecraft extends View
  @content: (params) ->
    @div =>
      @subview 'launchController', new LaunchController(countdown: params.countdown)
      @h1 "Spacecraft"
      ...

Freeform Markup Generation

You don't need a View class to use the SpacePen markup DSL. Call View.render with an unbound function (->, not =>) that calls tag methods, and it will return a document fragment for ad-hoc use. This method is also assigned to the $$ global variable for convenience.

view.list.append $$ ->
  @li =>
    @text "Starship"
    @em "Enterprise"

jQuery extensions

$.fn.view

You can retrieve the view object for any DOM element by calling view() on it. This usually shouldn't be necessary, as most DOM manipulation will take place within the view itself using outlet references, but is occasionally helpful.

view = new Spacecraft
$('body').append(view)

# assuming no other li elements on the DOM, for example purposes,
# the following expression should be true
$('li').view() == view

Attached/Detached Hooks

The initialize method is always called when the view is still a detached DOM fragment, before it is appended to the DOM. This is usually okay, but occasionally you'll have some initialization logic that depends on the view actually being on the DOM. For example, you may depend on applying a CSS rule before measuring an element's height.

For these situations, use the attached hook. It will be called whenever your element is actually attached to the DOM. Past versions of SpacePen would also call this hook when your element was attached to another detached node, but that behavior is no longer supported.

To be notified when your element is detached from the DOM, implement the detached hook.

class Spacecraft extends View
  @content: -> ...

  attached: ->
    console.log "With CSS applied, my height is", @height()

  detached: ->
    console.log "I have been detached."

Hacking on SpacePen

git clone https://github.com/atom/space-pen.git
cd space-pen
npm install
npm start

More Repositories

1

xray

An experimental next-generation Electron-based text editor
Rust
8,531
star
2

electron-starter

Example Electron app
CoffeeScript
529
star
3

terminal

Atom Terminal package - *not currently maintained*
CoffeeScript
230
star
4

autocomplete

See https://github.com/atom/autocomplete-plus for the package currently bundled with Atom
CoffeeScript
64
star
5

text-document

CoffeeScript
56
star
6

node-runas

Run command synchronously (with Administrator privilege)
C++
46
star
7

emissary

Node mixins for subscribing to and emitting events *no longer maintained*
CoffeeScript
25
star
8

release-notes

Notifies you of the latest Atom changes
CoffeeScript
24
star
9

tree-sitter-syntax

JavaScript
14
star
10

interval-skip-list

A JavaScript data structure for finding all intervals that overlap a point.
CoffeeScript
13
star
11

atom-patch

JavaScript
12
star
12

marker-index

C++
11
star
13

feedback

Send feedback from Atom
CoffeeScript
9
star
14

notify

Yet another Node file system notification module
Rust
8
star
15

walken

Power walk directories
CoffeeScript
5
star
16

patrick

Take a repo from over here and move it over there
CoffeeScript
5
star
17

display-index

A data structure for efficient translation between screen and buffer coordinates in Atom.
JavaScript
4
star
18

custom-object

Override object's methods without harmony proxy
C++
3
star
19

tomdoc

Parse tomdoc with JS / Coffee *no longer maintained*
CoffeeScript
2
star
20

node-vm-compatibility-layer

Make vm module in node v0.11.x support obsolete APIs of node v0.10.x
JavaScript
2
star
21

text-transform

CoffeeScript
2
star
22

buffer-offset-index

C++
1
star