Git Product home page Git Product logo

sammy's Introduction

Sammy

http://sammyjs.org

Description

Sammy is a tiny javascript framework built on top of jQuery inspired by Ruby's Sinatra.

Installation

Download Sammy.js and install it in your public javascripts directory. Include it in your document AFTER jQuery.

Usage

Like Sinatra, a Sammy application revolves around 'routes'. Routes in Sammy are a little different, though. Not only can you define 'get' and 'post' routes, but you can also bind routes to custom events triggered by your application.

You set up a Sammy Application by passing a Function to the $.sammy (which is a shortcut for the Sammy.Application constructor).

$.sammy(function() {

  this.get('#/', function() {
    $('#main').text('Welcome!');
  });

});

Inside the 'app' function() this is the Application. This is where you can configure the application and add routes.

Above, we defined a get() route. When the browser is pointed to #/ the function passed to that route will be run. Inside the route function, this is a Sammy.EventContext. EventContext has a bunch of special methods and properties including a params hash, the ability to redirect, render partials, and more.

In its coolness, Sammy can handle multiple chained asynchronous callbacks on a route.

this.get('#/', function(context,next) {
  $('#main').text('Welcome!');
  $.get('/some/url',function(){
    // save the data somewhere
    next();
  });
}, function(context,next) {
  $.get('/some/other/url',function(){
    // save this data too
    next();
  });
});

Once you've defined an application the only thing left to do is run it. The best-practice behavior is to encapsulate run() in a document.ready block:

var app = $.sammy(...)

$(function() {
  app.run();
});

This will guarantee that the DOM is loaded before we try to apply functionality to it.

Dependencies

Sammy requires jQuery >= 1.4.1 Get it from: http://jquery.com

More!

Learn!

Keep informed!

Authors

Sammy.js was created and is maintained by Aaron Quint with additional features and fixes contributed by these talented individuals:

  • Frank Prößdorf / endor
  • Alexander Lang / langalex
  • Scott McMillin / scottymac
  • ZhangJinzhu / jinzhu
  • Jesse Hallett / hallettj
  • Jonathan Vaught / gravelpup
  • Jason Davies / jasondavies
  • Russell Jones / CodeOfficer
  • Geoff Longman
  • Jens Bissinger / dpree
  • Tim Caswell / creationix
  • Mark Needham
  • SamDeLaGarza
  • Mickael Bailly / dready92
  • Rich Manalang / manalang
  • Brian Mitchell / binary42
  • Assaf Arkin / assaf
  • James Rosen / jamesrosen
  • Chris Mytton
  • kbuckler
  • dvv
  • Ben Vinegar / benvinegar
  • Avi Deitcher / deitch

Donate!

If you're using Sammy.js in production or just for fun, instead of gifting me a beer - please consider donating to the Code for Other People Fund. - you can probably spare a dollar or ten and it will be greatly appreciated.

License

Sammy is covered by the MIT License. See LICENSE for more information.

sammy's People

Contributors

assaf avatar bsingr avatar creationix avatar crofty avatar deitch avatar delrosario avatar dfunckt avatar endor avatar erpheus avatar fhemberger avatar flashingpumpkin avatar hallettj avatar jinzhu avatar jpgarcia avatar kbuckler avatar kevingessner avatar le0pard avatar mkoryak avatar mtscout6 avatar piecioshka avatar quirkey avatar rikkert avatar rikkiprince avatar rmanalan avatar samdelagarza avatar stephen101 avatar stevenharman avatar tdetoux avatar teelahti avatar vicb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sammy's Issues

routes with short paths added after those with longer paths are lost.

What I did:
In the backend demo I moved the 'connnecting' route up in the file, before the "#/' route

What I expected:
The demo would work the same as it did originally.

What I saw:
The app path '/#' shows the Sammy header and nothing else.

Whats working and what is not:
I debugged into the app and was able to determine that the startup was working correctly, until line 386 of sammy.js. the route '#/' is never run as the app does not contain the "#/' route in this.routes.

I think the culprit (and do correct me if I'm wrong) is that the method 'route' is attempting to stick the '#/' in to the list for the verb 'get' but the loop to find the correct spot does not handle the case where a new route path is shorter than any already in the list. When this happens the loop completes without adding the route at all. The loop I refer to is line 264 in sammy.js.

snippet starting at 263 in sammy.js:

    // place in order of longest path first
    this.each(this.routes[verb], function(i, route)  {
      if (path.toString().length >= route.path.toString().length) {
        this.routes[verb].splice(i, 0, r);
        // exit each()
        return false; 
      }
    });

Does that make sense?

Add Sammy.Flash plugin

I have a site that I would like to use Sammy with. Each page change can result in one or more informational or error messages that should be displayed. Each page, though, displays the error messages in a slightly different way. So far I've been passing them in query parameters to the next Sammy action, but I'd prefer to use something more like the Rails Flash:

this.use(Sammy.Flash);

this.post('#/foos', function(context) {
  // ...
  context.flash.info = "Added Foo";
  context.redirect('#/foos');
});
this.get('#/foos', function(context) {
  // ...
  context.flash.now.info = 'Have you seen our new Baz feature?';
  $('#flash').html(context.flash.toHtml());
});

That is, there's a context.flash and a context.flash.now. Sammy.Flash.prototype would include a default toHtml implementation, but it would be easy to change.

I'm happy to work on this; I just wanted to get the idea started in a public place before I jumped in.

Chrome 404s when requesting same url twice in a row

This is some odd behaviour that seems specific to chrome, safari and firefox are unaffected.

I have a request to #/settings/
this request basically toggles a component's visibility and then redirects to #/

This will work the first time
But when requesting this URL the second time, a 404 will be printed and the event won't reach the sammy application.

Now, this only happens when requesting an identical URL a second time without asking for something else in the meanwhile. Example if I ask for #/settings/ and then #/main/ and then #/settings/ everything will work. But if I ask for #/settings/ and then #/settings/ sequentially, the second request will fail with a 404.

partial system doesn't cache templates

I guess this is more of a feature request than anything. The partial method doesn't cache templates the first time it uses them, and I think because of that fails to render large groups of "items" consistently because its loading the template each time.

My example is I'm looping thru an array of 12 objects, and rendering each one using the partial method. The number it actually renders jumps all over the place. If I place a counter with an alert into the loop it alerts on all 12 and displays all 12, I guess because the alert slows it down.

JSON-Plugin not working in IE6

Sammy is a great framework, thanks for sharing it!

Too bad I (and probably many others) still need to support IE6.

Adding...

...
this.use(Sammy.JSON);
...

in IE6 causes an exception to be raised in IE6. Works fine in FF 3.x.

When I add the json2.js library to the HTML-header, the error disappears, but that is not how it's supposed to be, right?

Sammy.template changes have broken Sammy.form

If you download the demo for 0.6.2 and use the form from the documentation in the Sammy.form code everything works beautifully.

If you download the official 0.6.2 pack then forms do not work due to escaping problems.

If you look in the Sammy.template code at:

String(template)
.replace(/[\r\t\n]/g, " ")
.replace(/"/g, '"')
.split("<%").join("\t")
.replace(/((^|%>)[^\t])/g, "$1\r")
.replace(/\t=(.
?)%>/g, "",h($1),"")
.replace(/\t!(.*?)%>/g, "",$1,"")
.split("\t").join("");")
.split("%>").join("_$$$.push("")
.split("\r").join("")
+ "");}return **
$$$**_.join('');");

... right in the middle there is h($1). Remove the h() and everything
works fine. h() is a shortcut to escapeHTML. The notes against
escapeHTML says "Escape HTML in string, use in templates to prevent
script injection.". So it would appear that the updated code is
protecting itself from itself and breaking the form plugin.

Not really sure how to progress my code.

app.bind has no app.unbind counterpart

It would be nice if you could unbind events that were bound to the app using app.bind

In the interim, anyone who finds this bug will be interested in app.listeners, which is an object containing all the bound functions.

structured like so

app.listeners == {'change': [function() {}, function() {}, ...]...}

Sammy 0.5.1 on FF3.0.x runs the route twice probably because of polling...

Hi Aaron. I just started testing an app that works great on FF3.6/Chrome/Safari4.x, but when I run it on FF3.0.x, I noticed that I started getting dup calls to my Sammy routes causing duplicate ajax requests to be run. I'm trying to debug it, but thought I should file a bug. Have you heard of this? Here's the log:

[Tue Mar 09 2010 21:01:26 GMT-0800 (PST)] no native hash change, falling back to polling
[Tue Mar 09 2010 21:01:26 GMT-0800 (PST)] runRoute get #/
GET http://wc/rest/api/resourceIndex
[Tue Mar 09 2010 21:01:26 GMT-0800 (PST)] no native hash change, falling back to polling
[Tue Mar 09 2010 21:01:26 GMT-0800 (PST)] runRoute get #/

Thanks!

Rich

Redirect to same hash url but different verb does not work

this.get('#/users', function(e) {   }

this.post('#/users', function(e) {
  e.redirect('#/users')  // this does nothing
}

I was a bit surprised that this didn't work -- posting to a collection to create a new item, then redirecting back to the collection does not work, since the hash does not change.

DataLocationProxy is broken

Hello,

your Sammy.DataLocationProxy is broken : unfortunately jQuery (1.4.2) fires the setData event before actually updating the data store. So when you try a simple :

$.sammy('#main').bind("location-changed", function() {
    $.sammy('#main').log(  $.sammy('#main').getLocation()  );  
});

you get the old location...

Get params are cut off

When using additional parameters in addresses like /#/home/index?page=1 everything after question mark is removed

Need event for when partial has finished rendering

I am having a problem whereby there doesn't seem to be any way to provide a callback to the "partial" function that is called when a template has finished rendering. I understand that a callback is called when a collection is supplied, however I need to bind an event to an item that is being rendered in a partial and don't want to resort to timeouts... Am I missing something?

context.partial('my.template', { tasks: data });
app.trigger('make-list-sortable'); // this needs to be delayed until the list is in the DOM

I don't have a lot of hair left due to this one ;)

Templates not html escaping strings, possible XSS problems

I've just noticed that templates do not escape the interpolated values by default, there is an ugly workaround but I really don't like it:

<%= $('<div/>').text(my_variable).html() %>

I think these should be escaped by default like Rails 3, or there should at least be a way to escape via a simple function.

p.s. is there a way to user the 'partial' function inside another partial as this doesn't work:

<%= partial('blah') %>

hashchange event

We found that IE 6 and IE 7 had back button issues. By dropping in Ben Alman's hashchange special event, we were able to seamlessly fix the issue.

After calling swap consider triggering "change" automatically

I noticed that when I load template code (using swap) that contains a form, its submit doesn't get intercepted.

Proposed change to swap:

swap: function(content) {
  var s = this.$element().html(content);
  this.trigger('changed');
  return s;
},

The workaround, for now is that the user code will just have to call this trigger after swapping.

upload files

Hi! Is it possible to upload files from forms bound to '#...' actions?

TIA,
---Vladimir

window.console in IE8

When using IE8 with developer tools to open a Sammy application, the application does not work. This is because Sammy is calling the function window.console.log.apply, which causes IE8 to fail with the error "object does not support this property or method".

Sammy has a guard to prevent this sort of thing from happening - which is to check whether window.console exists before calling the function. Unfortunately, IE8 with developer tools has a window.console, but does not have the log.apply method Sammy was expecting to be present.

Confirmation of this strange behaviour here:

http://stackoverflow.com/questions/690251/what-happened-to-console-log-in-ie8/1262103#1262103

"It's worth noting that console.log in IE8 isn't a true Javascript function. It doesn't support the apply or call methods." - James Wheare

Here's how we got the application back to a working state, courtesy of my collegue, pat. In this case, the logging will not work for IE:

var isMSIE = /@cc_on!@/false;

if (!isMSIE && typeof window.console !== 'undefined') {
Sammy.addLogger(function() {
window.console.log.apply(window.console, arguments);
});
} else if (!isMSIE && typeof console !== 'undefined') {
Sammy.addLogger.push(function() {
console.log.apply(console, arguments);
});
}

Calling Helpers outside of the event context

Today I have been using helpers and plugin patterns. I really like the way they work and they are generally far easier to understand and use, for newbies, than the raw jQuery $.extend functionality. The bit I got stuck with is trying to use helpers within then() blocks where you are outside of the event context: what I wanted to do is add a trigger pointing at a helper within a then() block:

this.partial('some.template', { attributes: defaults })
.then(function() {
$('#clear').click(this.someHelperMethod);
});

So, what I am trying to do is bind a click event to a helper to make the code easier to manage. The code above will not work as 'this' is the wrong object, the following examples will work but exhibit various degrees of complexity:

var that = this;
this.partial('some.template', { attributes: defaults })
.then(function() {
$('#clear').click(that.someHelperMethod);
});

this.partial('some.template', { attributes: defaults })
.then(function() {
$('#clear').click(App.context_prototype.prototype.someHelperMethod);
});

this.partial('some.template', { attributes: defaults })
.then(function() {
$('#clear').click(this.event_context.someHelperMethod);
});

The documentation is pretty clear on this topic, although it has taken me a long time to understand, but I can't help but feel that doing something a little different here would be very valuable.

Cannot run test suite

Opening index.html directly causes the following failure:

XMLHttpRequest cannot load file://localhost/Users/jamesrosen/Projects/sammy/test/fixtures/partial.html. Origin null is not allowed by Access-Control-Allow-Origin.

Running test/test_server fares only slightly better. The request to /fixtures/partial.template fails because Sinatra doesn't have a MIME type for .template files.

URL Encoding

Hey, first of all, thanks for such a great library! We're really excited about Sammy and are incorporating it into lots of projects at my company.

Anyhoo, when a form is submitted to an Sammy application, the form fields are form encoded and set to the Location hash.

For example:

<form id="live_form" action="#/live" method="get">
    <input name="spaces" type="text" value="value with spaces" />
    <input name="pluses" type="text" value="+++" />
    <input type="submit" class="submit" />
</form>

Sets the location as follows:

#/live?spaces=value+with+spaces&pluses=%2B%2B%2B

... and is decoded as:

params['spaces'] => "value+with+spaces"
params['pluses'] => "+++"

So, at this point, we can't tell the difference between and space and a plus character. There are possibly other aliasing issues as well. I've mended this, but don't know if/how this will impact other users.

Routes not working as documented

The docs state that "Paths can be either full URIs (eg. /blah/blah/blah) or just the end hash (eg. #/mypath)". I can't get anything before the # to work. In the following, neither alerts happen when visiting '/my/url' or '/my/url#/thing'. The '#/foo' path works fine:

this.get('/my/url', function(context) {
  alert('Howdy 1');
});

this.get('/my/url#/thing', function(context) {
  alert('Howdy 2');
});

this.get('#/foo', function(context) {
  alert('Howdy 3');
});

Regex escaping in embedded javascript in templates

No biggie. Usually when I include Javascript in my templates I just code normally. I found that a correct regex wasn't matching because of an unescaped character.

I'll show it with an example:

This is normal code in a .js file, later included in the template

   return e.className.replace(/[^\d]/g, '');

when I put this code inside a template, however, the backslash needs to be escaped:

  return e.className.replace(/[^\\d]/g, '');

I'm submitting this issue mainly because it took me a while to figure out why the regex was not matching.

uuid() method getting overridden in QtWebView preventing firing of events

Seems like the this.uuid() sometimes never gets called under certain environments. Specifically with QT 4.6 webkit. Seems like that method is hidden by some core related javascript.

what happens is that the namespace property of the application receives an object when it should retrieve a uuid string, causing the triggering phase to fail when it is called.

A simple renaming of the .uuid method would do the trick
As a temporary solution i've replaced the method call to uuid() in the constructor of the application with the actual contents of that method.

Been scratching my head for 3 days on this issue. Glad i found a temporary fix!

Context is as follows:

  • maemo5 scratchbox
  • phonegap maemo ( thanks @jtoivola )
  • sammyjs ( 0.5.3 )
  • some jquery libs and more...

You'll want to say that my other libs is what's overriding the .uuid() method, but that's not the case, given that the application works on every browser i've tested it on, only the hybrid QtWebView that failed.

Cheers @quirkey and others for an awesome library! Truly amazing!

Redirect not triggering new callback

I have the following routes:

var app = $.sammy('#main', function() {
  this.use(Sammy.NestedParams);

  this.get('#/', function(context) {
    if (some_boolean) {
      context.redirect('#/foo');
    } else {
      context.redirect('#/bar');
    }
  });
  this.get('#/foo', function() {
    alert('foo');
  });
  // ...
});

app.run('#/');

When I go to the page (and some_boolean is true), Sammy redirects me to #/foo, but the callback is never triggered. Do I have to also force some sort of event?

I get the following log messages:

[Tue Nov 09 2010 11:05:20 GMT-0800 (PST)] runRoute get #/
[Tue Nov 09 2010 11:05:20 GMT-0800 (PST)] native hash change exists, using

Going directly to #/foo as the first URL for the page has the expected results. Manually changing the URL to add #/foo after Sammy has run the redirect does nothing. It's as though Sammy has stopped processing.

Cache not honored in IE?

Hi, I'm a newbie since I'm using Sammy since yesterday, but I'm pretty sure that there is a behavior difference between IE and Firefox/Chrome: in IE (version 8 in w7) when I use context.load with cache set to false, it still loads the cached version. Let me know if you need further details.

Templates not cached on initial template loading

After I turned caching on, I noticed that the same template was getting loaded multiple times when rendering partials.

Investigating this further, I saw that the ajax call hadn't returned before the next call to render the partial, so another ajax request request was made.

In our application, we were looping over a number of elements and this caused a considerable slowdown that made it seem like the application wasn't loading on iphone/android web browsers.

My fix was to defer a callback if the initial ajax call to load the template hadn't returned.

http://github.com/iffius/sammy/commit/a82198406810f4666195fc39051986bb7efea2e5

Cheers,

routes not based on hash aren't working

In the Docs:

get('/test/123', function() {
...
});

But I'm not seeing this work at all, getLocation returns '' when the hashtag is omitted and I can't find out where the code is that would implement this, which sucks because I really need it :)

hash proxy binding

On firefox (didn't test others), there is an issue using $.sammy("app").location_proxy.bind()|unbind().
You are using jQuery die() method to unbind, which does not seem to unbind events added with the bind() method. On the doc you find that die() is to use remove events handlers attached with live.

unbind: function() {
    $(window).die('hashchange.' + this.app.eventNamespace());
},

Mickael

track app state outside of URL

I'd love to be able to track a sammy app's state privately inside the dom element that the application is attached too, and not in the URL. It would be particularly useful in places where you run multiple apps on a page and don't want state collisions.

debug logging goes crazy when no initial route is set for run()

I define a sammy app and specify debug = true; ... then i load up that page and launch the app without a default route in the run() method. .... logged events being output to console repetitiously scream down the page, until I do something that triggers a route match. After an initial route is matched logging behaves as expected, only logging when route changes are observed.

Debug mode works inconsistently

Turning on debug mode for a sammy app via:

this.debug = true;

and turning it on globally via:

Sammy.Application.prototype.debug = true;

... seems to not have the same effect. Sammy.Application.prototype is more verbose.

Problem with partial rendering

Seem to be having an issue with rendering a partial, it just seemingly stops half way through with no error messages or reasons.

item.template contains <%= title %> to

this.get('#/', function() {
    var event_context = this;
    event_context.partial('/templates/item.template', { "title": "Hello!" });
}

After poking around for a while it seems to be stemming from the success call on line 1391, for some reason success is never trigged unless I monkey patch the code to have an ajax option of:

dataType: 'plain'

In order to get around this problem I've had to create a helper for rending partials that calls this:

event_context.load(location, { dataType: "plain" })
    .interpolate({ "data": data}, location)
    .then(function(content) {
        event_context.swap(content);
    });

But this feels very hacky just to get a partial rendering, is there a bug here or am I just doing something wrong?

Cheers,
Michael

Documentation code is mangled

The code examples in the documentation are greatly mishappen, for example:

<h1>{{title}}<h1>

   Hey, ! Welcome to Handlebars!

Wat's up with that? Why not just use gists for displaying code in the documentation?

routes don't appear to work as expected in IE8 since 0.4

Just tested a 0.4 sammy app in IE8 and it appears that only the first route (i.e. app.run('#/') ) works. If I click a link that goes to a different route, nothing happens.

After reverting to 0.3.1 everything seems to work fine.

inline template with mustache, not working with method_alias

var app = $.sammy(function() {

this.use(Sammy.Mustache); // works fine
this.use(Sammy.Mustache, "ms"); // not working: this.mustache is not a function

this.get('#/', function(ctx) {
ctx.log(this.mustache("hello, {{name}}", {name:"foo"}));
});

});

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.