Simple Hodl Address
This commit is contained in:
parent
9a669885b3
commit
c8577e03be
8 changed files with 542 additions and 30 deletions
5
css/bootstrap-datetimepicker.min.css
vendored
Normal file
5
css/bootstrap-datetimepicker.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
149
index.html
149
index.html
|
@ -12,10 +12,15 @@
|
|||
<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-datetimepicker.min.css">
|
||||
<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/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-datetimepicker.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="js/crypto-min.js"></script>
|
||||
<script type="text/javascript" src="js/crypto-sha256.js"></script>
|
||||
|
@ -54,6 +59,7 @@
|
|||
<ul class="dropdown-menu">
|
||||
<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="#newTimeLocked" data-toggle="tab">New Time Locked Address</a></li>
|
||||
<li><a href="#newHDaddress" data-toggle="tab">HD Address</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="#newTransaction" data-toggle="tab">Transaction</a></li>
|
||||
|
@ -377,6 +383,73 @@
|
|||
<br>
|
||||
</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">
|
||||
<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>
|
||||
|
@ -585,24 +658,64 @@
|
|||
<div class="hidden verifyData" id="verifyRsData">
|
||||
<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>
|
||||
<label>Multi Signature Address</label>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control address multisigAddress" 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 Signatures</label>
|
||||
<p class="signaturesRequired">?</p>
|
||||
<label>Signatures Required from</label>
|
||||
<table class="table table-striped table-hover">
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="hidden" id="verifyRsDataMultisig">
|
||||
<label>Multi Signature Address</label>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control address multisigAddress" 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 Signatures</label>
|
||||
<p class="signaturesRequired">?</p>
|
||||
<label>Signatures Required from</label>
|
||||
<table class="table table-striped table-hover">
|
||||
<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 class="hidden verifyData" id="verifyTransactionData">
|
||||
|
|
9
js/bootstrap-datetimepicker.min.js
vendored
Normal file
9
js/bootstrap-datetimepicker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
57
js/coin.js
57
js/coin.js
|
@ -135,6 +135,36 @@
|
|||
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, locktime) {
|
||||
|
||||
if(locktime < 0) {
|
||||
throw "Locktime is negative.";
|
||||
}
|
||||
|
||||
var s = coinjs.script();
|
||||
s.writeBytes(Crypto.util.hexToBytes(locktime.toString(16)));
|
||||
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 */
|
||||
coinjs.privkey2wif = function(h){
|
||||
var r = Crypto.util.hexToBytes(h);
|
||||
|
@ -649,6 +679,14 @@
|
|||
r.pubkeys = pubkeys;
|
||||
var multi = coinjs.pubkeys2MultisigAddress(pubkeys, r.signaturesRequired);
|
||||
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){
|
||||
// ^ <locktime> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG ^
|
||||
r = {}
|
||||
r.pubkey = Crypto.util.bytesToHex(s.chunks[3]);
|
||||
r.locktime = parseInt(Crypto.util.bytesToHex(s.chunks[0]), 16); // TODO is this endian safe?
|
||||
r.address = coinjs.simpleHodlAddress(r.pubkey, r.locktime).address;
|
||||
r.type = "hodl__";
|
||||
}
|
||||
} catch(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
|
||||
// regular scriptPubkey (not signed)
|
||||
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){
|
||||
// regular scriptPubkey (probably signed)
|
||||
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
|
||||
// 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])};
|
||||
|
@ -1048,6 +1092,17 @@
|
|||
this.ins[index].script = s;
|
||||
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 */
|
||||
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"){
|
||||
this.signinput(i, wif);
|
||||
} else if (d['type'] == 'hodl' && d['signed'] == "false") {
|
||||
this.signhodl(i, wif);
|
||||
} else if (d['type'] == 'multisig') {
|
||||
this.signmultisig(i, wif);
|
||||
} else {
|
||||
|
|
|
@ -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 */
|
||||
|
||||
$(".deriveHDbtn").click(function(){
|
||||
|
@ -941,20 +978,34 @@ $(document).ready(function() {
|
|||
var script = coinjs.script();
|
||||
var decode = script.decodeRedeemScript($("#verifyScript").val());
|
||||
if(decode){
|
||||
$("#verifyRsData .multisigAddress").val(decode['address']);
|
||||
$("#verifyRsData .signaturesRequired").html(decode['signaturesRequired']);
|
||||
$("#verifyRsData 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("#verifyRsData table tbody");
|
||||
$("#verifyRsDataMultisig").addClass('hidden');
|
||||
$("#verifyRsDataHodl").addClass('hidden');
|
||||
|
||||
if(decode.type == "multisig__") {
|
||||
$("#verifyRsDataMultisig .multisigAddress").val(decode['address']);
|
||||
$("#verifyRsDataMultisig .signaturesRequired").html(decode['signaturesRequired']);
|
||||
$("#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['locktime'] >= 500000000? moment.unix(decode['locktime']).format("MM/DD/YYYY HH:mm") : decode['locktime']);
|
||||
$("#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(){
|
||||
|
|
211
js/collapse.js
Normal file
211
js/collapse.js
Normal 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
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
59
js/transition.js
Normal 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);
|
Loading…
Reference in a new issue