連絡先管理アプリ

さらに復習用。

by FiNGAHOLiC

HTML

<script src="https://raw.github.com/maccman/spine/master/lib/spine.js"></script>
<script src="https://raw.github.com/maccman/spine/master/lib/local.js"></script>
<script src="https://raw.github.com/maccman/spine/master/lib/manager.js"></script>
<script src="https://raw.github.com/maccman/spine/master/lib/route.js"></script>
<script src="https://raw.github.com/jquery/jquery-tmpl/master/jquery.tmpl.min.js"></script>
<header id="mod-header"><h1>Spine Contacts</h1></header>
<article id="mod-article"></article>

<script type="text/x-jquery-tmpl" id="mod-tmpl-item">
    <div class="item">
        {{if username}}
            ${username}
        {{else}}
            <i>No Name</i>
        {{/if}}
    </div>
</script>

<script type="text/x-jquery-tmpl" id="mod-tmpl-sidebar">
    <header>
        <input type="search" placeholder="search" results="0" incremental="true" autofocus>
    </header>
    <div class="items"></div>
    <footer>
        <button>New Contact</button>
    </footer>
</script>

<script type="text/x-jquery-tmpl" id="mod-tmpl-show">
    <header>
        <a class="edit">Edit</a>
    </header>
    <div class="content">
        <p>${username}</p>
        <p>${email}</p>
    </div>
</script>

<script type="text/x-jquery-tmpl" id="mod-tmpl-edit">
    <header>
        <a class="save">Save</a>
        <a class="delete">Delete</a>
    </header>
    <div class="content">
        <form>
            <label>
                <span>Username</span>
                <input type="text" name="username" value="${username}">
            </label>
            <label>
                <span>Email</span>
                <input type="email" name="email" value="${email}">
            </label>
        </form>
    </div>
</script>

CSS

#mod-header{
    margin-bottom:1px;
    padding:10px;
    background:#000;
    color:#fff;
}
.contacts{
    overflow:hidden;
}
.sidebar{
    width:150px;
    float:left;
}
.sidebar header{
    padding:10px;
    background:#999;
    text-align:center;
}
.sidebar header a{
    margin-right:1px;
    padding:5px 10px;
    display:inline-block;
    background:#fff;
}
.sidebar header input{
    width:128px;
}
.sidebar footer{
    padding:10px;
    background:#999;
    text-align:center;
}
.sidebar .items{
    margin-top:1px;
}
.sidebar .item{
    margin-bottom:1px;
    padding:5px 10px;
    background:#efefef;
    cursor:pointer;
}
.sidebar .active{
    background:#c00;
    color:#fff;
}
.main{
    margin-left:151px;
}
.main header{
    overflow:hidden;
    background:#efefef;
}
.main header a{
    margin-right:1px;
    padding:5px 10px;
    display:block;
    float:left;
    background:#999;
    color:#fff;
    cursor:pointer;
}
.main .content{
    padding:10px;
}
.main p{
    margin-bottom:5px;
}
.main label{
    margin-bottom:5px;
    display:block;
}
.main label span{
    width:100px;
    display:inline-block;
}
.stack > *:not(.active){
    display:none;
}

JavaScript

(function($, window, document, undefined){
    
    $.extend($.fn, {
        item: function(){
            var item = $(this).tmplItem().data;
            return ($.isFunction(item.reload) ? item.reload() : null);
        },
        forItem: function(item){
            return this.filter(function(){
                var compare = $(this).tmplItem().data;
                // Returns a boolean indicating
                // if the other record is equal (i.e. same class and ID) as
                // the current instance.
                // http://spinejs.com/api/models
                if(item.eql && item.eql(compare) || item === compare) return true;
            });
        }
    });

    var Contact = Spine.Model.sub();
    Contact.configure('Contact', 'username', 'email');
    Contact.extend(Spine.Model.Local);
    Contact.extend({
        filter: function(query){
            if(!query) return this.all();
            var query = query.toLowerCase();
            return this.select(function(item){
                var _ref, _ref1;
                return ((_ref = item.username) != null ? _ref.toLowerCase().indexOf(query) : void 0) !== -1 || ((_ref1 = item.email) != null ? _ref1.toLowerCase().indexOf(query) : void 0) !== -1;
            });
        }
    });
    
    var Item = Spine.Controller.sub({
        events: {
            'click .item': 'click'
        },
        selectFirst: true,
        init: function(){
            this.bind('change', this.change);
        },
        template: function(items){
            return $('#mod-tmpl-item').tmpl(items);
        },
        change: function(item){
            this.current = item;
            if(!this.current){
                this.children().removeClass('active');
                return;
            };
            this.children().removeClass('active');
            this.children().forItem(this.current).addClass('active');
        },
        render: function(items){
            if(items){
                this.items =...