Merge pull request #37 from thelink2012/test-time

Simple Hodl Address
This commit is contained in:
OutCast3k 2015-12-29 14:27:09 +00:00
commit a9cfa44c44
8 changed files with 552 additions and 31 deletions

5
css/bootstrap-datetimepicker.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -12,10 +12,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/bootstrap.min.css" media="screen"> <link rel="stylesheet" href="css/bootstrap.min.css" media="screen">
<link rel="stylesheet" href="css/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="css/style.css" media="screen"> <link rel="stylesheet" href="css/style.css" media="screen">
<script type="text/javascript" src="js/jquery-1.9.1.min.js"></script> <script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="js/moment.min.js"></script>
<script type="text/javascript" src="js/transition.js"></script>
<script type="text/javascript" src="js/collapse.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script> <script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript" src="js/crypto-min.js"></script> <script type="text/javascript" src="js/crypto-min.js"></script>
<script type="text/javascript" src="js/crypto-sha256.js"></script> <script type="text/javascript" src="js/crypto-sha256.js"></script>
@ -54,6 +59,7 @@
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="#newAddress" data-toggle="tab">New Address</a></li> <li><a href="#newAddress" data-toggle="tab">New Address</a></li>
<li><a href="#newMultiSig" data-toggle="tab">MultiSig Address</a></li> <li><a href="#newMultiSig" data-toggle="tab">MultiSig Address</a></li>
<li><a href="#newTimeLocked" data-toggle="tab">New Time Locked Address</a></li>
<li><a href="#newHDaddress" data-toggle="tab">HD Address</a></li> <li><a href="#newHDaddress" data-toggle="tab">HD Address</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#newTransaction" data-toggle="tab">Transaction</a></li> <li><a href="#newTransaction" data-toggle="tab">Transaction</a></li>
@ -377,6 +383,73 @@
<br> <br>
</div> </div>
<div class="tab-pane tab-content" id="newTimeLocked">
<h2>New Time Locked Address <small>Coins can be released only after a certain date</small></h2>
<div class="row">
<div class="col-md-11">
<p>Public keys can be <a href="#newAddress">generated in your browser</a> or from your bitcoin client</a>.</p>
<p>Enter the public key that will be able to unlock the funds after the a certain date.
</div>
<div class="col-md-1">
</div>
</div>
<div class="row">
<div class="form-horizontal">
<div class="col-xs-11">
<input id="timeLockedPubKey" type="text" class="form-control pubkey">
</div>
</div>
</div>
<p>Enter the date and time required to release the coins</p>
<div class="row">
<div class='col-xs-3'>
<div class="form-group">
<div class='input-group date' id='timeLockedDateTimePicker'>
<input type='text' class="form-control" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
<div class='col-xs-3'>
<small>System Local Time</small>
</div>
</div>
<br>
<div id="timeLockedErrorMsg" class="alert alert-danger" style="display:none;"></div>
<div class="alert alert-success hidden" id="timeLockedData">
<label>Address</label>
<p>Payment should be made to this address:</p>
<div class="row">
<div class="col-lg-6">
<div class="input-group">
<input type="text" class="form-control address" value="" readonly>
<span class="input-group-btn">
<button class="qrcodeBtn btn btn-default" type="button" data-toggle="modal" data-target="#modalQrcode"><span class="glyphicon glyphicon-qrcode"></span></button>
</span>
</div>
</div>
</div>
<label>Redeem Script</label>
<p>This script should be <i>saved and should be shared with all the participants before a payment is made</i>, so they may validate the authenticity of the address, it will also be used later to release the bitcoins.</p>
<textarea class="form-control script" style="height:160px" readonly></textarea>
<label>Shareable URL</label>
<input type="text" class="scriptUrl form-control" readonly>
</div>
<input type="button" class="btn btn-primary" value="Submit" id="newTimeLockedAddress">
<br>
</div>
<div class="tab-pane tab-content" id="newHDaddress"> <div class="tab-pane tab-content" id="newHDaddress">
<h2>New HD Address <small>making bip32 even easier</small></h2> <h2>New HD Address <small>making bip32 even easier</small></h2>
<p>Use the form below to generate a <i>master</i> hierarchical deterministic address.</p> <p>Use the form below to generate a <i>master</i> hierarchical deterministic address.</p>
@ -585,24 +658,64 @@
<div class="hidden verifyData" id="verifyRsData"> <div class="hidden verifyData" id="verifyRsData">
<h4>Redeem Script</h4> <h4>Redeem Script</h4>
<p><span style="float:right"><a href="javascript:;" target="_blank" class="verifyLink" title="Link to this page"><span class="glyphicon glyphicon-link"></span></a></span>The above redeem script has been decoded</p> <p><span style="float:right"><a href="javascript:;" target="_blank" class="verifyLink" title="Link to this page"><span class="glyphicon glyphicon-link"></span></a></span>The above redeem script has been decoded</p>
<label>Multi Signature Address</label>
<div class="row"> <div class="hidden" id="verifyRsDataMultisig">
<div class="col-lg-6"> <label>Multi Signature Address</label>
<div class="input-group"> <div class="row">
<input type="text" class="form-control address multisigAddress" value="" readonly> <div class="col-lg-6">
<span class="input-group-btn"> <div class="input-group">
<button class="qrcodeBtn btn btn-default" type="button" data-toggle="modal" data-target="#modalQrcode"><span class="glyphicon glyphicon-qrcode"></span></button> <input type="text" class="form-control address multisigAddress" value="" readonly>
</span> <span class="input-group-btn">
</div> <button class="qrcodeBtn btn btn-default" type="button" data-toggle="modal" data-target="#modalQrcode"><span class="glyphicon glyphicon-qrcode"></span></button>
</div> </span>
</div> </div>
<label>Required Signatures</label> </div>
<p class="signaturesRequired">?</p> </div>
<label>Signatures Required from</label> <label>Required Signatures</label>
<table class="table table-striped table-hover"> <p class="signaturesRequired">?</p>
<tbody> <label>Signatures Required from</label>
</tbody> <table class="table table-striped table-hover">
</table> <tbody>
</tbody>
</table>
</div>
<div class="hidden" id="verifyRsDataHodl">
<label>Hodl Address</label>
<div class="row">
<div class="col-lg-6">
<div class="input-group">
<input type="text" class="form-control address" value="" readonly>
<span class="input-group-btn">
<button class="qrcodeBtn btn btn-default" type="button" data-toggle="modal" data-target="#modalQrcode"><span class="glyphicon glyphicon-qrcode"></span></button>
</span>
</div>
</div>
</div>
<label>Required Signature</label>
<div class="row">
<div class="col-lg-6">
<div class="input-group">
<input type="text" class="form-control pubkey" value="" readonly>
<span class="input-group-btn">
<button class="qrcodeBtn btn btn-default" type="button" data-toggle="modal" data-target="#modalQrcode"><span class="glyphicon glyphicon-qrcode"></span></button>
</span>
</div>
</div>
</div>
<label>Unlock Time</label>
<div class="row">
<div class='col-lg-6'>
<div class="input-group">
<input type="text" class="form-control date" value="" readonly>
</div>
</div>
</div>
<br>
</div>
</div> </div>
<div class="hidden verifyData" id="verifyTransactionData"> <div class="hidden verifyData" id="verifyTransactionData">

9
js/bootstrap-datetimepicker.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -135,6 +135,36 @@
return {'address':address, 'redeemScript':redeemScript}; return {'address':address, 'redeemScript':redeemScript};
} }
/* new time locked address, provide the pubkey and time necessary to unlock the funds.
when time is greater than 500000000, it should be a unix timestamp (seconds since epoch),
otherwise it should be the block height required before this transaction can be released.
may throw a string on failure!
*/
coinjs.simpleHodlAddress = function(pubkey, checklocktimeverify) {
if(checklocktimeverify < 0) {
throw "Parameter for OP_CHECKLOCKTIMEVERIFY is negative.";
}
var s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(checklocktimeverify.toString(16)).reverse());
s.writeOp(177);//OP_CHECKLOCKTIMEVERIFY
s.writeOp(117);//OP_DROP
s.writeBytes(Crypto.util.hexToBytes(pubkey));
s.writeOp(172);//OP_CHECKSIG
var x = ripemd160(Crypto.SHA256(s.buffer, {asBytes: true}), {asBytes: true});
x.unshift(coinjs.multisig);
var r = x;
r = Crypto.SHA256(Crypto.SHA256(r, {asBytes: true}), {asBytes: true});
var checksum = r.slice(0,4);
var redeemScript = Crypto.util.bytesToHex(s.buffer);
var address = coinjs.base58encode(x.concat(checksum));
return {'address':address, 'redeemScript':redeemScript};
}
/* provide a privkey and return an WIF */ /* provide a privkey and return an WIF */
coinjs.privkey2wif = function(h){ coinjs.privkey2wif = function(h){
var r = Crypto.util.hexToBytes(h); var r = Crypto.util.hexToBytes(h);
@ -649,6 +679,14 @@
r.pubkeys = pubkeys; r.pubkeys = pubkeys;
var multi = coinjs.pubkeys2MultisigAddress(pubkeys, r.signaturesRequired); var multi = coinjs.pubkeys2MultisigAddress(pubkeys, r.signaturesRequired);
r.address = multi['address']; r.address = multi['address'];
r.type = 'multisig__'; // using __ for now to differentiat from the other object .type == "multisig"
} else if(s.chunks.length == 5 && s.chunks[1] == 177 && s.chunks[2] == 117 && s.chunks[4] == 172){
// ^ <unlocktime> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG ^
r = {}
r.pubkey = Crypto.util.bytesToHex(s.chunks[3]);
r.checklocktimeverify = parseInt(Crypto.util.bytesToHex(s.chunks[0].slice().reverse()), 16);
r.address = coinjs.simpleHodlAddress(r.pubkey, r.checklocktimeverify).address;
r.type = "hodl__";
} }
} catch(e) { } catch(e) {
// console.log(e); // console.log(e);
@ -898,9 +936,15 @@
if((this.ins[index].script.chunks.length==5) && this.ins[index].script.chunks[4]==172 && coinjs.isArray(this.ins[index].script.chunks[2])){ //OP_CHECKSIG if((this.ins[index].script.chunks.length==5) && this.ins[index].script.chunks[4]==172 && coinjs.isArray(this.ins[index].script.chunks[2])){ //OP_CHECKSIG
// regular scriptPubkey (not signed) // regular scriptPubkey (not signed)
return {'type':'scriptpubkey', 'signed':'false', 'signatures':0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)}; return {'type':'scriptpubkey', 'signed':'false', 'signatures':0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
} else if((this.ins[index].script.chunks.length==2) && this.ins[index].script.chunks[0][0]==48 && this.ins[index].script.chunks[1].length == 5 && this.ins[index].script.chunks[1][1]==177){//OP_CHECKLOCKTIMEVERIFY
// hodl script (signed)
return {'type':'hodl', 'signed':'true', 'signatures':1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
} else if((this.ins[index].script.chunks.length==2) && this.ins[index].script.chunks[0][0]==48){ } else if((this.ins[index].script.chunks.length==2) && this.ins[index].script.chunks[0][0]==48){
// regular scriptPubkey (probably signed) // regular scriptPubkey (probably signed)
return {'type':'scriptpubkey', 'signed':'true', 'signatures':1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)}; return {'type':'scriptpubkey', 'signed':'true', 'signatures':1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
} else if(this.ins[index].script.chunks.length == 5 && this.ins[index].script.chunks[1] == 177){//OP_CHECKLOCKTIMEVERIFY
// hodl script (not signed)
return {'type':'hodl', 'signed':'false', 'signatures': 0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
} else if (this.ins[index].script.chunks[0]==0 && this.ins[index].script.chunks[this.ins[index].script.chunks.length-1][this.ins[index].script.chunks[this.ins[index].script.chunks.length-1].length-1]==174) { // OP_CHECKMULTISIG } else if (this.ins[index].script.chunks[0]==0 && this.ins[index].script.chunks[this.ins[index].script.chunks.length-1][this.ins[index].script.chunks[this.ins[index].script.chunks.length-1].length-1]==174) { // OP_CHECKMULTISIG
// multisig script, with signature(s) included // multisig script, with signature(s) included
return {'type':'multisig', 'signed':'true', 'signatures':this.ins[index].script.chunks.length-2, 'script': Crypto.util.bytesToHex(this.ins[index].script.chunks[this.ins[index].script.chunks.length-1])}; return {'type':'multisig', 'signed':'true', 'signatures':this.ins[index].script.chunks.length-2, 'script': Crypto.util.bytesToHex(this.ins[index].script.chunks[this.ins[index].script.chunks.length-1])};
@ -1048,6 +1092,17 @@
this.ins[index].script = s; this.ins[index].script = s;
return true; return true;
} }
/* signs a time locked / hodl input */
r.signhodl = function(index, wif){
var signature = this.transactionSig(index, wif);
var redeemScript = this.ins[index].script.buffer
var s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(signature));
s.writeBytes(redeemScript);
this.ins[index].script = s;
return true;
}
/* sign a multisig input */ /* sign a multisig input */
r.signmultisig = function(index, wif){ r.signmultisig = function(index, wif){
@ -1111,6 +1166,8 @@
if(((d['type'] == 'scriptpubkey' && d['script']==Crypto.util.bytesToHex(pubkeyHash.buffer)) || d['type'] == 'empty') && d['signed'] == "false"){ if(((d['type'] == 'scriptpubkey' && d['script']==Crypto.util.bytesToHex(pubkeyHash.buffer)) || d['type'] == 'empty') && d['signed'] == "false"){
this.signinput(i, wif); this.signinput(i, wif);
} else if (d['type'] == 'hodl' && d['signed'] == "false") {
this.signhodl(i, wif);
} else if (d['type'] == 'multisig') { } else if (d['type'] == 'multisig') {
this.signmultisig(i, wif); this.signmultisig(i, wif);
} else { } else {

View file

@ -360,6 +360,43 @@ $(document).ready(function() {
} }
}); });
/* new -> time locked code */
$('#timeLockedDateTimePicker').datetimepicker({
format: "MM/DD/YYYY HH:mm",
});
$("#newTimeLockedAddress").click(function(){
$("#timeLockedData").removeClass('show').addClass('hidden').fadeOut();
$("#timeLockedPubKey").parent().removeClass('has-error');
$("#timeLockedDateTimePicker").parent().removeClass('has-error');
$("#timeLockedErrorMsg").hide();
if(!coinjs.pubkeydecompress($("#timeLockedPubKey").val())) {
$('#timeLockedPubKey').parent().addClass('has-error');
}
var date = $('#timeLockedDateTimePicker').data("DateTimePicker").date();
if(!date || !date.isValid()) {
$('#timeLockedDateTimePicker').parent().addClass('has-error');
}
if(($("#timeLockedPubKey").parent().hasClass('has-error')==false) && $("#timeLockedDateTimePicker").parent().hasClass('has-error')==false){
try {
var hodl = coinjs.simpleHodlAddress($("#timeLockedPubKey").val(), date.unix());
$("#timeLockedData .address").val(hodl['address']);
$("#timeLockedData .script").val(hodl['redeemScript']);
$("#timeLockedData .scriptUrl").val(document.location.origin+''+document.location.pathname+'?verify='+hodl['redeemScript']+'#verify');
$("#timeLockedData").removeClass('hidden').addClass('show').fadeIn();
} catch(e) {
$("#timeLockedErrorMsg").html('<span class="glyphicon glyphicon-exclamation-sign"></span> ' + e).fadeIn();
}
} else {
$("#timeLockedErrorMsg").html('<span class="glyphicon glyphicon-exclamation-sign"></span> Public key and/or date is invalid!').fadeIn();
}
});
/* new -> Hd address code */ /* new -> Hd address code */
$(".deriveHDbtn").click(function(){ $(".deriveHDbtn").click(function(){
@ -594,6 +631,15 @@ $(document).ready(function() {
} else { } else {
listUnspentDefault(redeem); listUnspentDefault(redeem);
} }
if($("#redeemFromStatus").hasClass("hidden")) {
// An ethical dilemma: Should we automatically set nLockTime?
if(redeem.from == 'redeemScript' && redeem.decodedRs.type == "hodl__") {
$("#nLockTime").val(redeem.decodedRs.checklocktimeverify);
} else {
$("#nLockTime").val(0);
}
}
}); });
/* function to determine what we are redeeming from */ /* function to determine what we are redeeming from */
@ -620,7 +666,7 @@ $(document).ready(function() {
r.addr = decodeRs['address']; r.addr = decodeRs['address'];
r.from = 'redeemScript'; r.from = 'redeemScript';
r.decodedRs = decodeRs; r.decodedRs = decodeRs;
r.isMultisig = true; r.isMultisig = true; // not quite, may be hodl
} else { // something else } else { // something else
r.addr = ''; r.addr = '';
r.from = 'other'; r.from = 'other';
@ -941,20 +987,34 @@ $(document).ready(function() {
var script = coinjs.script(); var script = coinjs.script();
var decode = script.decodeRedeemScript($("#verifyScript").val()); var decode = script.decodeRedeemScript($("#verifyScript").val());
if(decode){ if(decode){
$("#verifyRsData .multisigAddress").val(decode['address']); $("#verifyRsDataMultisig").addClass('hidden');
$("#verifyRsData .signaturesRequired").html(decode['signaturesRequired']); $("#verifyRsDataHodl").addClass('hidden');
$("#verifyRsData table tbody").html("");
for(var i=0;i<decode.pubkeys.length;i++){ if(decode.type == "multisig__") {
var pubkey = decode.pubkeys[i]; $("#verifyRsDataMultisig .multisigAddress").val(decode['address']);
var address = coinjs.pubkey2address(pubkey); $("#verifyRsDataMultisig .signaturesRequired").html(decode['signaturesRequired']);
$('<tr><td width="30%"><input type="text" class="form-control" value="'+address+'" readonly></td><td><input type="text" class="form-control" value="'+pubkey+'" readonly></td></tr>').appendTo("#verifyRsData table tbody"); $("#verifyRsDataMultisig table tbody").html("");
for(var i=0;i<decode.pubkeys.length;i++){
var pubkey = decode.pubkeys[i];
var address = coinjs.pubkey2address(pubkey);
$('<tr><td width="30%"><input type="text" class="form-control" value="'+address+'" readonly></td><td><input type="text" class="form-control" value="'+pubkey+'" readonly></td></tr>').appendTo("#verifyRsDataMultisig table tbody");
}
$("#verifyRsData").removeClass("hidden");
$("#verifyRsDataMultisig").removeClass('hidden');
$(".verifyLink").attr('href','?verify='+$("#verifyScript").val());
return true;
} else if(decode.type == "hodl__") {
var d = $("#verifyRsDataHodl .date").data("DateTimePicker");
$("#verifyRsDataHodl .address").val(decode['address']);
$("#verifyRsDataHodl .pubkey").val(coinjs.pubkey2address(decode['pubkey']));
$("#verifyRsDataHodl .date").val(decode['checklocktimeverify'] >= 500000000? moment.unix(decode['checklocktimeverify']).format("MM/DD/YYYY HH:mm") : decode['checklocktimeverify']);
$("#verifyRsData").removeClass("hidden");
$("#verifyRsDataHodl").removeClass('hidden');
$(".verifyLink").attr('href','?verify='+$("#verifyScript").val());
return true;
} }
$("#verifyRsData").removeClass("hidden");
$(".verifyLink").attr('href','?verify='+$("#verifyScript").val());
return true;
} else {
return false;
} }
return false;
} }
function decodeTransactionScript(){ function decodeTransactionScript(){

211
js/collapse.js Normal file
View file

@ -0,0 +1,211 @@
/* ========================================================================
* Bootstrap: collapse.js v3.3.4
* http://getbootstrap.com/javascript/#collapse
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// COLLAPSE PUBLIC CLASS DEFINITION
// ================================
var Collapse = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, Collapse.DEFAULTS, options)
this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
'[data-toggle="collapse"][data-target="#' + element.id + '"]')
this.transitioning = null
if (this.options.parent) {
this.$parent = this.getParent()
} else {
this.addAriaAndCollapsedClass(this.$element, this.$trigger)
}
if (this.options.toggle) this.toggle()
}
Collapse.VERSION = '3.3.4'
Collapse.TRANSITION_DURATION = 350
Collapse.DEFAULTS = {
toggle: true
}
Collapse.prototype.dimension = function () {
var hasWidth = this.$element.hasClass('width')
return hasWidth ? 'width' : 'height'
}
Collapse.prototype.show = function () {
if (this.transitioning || this.$element.hasClass('in')) return
var activesData
var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
if (actives && actives.length) {
activesData = actives.data('bs.collapse')
if (activesData && activesData.transitioning) return
}
var startEvent = $.Event('show.bs.collapse')
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
if (actives && actives.length) {
Plugin.call(actives, 'hide')
activesData || actives.data('bs.collapse', null)
}
var dimension = this.dimension()
this.$element
.removeClass('collapse')
.addClass('collapsing')[dimension](0)
.attr('aria-expanded', true)
this.$trigger
.removeClass('collapsed')
.attr('aria-expanded', true)
this.transitioning = 1
var complete = function () {
this.$element
.removeClass('collapsing')
.addClass('collapse in')[dimension]('')
this.transitioning = 0
this.$element
.trigger('shown.bs.collapse')
}
if (!$.support.transition) return complete.call(this)
var scrollSize = $.camelCase(['scroll', dimension].join('-'))
this.$element
.one('bsTransitionEnd', $.proxy(complete, this))
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
}
Collapse.prototype.hide = function () {
if (this.transitioning || !this.$element.hasClass('in')) return
var startEvent = $.Event('hide.bs.collapse')
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
var dimension = this.dimension()
this.$element[dimension](this.$element[dimension]())[0].offsetHeight
this.$element
.addClass('collapsing')
.removeClass('collapse in')
.attr('aria-expanded', false)
this.$trigger
.addClass('collapsed')
.attr('aria-expanded', false)
this.transitioning = 1
var complete = function () {
this.transitioning = 0
this.$element
.removeClass('collapsing')
.addClass('collapse')
.trigger('hidden.bs.collapse')
}
if (!$.support.transition) return complete.call(this)
this.$element
[dimension](0)
.one('bsTransitionEnd', $.proxy(complete, this))
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)
}
Collapse.prototype.toggle = function () {
this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
Collapse.prototype.getParent = function () {
return $(this.options.parent)
.find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
.each($.proxy(function (i, element) {
var $element = $(element)
this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
}, this))
.end()
}
Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
var isOpen = $element.hasClass('in')
$element.attr('aria-expanded', isOpen)
$trigger
.toggleClass('collapsed', !isOpen)
.attr('aria-expanded', isOpen)
}
function getTargetFromTrigger($trigger) {
var href
var target = $trigger.attr('data-target')
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
return $(target)
}
// COLLAPSE PLUGIN DEFINITION
// ==========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.collapse')
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.collapse
$.fn.collapse = Plugin
$.fn.collapse.Constructor = Collapse
// COLLAPSE NO CONFLICT
// ====================
$.fn.collapse.noConflict = function () {
$.fn.collapse = old
return this
}
// COLLAPSE DATA-API
// =================
$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
var $this = $(this)
if (!$this.attr('data-target')) e.preventDefault()
var $target = getTargetFromTrigger($this)
var data = $target.data('bs.collapse')
var option = data ? 'toggle' : $this.data()
Plugin.call($target, option)
})
}(jQuery);

7
js/moment.min.js vendored Normal file

File diff suppressed because one or more lines are too long

59
js/transition.js Normal file
View file

@ -0,0 +1,59 @@
/* ========================================================================
* Bootstrap: transition.js v3.3.4
* http://getbootstrap.com/javascript/#transitions
* ========================================================================
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
+function ($) {
'use strict';
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
// ============================================================
function transitionEnd() {
var el = document.createElement('bootstrap')
var transEndEventNames = {
WebkitTransition : 'webkitTransitionEnd',
MozTransition : 'transitionend',
OTransition : 'oTransitionEnd otransitionend',
transition : 'transitionend'
}
for (var name in transEndEventNames) {
if (el.style[name] !== undefined) {
return { end: transEndEventNames[name] }
}
}
return false // explicit for ie8 ( ._.)
}
// http://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function (duration) {
var called = false
var $el = this
$(this).one('bsTransitionEnd', function () { called = true })
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
setTimeout(callback, duration)
return this
}
$(function () {
$.support.transition = transitionEnd()
if (!$.support.transition) return
$.event.special.bsTransitionEnd = {
bindType: $.support.transition.end,
delegateType: $.support.transition.end,
handle: function (e) {
if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
}
}
})
}(jQuery);