1
1
Fork 0

bug #96 move libraries to library - better alignment of like rotator

This commit is contained in:
Friendika 2011-06-28 21:11:52 -07:00
commit 0b221e8945
397 changed files with 30 additions and 30 deletions

182
library/cropper/cropper.css Normal file
View file

@ -0,0 +1,182 @@
.imgCrop_wrap {
/* width: 500px; @done_in_js */
/* height: 375px; @done_in_js */
position: relative;
cursor: crosshair;
}
/* an extra classname is applied for Opera < 9.0 to fix it's lack of opacity support */
.imgCrop_wrap.opera8 .imgCrop_overlay,
.imgCrop_wrap.opera8 .imgCrop_clickArea {
background-color: transparent;
}
/* fix for IE displaying all boxes at line-height by default, although they are still 1 pixel high until we combine them with the pointless span */
.imgCrop_wrap,
.imgCrop_wrap * {
font-size: 0;
}
.imgCrop_overlay {
background-color: #000;
opacity: 0.5;
filter:alpha(opacity=50);
position: absolute;
width: 100%;
height: 100%;
}
.imgCrop_selArea {
position: absolute;
/* @done_in_js
top: 20px;
left: 20px;
width: 200px;
height: 200px;
background: transparent url(castle.jpg) no-repeat -210px -110px;
*/
cursor: move;
z-index: 2;
}
/* clickArea is all a fix for IE 5.5 & 6 to allow the user to click on the given area */
.imgCrop_clickArea {
width: 100%;
height: 100%;
background-color: #FFF;
opacity: 0.01;
filter:alpha(opacity=01);
}
.imgCrop_marqueeHoriz {
position: absolute;
width: 100%;
height: 1px;
background: transparent url(marqueeHoriz.gif) repeat-x 0 0;
z-index: 3;
}
.imgCrop_marqueeVert {
position: absolute;
height: 100%;
width: 1px;
background: transparent url(marqueeVert.gif) repeat-y 0 0;
z-index: 3;
}
/*
* FIX MARCHING ANTS IN IE
* As IE <6 tries to load background images we can uncomment the follwoing hack
* to remove that issue, not as pretty - but is anything in IE?
* And yes I do know that 'filter' is evil, but it will make it look semi decent in IE
*
* html .imgCrop_marqueeHoriz,
* html .imgCrop_marqueeVert {
background: transparent;
filter: Invert;
}
* html .imgCrop_marqueeNorth { border-top: 1px dashed #000; }
* html .imgCrop_marqueeEast { border-right: 1px dashed #000; }
* html .imgCrop_marqueeSouth { border-bottom: 1px dashed #000; }
* html .imgCrop_marqueeWest { border-left: 1px dashed #000; }
*/
.imgCrop_marqueeNorth { top: 0; left: 0; }
.imgCrop_marqueeEast { top: 0; right: 0; }
.imgCrop_marqueeSouth { bottom: 0px; left: 0; }
.imgCrop_marqueeWest { top: 0; left: 0; }
.imgCrop_handle {
position: absolute;
border: 1px solid #333;
width: 6px;
height: 6px;
background: #FFF;
opacity: 0.5;
filter:alpha(opacity=50);
z-index: 4;
}
/* fix IE 5 box model */
* html .imgCrop_handle {
width: 8px;
height: 8px;
wid\th: 6px;
hei\ght: 6px;
}
.imgCrop_handleN {
top: -3px;
left: 0;
/* margin-left: 49%; @done_in_js */
cursor: n-resize;
}
.imgCrop_handleNE {
top: -3px;
right: -3px;
cursor: ne-resize;
}
.imgCrop_handleE {
top: 0;
right: -3px;
/* margin-top: 49%; @done_in_js */
cursor: e-resize;
}
.imgCrop_handleSE {
right: -3px;
bottom: -3px;
cursor: se-resize;
}
.imgCrop_handleS {
right: 0;
bottom: -3px;
/* margin-right: 49%; @done_in_js */
cursor: s-resize;
}
.imgCrop_handleSW {
left: -3px;
bottom: -3px;
cursor: sw-resize;
}
.imgCrop_handleW {
top: 0;
left: -3px;
/* margin-top: 49%; @done_in_js */
cursor: w-resize;
}
.imgCrop_handleNW {
top: -3px;
left: -3px;
cursor: nw-resize;
}
/**
* Create an area to click & drag around on as the default browser behaviour is to let you drag the image
*/
.imgCrop_dragArea {
width: 100%;
height: 100%;
z-index: 200;
position: absolute;
top: 0;
left: 0;
}
.imgCrop_previewWrap {
/* width: 200px; @done_in_js */
/* height: 200px; @done_in_js */
overflow: hidden;
position: relative;
}
.imgCrop_previewWrap img {
position: absolute;
}

View file

@ -0,0 +1,228 @@
1.
<script type="text/javascript" src="scripts/cropper/lib/prototype.js" language="javascript"></script>
2.
<script type="text/javascript" src="scripts/cropper/lib/scriptaculous.js?load=builder,dragdrop" language="javascript"></script>
3.
<script type="text/javascript" src="scripts/cropper/cropper.js" language="javascript"></script>
Options
ratioDim obj
The pixel dimensions to apply as a restrictive ratio, with properties x & y.
minWidth int
The minimum width for the select area in pixels.
minHeight int
The mimimum height for the select area in pixels.
maxWidth int
The maximum width for the select areas in pixels (if both minWidth & maxWidth set to same the width of the cropper will be fixed)
maxHeight int
The maximum height for the select areas in pixels (if both minHeight & maxHeight set to same the height of the cropper will be fixed)
displayOnInit int
Whether to display the select area on initialisation, only used when providing minimum width & height or ratio.
onEndCrop func
The callback function to provide the crop details to on end of a crop.
captureKeys boolean
Whether to capture the keys for moving the select area, as these can cause some problems at the moment.
onloadCoords obj
A coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area to display onload
The callback function
The callback function is a function that allows you to capture the crop co-ordinates when the user finished a crop movement, it is passed two arguments:
* coords, obj, coordinates object with properties x1, y1, x2 & y2; for the coordinates of the select area.
* dimensions, obj, dimensions object with properities width & height; for the dimensions of the select area.
An example function which outputs the crop values to form fields:
Display code as plain text
JavaScript:
1.
function onEndCrop( coords, dimensions ) {
2.
$( 'x1' ).value = coords.x1;
3.
$( 'y1' ).value = coords.y1;
4.
$( 'x2' ).value = coords.x2;
5.
$( 'y2' ).value = coords.y2;
6.
$( 'width' ).value = dimensions.width;
7.
$( 'height' ).value = dimensions.height;
8.
}
Basic interface
This basic example will attach the cropper UI to the test image and return crop results to the provided callback function.
Display code as plain text
HTML:
1.
<img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
2.
3.
<script type="text/javascript" language="javascript">
4.
Event.observe( window, 'load', function() {
5.
new Cropper.Img(
6.
'testImage',
7.
{ onEndCrop: onEndCrop }
8.
);
9.
} );
10.
</script>
Minimum dimensions
You can apply minimum dimensions to a single axis or both, this example applies minimum dimensions to both axis.
Display code as plain text
HTML:
1.
<img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
2.
3.
<script type="text/javascript" language="javascript">
4.
Event.observe( window, 'load', function() {
5.
new Cropper.Img(
6.
'testImage',
7.
{
8.
minWidth: 220,
9.
minHeight: 120,
10.
onEndCrop: onEndCrop
11.
}
12.
);
13.
} );
14.
</script>
Select area ratio
You can apply a ratio to the selection area, this example applies a 4:3 ratio to the select area.
Display code as plain text
HTML:
1.
<img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
2.
3.
<script type="text/javascript" language="javascript">
4.
Event.observe( window, 'load', function() {
5.
new Cropper.Img(
6.
'testImage',
7.
{
8.
ratioDim: {
9.
x: 220,
10.
y: 165
11.
},
12.
displayOnInit: true,
13.
onEndCrop: onEndCrop
14.
}
15.
);
16.
} );
17.
</script>
With crop preview
You can display a dynamically prouced preview of the resulting crop by using the ImgWithPreview subclass, a preview can only be displayed when we have a fixed size (set via minWidth & minHeight options). Note that the displayOnInit option is not required as this is the default behaviour when displaying a crop preview.
Display code as plain text
HTML:
1.
<img src="test.jpg" alt="Test image" id="testImage" width="500" height="333" />
2.
<div id="previewWrap"></div>
3.
4.
<script type="text/javascript" language="javascript">
5.
Event.observe( window, 'load', function() {
6.
new Cropper.ImgWithPreview(
7.
'testImage',
8.
{
9.
previewWrap: 'previewWrap',
10.
minWidth: 120,
11.
minHeight: 120,
12.
ratioDim: { x: 200, y: 120 },
13.
onEndCrop: onEndCrop
14.
}
15.
);
16.
} );
17.
</script>
Known Issues
* Safari animated gifs, only one of each will animate, this seems to be a known Safari issue.
* After drawing an area and then clicking to start a new drag in IE 5.5 the rendered height appears as the last height until the user drags, this appears to be the related to another IE error (which has been fixed) where IE does not always redraw the select area properly.
* Lack of CSS opacity support in Opera before version 9 mean we disable those style rules, if Opera 8 support is important you & you want the overlay to work then you can use the Opera rules in the CSS to apply a black PNG with 50% alpha transparency to replicate the effect.
* Styling & borders on image, any CSS styling applied directly to the image itself (floats, borders, padding, margin, etc.) will cause problems with the cropper. The use of a wrapper element to apply these styles to is recommended.
* overflow: auto or overflow: scroll on parent will cause cropper to burst out of parent in IE and Opera when applied (maybe Mac browsers too) I'm not sure why yet.
If you use CakePHP you will notice that including this in your script will break the CSS layout. This is due to the CSS rule
form div{
vertical-align: text-top;
margin-left: 1em;
margin-bottom:2em;
overflow: auto;
}
A simple workaround is to add another rule directly after this like so:
form div.no_cake, form div.no_cake div {
margin:0;
overflow:hidden;
}
and then in your code surround the img tag with a div with the class name of no_cake.
Cheers

566
library/cropper/cropper.js Normal file
View file

@ -0,0 +1,566 @@
/**
* Copyright (c) 2006, David Spurr (http://www.defusion.org.uk/)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* * Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://www.opensource.org/licenses/bsd-license.php
*
* See scriptaculous.js for full scriptaculous licence
*/
var CropDraggable=Class.create();
Object.extend(Object.extend(CropDraggable.prototype,Draggable.prototype),{initialize:function(_1){
this.options=Object.extend({drawMethod:function(){
}},arguments[1]||{});
this.element=$(_1);
this.handle=this.element;
this.delta=this.currentDelta();
this.dragging=false;
this.eventMouseDown=this.initDrag.bindAsEventListener(this);
Event.observe(this.handle,"mousedown",this.eventMouseDown);
Draggables.register(this);
},draw:function(_2){
var _3=Position.cumulativeOffset(this.element);
var d=this.currentDelta();
_3[0]-=d[0];
_3[1]-=d[1];
var p=[0,1].map(function(i){
return (_2[i]-_3[i]-this.offset[i]);
}.bind(this));
this.options.drawMethod(p);
}});
var Cropper={};
Cropper.Img=Class.create();
Cropper.Img.prototype={initialize:function(_7,_8){
this.options=Object.extend({ratioDim:{x:0,y:0},minWidth:0,minHeight:0,displayOnInit:false,onEndCrop:Prototype.emptyFunction,captureKeys:true,onloadCoords:null,maxWidth:0,maxHeight:0},_8||{});
this.img=$(_7);
this.clickCoords={x:0,y:0};
this.dragging=false;
this.resizing=false;
this.isWebKit=/Konqueror|Safari|KHTML/.test(navigator.userAgent);
this.isIE=/MSIE/.test(navigator.userAgent);
this.isOpera8=/Opera\s[1-8]/.test(navigator.userAgent);
this.ratioX=0;
this.ratioY=0;
this.attached=false;
this.fixedWidth=(this.options.maxWidth>0&&(this.options.minWidth>=this.options.maxWidth));
this.fixedHeight=(this.options.maxHeight>0&&(this.options.minHeight>=this.options.maxHeight));
if(typeof this.img=="undefined"){
return;
}
$A(document.getElementsByTagName("script")).each(function(s){
if(s.src.match(/cropper\.js/)){
var _a=s.src.replace(/cropper\.js(.*)?/,"");
var _b=document.createElement("link");
_b.rel="stylesheet";
_b.type="text/css";
_b.href=_a+"cropper.css";
_b.media="screen";
document.getElementsByTagName("head")[0].appendChild(_b);
}
});
if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){
var _c=this.getGCD(this.options.ratioDim.x,this.options.ratioDim.y);
this.ratioX=this.options.ratioDim.x/_c;
this.ratioY=this.options.ratioDim.y/_c;
}
this.subInitialize();
if(this.img.complete||this.isWebKit){
this.onLoad();
}else{
Event.observe(this.img,"load",this.onLoad.bindAsEventListener(this));
}
},getGCD:function(a,b){
if(b==0){
return a;
}
return this.getGCD(b,a%b);
},onLoad:function(){
var _f="imgCrop_";
var _10=this.img.parentNode;
var _11="";
if(this.isOpera8){
_11=" opera8";
}
this.imgWrap=Builder.node("div",{"class":_f+"wrap"+_11});
this.north=Builder.node("div",{"class":_f+"overlay "+_f+"north"},[Builder.node("span")]);
this.east=Builder.node("div",{"class":_f+"overlay "+_f+"east"},[Builder.node("span")]);
this.south=Builder.node("div",{"class":_f+"overlay "+_f+"south"},[Builder.node("span")]);
this.west=Builder.node("div",{"class":_f+"overlay "+_f+"west"},[Builder.node("span")]);
var _12=[this.north,this.east,this.south,this.west];
this.dragArea=Builder.node("div",{"class":_f+"dragArea"},_12);
this.handleN=Builder.node("div",{"class":_f+"handle "+_f+"handleN"});
this.handleNE=Builder.node("div",{"class":_f+"handle "+_f+"handleNE"});
this.handleE=Builder.node("div",{"class":_f+"handle "+_f+"handleE"});
this.handleSE=Builder.node("div",{"class":_f+"handle "+_f+"handleSE"});
this.handleS=Builder.node("div",{"class":_f+"handle "+_f+"handleS"});
this.handleSW=Builder.node("div",{"class":_f+"handle "+_f+"handleSW"});
this.handleW=Builder.node("div",{"class":_f+"handle "+_f+"handleW"});
this.handleNW=Builder.node("div",{"class":_f+"handle "+_f+"handleNW"});
this.selArea=Builder.node("div",{"class":_f+"selArea"},[Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeNorth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeEast"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeHoriz "+_f+"marqueeSouth"},[Builder.node("span")]),Builder.node("div",{"class":_f+"marqueeVert "+_f+"marqueeWest"},[Builder.node("span")]),this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW,Builder.node("div",{"class":_f+"clickArea"})]);
this.imgWrap.appendChild(this.img);
this.imgWrap.appendChild(this.dragArea);
this.dragArea.appendChild(this.selArea);
this.dragArea.appendChild(Builder.node("div",{"class":_f+"clickArea"}));
_10.appendChild(this.imgWrap);
this.startDragBind=this.startDrag.bindAsEventListener(this);
Event.observe(this.dragArea,"mousedown",this.startDragBind);
this.onDragBind=this.onDrag.bindAsEventListener(this);
Event.observe(document,"mousemove",this.onDragBind);
this.endCropBind=this.endCrop.bindAsEventListener(this);
Event.observe(document,"mouseup",this.endCropBind);
this.resizeBind=this.startResize.bindAsEventListener(this);
this.handles=[this.handleN,this.handleNE,this.handleE,this.handleSE,this.handleS,this.handleSW,this.handleW,this.handleNW];
this.registerHandles(true);
if(this.options.captureKeys){
this.keysBind=this.handleKeys.bindAsEventListener(this);
Event.observe(document,"keypress",this.keysBind);
}
new CropDraggable(this.selArea,{drawMethod:this.moveArea.bindAsEventListener(this)});
this.setParams();
},registerHandles:function(_13){
for(var i=0;i<this.handles.length;i++){
var _15=$(this.handles[i]);
if(_13){
var _16=false;
if(this.fixedWidth&&this.fixedHeight){
_16=true;
}else{
if(this.fixedWidth||this.fixedHeight){
var _17=_15.className.match(/([S|N][E|W])$/);
var _18=_15.className.match(/(E|W)$/);
var _19=_15.className.match(/(N|S)$/);
if(_17){
_16=true;
}else{
if(this.fixedWidth&&_18){
_16=true;
}else{
if(this.fixedHeight&&_19){
_16=true;
}
}
}
}
}
if(_16){
_15.hide();
}else{
Event.observe(_15,"mousedown",this.resizeBind);
}
}else{
_15.show();
Event.stopObserving(_15,"mousedown",this.resizeBind);
}
}
},setParams:function(){
this.imgW=this.img.width;
this.imgH=this.img.height;
$(this.north).setStyle({height:0});
$(this.east).setStyle({width:0,height:0});
$(this.south).setStyle({height:0});
$(this.west).setStyle({width:0,height:0});
$(this.imgWrap).setStyle({"width":this.imgW+"px","height":this.imgH+"px"});
$(this.selArea).hide();
var _1a={x1:0,y1:0,x2:0,y2:0};
var _1b=false;
if(this.options.onloadCoords!=null){
_1a=this.cloneCoords(this.options.onloadCoords);
_1b=true;
}else{
if(this.options.ratioDim.x>0&&this.options.ratioDim.y>0){
_1a.x1=Math.ceil((this.imgW-this.options.ratioDim.x)/2);
_1a.y1=Math.ceil((this.imgH-this.options.ratioDim.y)/2);
_1a.x2=_1a.x1+this.options.ratioDim.x;
_1a.y2=_1a.y1+this.options.ratioDim.y;
_1b=true;
}
}
this.setAreaCoords(_1a,false,false,1);
if(this.options.displayOnInit&&_1b){
this.selArea.show();
this.drawArea();
this.endCrop();
}
this.attached=true;
},remove:function(){
if(this.attached){
this.attached=false;
this.imgWrap.parentNode.insertBefore(this.img,this.imgWrap);
this.imgWrap.parentNode.removeChild(this.imgWrap);
Event.stopObserving(this.dragArea,"mousedown",this.startDragBind);
Event.stopObserving(document,"mousemove",this.onDragBind);
Event.stopObserving(document,"mouseup",this.endCropBind);
this.registerHandles(false);
if(this.options.captureKeys){
Event.stopObserving(document,"keypress",this.keysBind);
}
}
},reset:function(){
if(!this.attached){
this.onLoad();
}else{
this.setParams();
}
this.endCrop();
},handleKeys:function(e){
var dir={x:0,y:0};
if(!this.dragging){
switch(e.keyCode){
case (37):
dir.x=-1;
break;
case (38):
dir.y=-1;
break;
case (39):
dir.x=1;
break;
case (40):
dir.y=1;
break;
}
if(dir.x!=0||dir.y!=0){
if(e.shiftKey){
dir.x*=10;
dir.y*=10;
}
this.moveArea([this.areaCoords.x1+dir.x,this.areaCoords.y1+dir.y]);
Event.stop(e);
}
}
},calcW:function(){
return (this.areaCoords.x2-this.areaCoords.x1);
},calcH:function(){
return (this.areaCoords.y2-this.areaCoords.y1);
},moveArea:function(_1e){
this.setAreaCoords({x1:_1e[0],y1:_1e[1],x2:_1e[0]+this.calcW(),y2:_1e[1]+this.calcH()},true,false);
this.drawArea();
},cloneCoords:function(_1f){
return {x1:_1f.x1,y1:_1f.y1,x2:_1f.x2,y2:_1f.y2};
},setAreaCoords:function(_20,_21,_22,_23,_24){
if(_21){
var _25=_20.x2-_20.x1;
var _26=_20.y2-_20.y1;
if(_20.x1<0){
_20.x1=0;
_20.x2=_25;
}
if(_20.y1<0){
_20.y1=0;
_20.y2=_26;
}
if(_20.x2>this.imgW){
_20.x2=this.imgW;
_20.x1=this.imgW-_25;
}
if(_20.y2>this.imgH){
_20.y2=this.imgH;
_20.y1=this.imgH-_26;
}
}else{
if(_20.x1<0){
_20.x1=0;
}
if(_20.y1<0){
_20.y1=0;
}
if(_20.x2>this.imgW){
_20.x2=this.imgW;
}
if(_20.y2>this.imgH){
_20.y2=this.imgH;
}
if(_23!=null){
if(this.ratioX>0){
this.applyRatio(_20,{x:this.ratioX,y:this.ratioY},_23,_24);
}else{
if(_22){
this.applyRatio(_20,{x:1,y:1},_23,_24);
}
}
var _27=[this.options.minWidth,this.options.minHeight];
var _28=[this.options.maxWidth,this.options.maxHeight];
if(_27[0]>0||_27[1]>0||_28[0]>0||_28[1]>0){
var _29={a1:_20.x1,a2:_20.x2};
var _2a={a1:_20.y1,a2:_20.y2};
var _2b={min:0,max:this.imgW};
var _2c={min:0,max:this.imgH};
if((_27[0]!=0||_27[1]!=0)&&_22){
if(_27[0]>0){
_27[1]=_27[0];
}else{
if(_27[1]>0){
_27[0]=_27[1];
}
}
}
if((_28[0]!=0||_28[0]!=0)&&_22){
if(_28[0]>0&&_28[0]<=_28[1]){
_28[1]=_28[0];
}else{
if(_28[1]>0&&_28[1]<=_28[0]){
_28[0]=_28[1];
}
}
}
if(_27[0]>0){
this.applyDimRestriction(_29,_27[0],_23.x,_2b,"min");
}
if(_27[1]>1){
this.applyDimRestriction(_2a,_27[1],_23.y,_2c,"min");
}
if(_28[0]>0){
this.applyDimRestriction(_29,_28[0],_23.x,_2b,"max");
}
if(_28[1]>1){
this.applyDimRestriction(_2a,_28[1],_23.y,_2c,"max");
}
_20={x1:_29.a1,y1:_2a.a1,x2:_29.a2,y2:_2a.a2};
}
}
}
this.areaCoords=_20;
},applyDimRestriction:function(_2d,val,_2f,_30,_31){
var _32;
if(_31=="min"){
_32=((_2d.a2-_2d.a1)<val);
}else{
_32=((_2d.a2-_2d.a1)>val);
}
if(_32){
if(_2f==1){
_2d.a2=_2d.a1+val;
}else{
_2d.a1=_2d.a2-val;
}
if(_2d.a1<_30.min){
_2d.a1=_30.min;
_2d.a2=val;
}else{
if(_2d.a2>_30.max){
_2d.a1=_30.max-val;
_2d.a2=_30.max;
}
}
}
},applyRatio:function(_33,_34,_35,_36){
var _37;
if(_36=="N"||_36=="S"){
_37=this.applyRatioToAxis({a1:_33.y1,b1:_33.x1,a2:_33.y2,b2:_33.x2},{a:_34.y,b:_34.x},{a:_35.y,b:_35.x},{min:0,max:this.imgW});
_33.x1=_37.b1;
_33.y1=_37.a1;
_33.x2=_37.b2;
_33.y2=_37.a2;
}else{
_37=this.applyRatioToAxis({a1:_33.x1,b1:_33.y1,a2:_33.x2,b2:_33.y2},{a:_34.x,b:_34.y},{a:_35.x,b:_35.y},{min:0,max:this.imgH});
_33.x1=_37.a1;
_33.y1=_37.b1;
_33.x2=_37.a2;
_33.y2=_37.b2;
}
},applyRatioToAxis:function(_38,_39,_3a,_3b){
var _3c=Object.extend(_38,{});
var _3d=_3c.a2-_3c.a1;
var _3e=Math.floor(_3d*_39.b/_39.a);
var _3f;
var _40;
var _41=null;
if(_3a.b==1){
_3f=_3c.b1+_3e;
if(_3f>_3b.max){
_3f=_3b.max;
_41=_3f-_3c.b1;
}
_3c.b2=_3f;
}else{
_3f=_3c.b2-_3e;
if(_3f<_3b.min){
_3f=_3b.min;
_41=_3f+_3c.b2;
}
_3c.b1=_3f;
}
if(_41!=null){
_40=Math.floor(_41*_39.a/_39.b);
if(_3a.a==1){
_3c.a2=_3c.a1+_40;
}else{
_3c.a1=_3c.a1=_3c.a2-_40;
}
}
return _3c;
},drawArea:function(){
var _42=this.calcW();
var _43=this.calcH();
var px="px";
var _45=[this.areaCoords.x1+px,this.areaCoords.y1+px,_42+px,_43+px,this.areaCoords.x2+px,this.areaCoords.y2+px,(this.img.width-this.areaCoords.x2)+px,(this.img.height-this.areaCoords.y2)+px];
var _46=this.selArea.style;
_46.left=_45[0];
_46.top=_45[1];
_46.width=_45[2];
_46.height=_45[3];
var _47=Math.ceil((_42-6)/2)+px;
var _48=Math.ceil((_43-6)/2)+px;
this.handleN.style.left=_47;
this.handleE.style.top=_48;
this.handleS.style.left=_47;
this.handleW.style.top=_48;
this.north.style.height=_45[1];
var _49=this.east.style;
_49.top=_45[1];
_49.height=_45[3];
_49.left=_45[4];
_49.width=_45[6];
var _4a=this.south.style;
_4a.top=_45[5];
_4a.height=_45[7];
var _4b=this.west.style;
_4b.top=_45[1];
_4b.height=_45[3];
_4b.width=_45[0];
this.subDrawArea();
this.forceReRender();
},forceReRender:function(){
if(this.isIE||this.isWebKit){
var n=document.createTextNode(" ");
var d,el,fixEL,i;
if(this.isIE){
fixEl=this.selArea;
}else{
if(this.isWebKit){
fixEl=document.getElementsByClassName("imgCrop_marqueeSouth",this.imgWrap)[0];
d=Builder.node("div","");
d.style.visibility="hidden";
var _4e=["SE","S","SW"];
for(i=0;i<_4e.length;i++){
el=document.getElementsByClassName("imgCrop_handle"+_4e[i],this.selArea)[0];
if(el.childNodes.length){
el.removeChild(el.childNodes[0]);
}
el.appendChild(d);
}
}
}
fixEl.appendChild(n);
fixEl.removeChild(n);
}
},startResize:function(e){
this.startCoords=this.cloneCoords(this.areaCoords);
this.resizing=true;
this.resizeHandle=Event.element(e).classNames().toString().replace(/([^N|NE|E|SE|S|SW|W|NW])+/,"");
Event.stop(e);
},startDrag:function(e){
this.selArea.show();
this.clickCoords=this.getCurPos(e);
this.setAreaCoords({x1:this.clickCoords.x,y1:this.clickCoords.y,x2:this.clickCoords.x,y2:this.clickCoords.y},false,false,null);
this.dragging=true;
this.onDrag(e);
Event.stop(e);
},getCurPos:function(e){
var el=this.imgWrap,wrapOffsets=Position.cumulativeOffset(el);
while(el.nodeName!="BODY"){
wrapOffsets[1]-=el.scrollTop||0;
wrapOffsets[0]-=el.scrollLeft||0;
el=el.parentNode;
}
return curPos={x:Event.pointerX(e)-wrapOffsets[0],y:Event.pointerY(e)-wrapOffsets[1]};
},onDrag:function(e){
if(this.dragging||this.resizing){
var _54=null;
var _55=this.getCurPos(e);
var _56=this.cloneCoords(this.areaCoords);
var _57={x:1,y:1};
if(this.dragging){
if(_55.x<this.clickCoords.x){
_57.x=-1;
}
if(_55.y<this.clickCoords.y){
_57.y=-1;
}
this.transformCoords(_55.x,this.clickCoords.x,_56,"x");
this.transformCoords(_55.y,this.clickCoords.y,_56,"y");
}else{
if(this.resizing){
_54=this.resizeHandle;
if(_54.match(/E/)){
this.transformCoords(_55.x,this.startCoords.x1,_56,"x");
if(_55.x<this.startCoords.x1){
_57.x=-1;
}
}else{
if(_54.match(/W/)){
this.transformCoords(_55.x,this.startCoords.x2,_56,"x");
if(_55.x<this.startCoords.x2){
_57.x=-1;
}
}
}
if(_54.match(/N/)){
this.transformCoords(_55.y,this.startCoords.y2,_56,"y");
if(_55.y<this.startCoords.y2){
_57.y=-1;
}
}else{
if(_54.match(/S/)){
this.transformCoords(_55.y,this.startCoords.y1,_56,"y");
if(_55.y<this.startCoords.y1){
_57.y=-1;
}
}
}
}
}
this.setAreaCoords(_56,false,e.shiftKey,_57,_54);
this.drawArea();
Event.stop(e);
}
},transformCoords:function(_58,_59,_5a,_5b){
var _5c=[_58,_59];
if(_58>_59){
_5c.reverse();
}
_5a[_5b+"1"]=_5c[0];
_5a[_5b+"2"]=_5c[1];
},endCrop:function(){
this.dragging=false;
this.resizing=false;
this.options.onEndCrop(this.areaCoords,{width:this.calcW(),height:this.calcH()});
},subInitialize:function(){
},subDrawArea:function(){
}};
Cropper.ImgWithPreview=Class.create();
Object.extend(Object.extend(Cropper.ImgWithPreview.prototype,Cropper.Img.prototype),{subInitialize:function(){
this.hasPreviewImg=false;
if(typeof (this.options.previewWrap)!="undefined"&&this.options.minWidth>0&&this.options.minHeight>0){
this.previewWrap=$(this.options.previewWrap);
this.previewImg=this.img.cloneNode(false);
this.previewImg.id="imgCrop_"+this.previewImg.id;
this.options.displayOnInit=true;
this.hasPreviewImg=true;
this.previewWrap.addClassName("imgCrop_previewWrap");
this.previewWrap.setStyle({width:this.options.minWidth+"px",height:this.options.minHeight+"px"});
this.previewWrap.appendChild(this.previewImg);
}
},subDrawArea:function(){
if(this.hasPreviewImg){
var _5d=this.calcW();
var _5e=this.calcH();
var _5f={x:this.imgW/_5d,y:this.imgH/_5e};
var _60={x:_5d/this.options.minWidth,y:_5e/this.options.minHeight};
var _61={w:Math.ceil(this.options.minWidth*_5f.x)+"px",h:Math.ceil(this.options.minHeight*_5f.y)+"px",x:"-"+Math.ceil(this.areaCoords.x1/_60.x)+"px",y:"-"+Math.ceil(this.areaCoords.y1/_60.y)+"px"};
var _62=this.previewImg.style;
_62.width=_61.w;
_62.height=_61.h;
_62.left=_61.x;
_62.top=_61.y;
}
}});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,101 @@
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// See scriptaculous.js for full license.
var Builder = {
NODEMAP: {
AREA: 'map',
CAPTION: 'table',
COL: 'table',
COLGROUP: 'table',
LEGEND: 'fieldset',
OPTGROUP: 'select',
OPTION: 'select',
PARAM: 'object',
TBODY: 'table',
TD: 'table',
TFOOT: 'table',
TH: 'table',
THEAD: 'table',
TR: 'table'
},
// note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
// due to a Firefox bug
node: function(elementName) {
elementName = elementName.toUpperCase();
// try innerHTML approach
var parentTag = this.NODEMAP[elementName] || 'div';
var parentElement = document.createElement(parentTag);
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
} catch(e) {}
var element = parentElement.firstChild || null;
// see if browser added wrapping tags
if(element && (element.tagName != elementName))
element = element.getElementsByTagName(elementName)[0];
// fallback to createElement approach
if(!element) element = document.createElement(elementName);
// abort if nothing could be created
if(!element) return;
// attributes (or text)
if(arguments[1])
if(this._isStringOrNumber(arguments[1]) ||
(arguments[1] instanceof Array)) {
this._children(element, arguments[1]);
} else {
var attrs = this._attributes(arguments[1]);
if(attrs.length) {
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
parentElement.innerHTML = "<" +elementName + " " +
attrs + "></" + elementName + ">";
} catch(e) {}
element = parentElement.firstChild || null;
// workaround firefox 1.0.X bug
if(!element) {
element = document.createElement(elementName);
for(attr in arguments[1])
element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
}
if(element.tagName != elementName)
element = parentElement.getElementsByTagName(elementName)[0];
}
}
// text, or array of children
if(arguments[2])
this._children(element, arguments[2]);
return element;
},
_text: function(text) {
return document.createTextNode(text);
},
_attributes: function(attributes) {
var attrs = [];
for(attribute in attributes)
attrs.push((attribute=='className' ? 'class' : attribute) +
'="' + attributes[attribute].toString().escapeHTML() + '"');
return attrs.join(" ");
},
_children: function(element, children) {
if(typeof children=='object') { // array can hold nodes and text
children.flatten().each( function(e) {
if(typeof e=='object')
element.appendChild(e)
else
if(Builder._isStringOrNumber(e))
element.appendChild(Builder._text(e));
});
} else
if(Builder._isStringOrNumber(children))
element.appendChild(Builder._text(children));
},
_isStringOrNumber: function(param) {
return(typeof param=='string' || typeof param=='number');
}
}

815
library/cropper/lib/controls.js vendored Normal file
View file

@ -0,0 +1,815 @@
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
// (c) 2005 Jon Tirsen (http://www.tirsen.com)
// Contributors:
// Richard Livsey
// Rahul Bhargava
// Rob Wills
//
// See scriptaculous.js for full license.
// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.
var Autocompleter = {}
Autocompleter.Base = function() {};
Autocompleter.Base.prototype = {
baseInitialize: function(element, update, options) {
this.element = $(element);
this.update = $(update);
this.hasFocus = false;
this.changed = false;
this.active = false;
this.index = 0;
this.entryCount = 0;
if (this.setOptions)
this.setOptions(options);
else
this.options = options || {};
this.options.paramName = this.options.paramName || this.element.name;
this.options.tokens = this.options.tokens || [];
this.options.frequency = this.options.frequency || 0.4;
this.options.minChars = this.options.minChars || 1;
this.options.onShow = this.options.onShow ||
function(element, update){
if(!update.style.position || update.style.position=='absolute') {
update.style.position = 'absolute';
Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
}
Effect.Appear(update,{duration:0.15});
};
this.options.onHide = this.options.onHide ||
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
if (typeof(this.options.tokens) == 'string')
this.options.tokens = new Array(this.options.tokens);
this.observer = null;
this.element.setAttribute('autocomplete','off');
Element.hide(this.update);
Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
},
show: function() {
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
if(!this.iefix &&
(navigator.appVersion.indexOf('MSIE')>0) &&
(navigator.userAgent.indexOf('Opera')<0) &&
(Element.getStyle(this.update, 'position')=='absolute')) {
new Insertion.After(this.update,
'<iframe id="' + this.update.id + '_iefix" '+
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
this.iefix = $(this.update.id+'_iefix');
}
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
},
fixIEOverlapping: function() {
Position.clone(this.update, this.iefix);
this.iefix.style.zIndex = 1;
this.update.style.zIndex = 2;
Element.show(this.iefix);
},
hide: function() {
this.stopIndicator();
if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
if(this.iefix) Element.hide(this.iefix);
},
startIndicator: function() {
if(this.options.indicator) Element.show(this.options.indicator);
},
stopIndicator: function() {
if(this.options.indicator) Element.hide(this.options.indicator);
},
onKeyPress: function(event) {
if(this.active)
switch(event.keyCode) {
case Event.KEY_TAB:
case Event.KEY_RETURN:
this.selectEntry();
Event.stop(event);
case Event.KEY_ESC:
this.hide();
this.active = false;
Event.stop(event);
return;
case Event.KEY_LEFT:
case Event.KEY_RIGHT:
return;
case Event.KEY_UP:
this.markPrevious();
this.render();
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
return;
case Event.KEY_DOWN:
this.markNext();
this.render();
if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
return;
}
else
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
(navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
this.changed = true;
this.hasFocus = true;
if(this.observer) clearTimeout(this.observer);
this.observer =
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
},
activate: function() {
this.changed = false;
this.hasFocus = true;
this.getUpdatedChoices();
},
onHover: function(event) {
var element = Event.findElement(event, 'LI');
if(this.index != element.autocompleteIndex)
{
this.index = element.autocompleteIndex;
this.render();
}
Event.stop(event);
},
onClick: function(event) {
var element = Event.findElement(event, 'LI');
this.index = element.autocompleteIndex;
this.selectEntry();
this.hide();
},
onBlur: function(event) {
// needed to make click events working
setTimeout(this.hide.bind(this), 250);
this.hasFocus = false;
this.active = false;
},
render: function() {
if(this.entryCount > 0) {
for (var i = 0; i < this.entryCount; i++)
this.index==i ?
Element.addClassName(this.getEntry(i),"selected") :
Element.removeClassName(this.getEntry(i),"selected");
if(this.hasFocus) {
this.show();
this.active = true;
}
} else {
this.active = false;
this.hide();
}
},
markPrevious: function() {
if(this.index > 0) this.index--
else this.index = this.entryCount-1;
},
markNext: function() {
if(this.index < this.entryCount-1) this.index++
else this.index = 0;
},
getEntry: function(index) {
return this.update.firstChild.childNodes[index];
},
getCurrentEntry: function() {
return this.getEntry(this.index);
},
selectEntry: function() {
this.active = false;
this.updateElement(this.getCurrentEntry());
},
updateElement: function(selectedElement) {
if (this.options.updateElement) {
this.options.updateElement(selectedElement);
return;
}
var value = '';
if (this.options.select) {
var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
} else
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
var lastTokenPos = this.findLastToken();
if (lastTokenPos != -1) {
var newValue = this.element.value.substr(0, lastTokenPos + 1);
var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
if (whitespace)
newValue += whitespace[0];
this.element.value = newValue + value;
} else {
this.element.value = value;
}
this.element.focus();
if (this.options.afterUpdateElement)
this.options.afterUpdateElement(this.element, selectedElement);
},
updateChoices: function(choices) {
if(!this.changed && this.hasFocus) {
this.update.innerHTML = choices;
Element.cleanWhitespace(this.update);
Element.cleanWhitespace(this.update.firstChild);
if(this.update.firstChild && this.update.firstChild.childNodes) {
this.entryCount =
this.update.firstChild.childNodes.length;
for (var i = 0; i < this.entryCount; i++) {
var entry = this.getEntry(i);
entry.autocompleteIndex = i;
this.addObservers(entry);
}
} else {
this.entryCount = 0;
}
this.stopIndicator();
this.index = 0;
this.render();
}
},
addObservers: function(element) {
Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
Event.observe(element, "click", this.onClick.bindAsEventListener(this));
},
onObserverEvent: function() {
this.changed = false;
if(this.getToken().length>=this.options.minChars) {
this.startIndicator();
this.getUpdatedChoices();
} else {
this.active = false;
this.hide();
}
},
getToken: function() {
var tokenPos = this.findLastToken();
if (tokenPos != -1)
var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
else
var ret = this.element.value;
return /\n/.test(ret) ? '' : ret;
},
findLastToken: function() {
var lastTokenPos = -1;
for (var i=0; i<this.options.tokens.length; i++) {
var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
if (thisTokenPos > lastTokenPos)
lastTokenPos = thisTokenPos;
}
return lastTokenPos;
}
}
Ajax.Autocompleter = Class.create();
Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
initialize: function(element, update, url, options) {
this.baseInitialize(element, update, options);
this.options.asynchronous = true;
this.options.onComplete = this.onComplete.bind(this);
this.options.defaultParams = this.options.parameters || null;
this.url = url;
},
getUpdatedChoices: function() {
entry = encodeURIComponent(this.options.paramName) + '=' +
encodeURIComponent(this.getToken());
this.options.parameters = this.options.callback ?
this.options.callback(this.element, entry) : entry;
if(this.options.defaultParams)
this.options.parameters += '&' + this.options.defaultParams;
new Ajax.Request(this.url, this.options);
},
onComplete: function(request) {
this.updateChoices(request.responseText);
}
});
// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
// text only at the beginning of strings in the
// autocomplete array. Defaults to true, which will
// match text at the beginning of any *word* in the
// strings in the autocomplete array. If you want to
// search anywhere in the string, additionally set
// the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
// a partial match (unlike minChars, which defines
// how many characters are required to do any match
// at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
// Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.
Autocompleter.Local = Class.create();
Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
initialize: function(element, update, array, options) {
this.baseInitialize(element, update, options);
this.options.array = array;
},
getUpdatedChoices: function() {
this.updateChoices(this.options.selector(this));
},
setOptions: function(options) {
this.options = Object.extend({
choices: 10,
partialSearch: true,
partialChars: 2,
ignoreCase: true,
fullSearch: false,
selector: function(instance) {
var ret = []; // Beginning matches
var partial = []; // Inside matches
var entry = instance.getToken();
var count = 0;
for (var i = 0; i < instance.options.array.length &&
ret.length < instance.options.choices ; i++) {
var elem = instance.options.array[i];
var foundPos = instance.options.ignoreCase ?
elem.toLowerCase().indexOf(entry.toLowerCase()) :
elem.indexOf(entry);
while (foundPos != -1) {
if (foundPos == 0 && elem.length != entry.length) {
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
elem.substr(entry.length) + "</li>");
break;
} else if (entry.length >= instance.options.partialChars &&
instance.options.partialSearch && foundPos != -1) {
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
foundPos + entry.length) + "</li>");
break;
}
}
foundPos = instance.options.ignoreCase ?
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
elem.indexOf(entry, foundPos + 1);
}
}
if (partial.length)
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
return "<ul>" + ret.join('') + "</ul>";
}
}, options || {});
}
});
// AJAX in-place editor
//
// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
setTimeout(function() {
Field.activate(field);
}, 1);
}
Ajax.InPlaceEditor = Class.create();
Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
Ajax.InPlaceEditor.prototype = {
initialize: function(element, url, options) {
this.url = url;
this.element = $(element);
this.options = Object.extend({
okButton: true,
okText: "ok",
cancelLink: true,
cancelText: "cancel",
savingText: "Saving...",
clickToEditText: "Click to edit",
okText: "ok",
rows: 1,
onComplete: function(transport, element) {
new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
},
onFailure: function(transport) {
alert("Error communicating with the server: " + transport.responseText.stripTags());
},
callback: function(form) {
return Form.serialize(form);
},
handleLineBreaks: true,
loadingText: 'Loading...',
savingClassName: 'inplaceeditor-saving',
loadingClassName: 'inplaceeditor-loading',
formClassName: 'inplaceeditor-form',
highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
highlightendcolor: "#FFFFFF",
externalControl: null,
submitOnBlur: false,
ajaxOptions: {},
evalScripts: false
}, options || {});
if(!this.options.formId && this.element.id) {
this.options.formId = this.element.id + "-inplaceeditor";
if ($(this.options.formId)) {
// there's already a form with that name, don't specify an id
this.options.formId = null;
}
}
if (this.options.externalControl) {
this.options.externalControl = $(this.options.externalControl);
}
this.originalBackground = Element.getStyle(this.element, 'background-color');
if (!this.originalBackground) {
this.originalBackground = "transparent";
}
this.element.title = this.options.clickToEditText;
this.onclickListener = this.enterEditMode.bindAsEventListener(this);
this.mouseoverListener = this.enterHover.bindAsEventListener(this);
this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
Event.observe(this.element, 'click', this.onclickListener);
Event.observe(this.element, 'mouseover', this.mouseoverListener);
Event.observe(this.element, 'mouseout', this.mouseoutListener);
if (this.options.externalControl) {
Event.observe(this.options.externalControl, 'click', this.onclickListener);
Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
}
},
enterEditMode: function(evt) {
if (this.saving) return;
if (this.editing) return;
this.editing = true;
this.onEnterEditMode();
if (this.options.externalControl) {
Element.hide(this.options.externalControl);
}
Element.hide(this.element);
this.createForm();
this.element.parentNode.insertBefore(this.form, this.element);
Field.scrollFreeActivate(this.editField);
// stop the event to avoid a page refresh in Safari
if (evt) {
Event.stop(evt);
}
return false;
},
createForm: function() {
this.form = document.createElement("form");
this.form.id = this.options.formId;
Element.addClassName(this.form, this.options.formClassName)
this.form.onsubmit = this.onSubmit.bind(this);
this.createEditField();
if (this.options.textarea) {
var br = document.createElement("br");
this.form.appendChild(br);
}
if (this.options.okButton) {
okButton = document.createElement("input");
okButton.type = "submit";
okButton.value = this.options.okText;
okButton.className = 'editor_ok_button';
this.form.appendChild(okButton);
}
if (this.options.cancelLink) {
cancelLink = document.createElement("a");
cancelLink.href = "#";
cancelLink.appendChild(document.createTextNode(this.options.cancelText));
cancelLink.onclick = this.onclickCancel.bind(this);
cancelLink.className = 'editor_cancel';
this.form.appendChild(cancelLink);
}
},
hasHTMLLineBreaks: function(string) {
if (!this.options.handleLineBreaks) return false;
return string.match(/<br/i) || string.match(/<p>/i);
},
convertHTMLLineBreaks: function(string) {
return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
},
createEditField: function() {
var text;
if(this.options.loadTextURL) {
text = this.options.loadingText;
} else {
text = this.getText();
}
var obj = this;
if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
this.options.textarea = false;
var textField = document.createElement("input");
textField.obj = this;
textField.type = "text";
textField.name = "value";
textField.value = text;
textField.style.backgroundColor = this.options.highlightcolor;
textField.className = 'editor_field';
var size = this.options.size || this.options.cols || 0;
if (size != 0) textField.size = size;
if (this.options.submitOnBlur)
textField.onblur = this.onSubmit.bind(this);
this.editField = textField;
} else {
this.options.textarea = true;
var textArea = document.createElement("textarea");
textArea.obj = this;
textArea.name = "value";
textArea.value = this.convertHTMLLineBreaks(text);
textArea.rows = this.options.rows;
textArea.cols = this.options.cols || 40;
textArea.className = 'editor_field';
if (this.options.submitOnBlur)
textArea.onblur = this.onSubmit.bind(this);
this.editField = textArea;
}
if(this.options.loadTextURL) {
this.loadExternalText();
}
this.form.appendChild(this.editField);
},
getText: function() {
return this.element.innerHTML;
},
loadExternalText: function() {
Element.addClassName(this.form, this.options.loadingClassName);
this.editField.disabled = true;
new Ajax.Request(
this.options.loadTextURL,
Object.extend({
asynchronous: true,
onComplete: this.onLoadedExternalText.bind(this)
}, this.options.ajaxOptions)
);
},
onLoadedExternalText: function(transport) {
Element.removeClassName(this.form, this.options.loadingClassName);
this.editField.disabled = false;
this.editField.value = transport.responseText.stripTags();
},
onclickCancel: function() {
this.onComplete();
this.leaveEditMode();
return false;
},
onFailure: function(transport) {
this.options.onFailure(transport);
if (this.oldInnerHTML) {
this.element.innerHTML = this.oldInnerHTML;
this.oldInnerHTML = null;
}
return false;
},
onSubmit: function() {
// onLoading resets these so we need to save them away for the Ajax call
var form = this.form;
var value = this.editField.value;
// do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
// which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
// to be displayed indefinitely
this.onLoading();
if (this.options.evalScripts) {
new Ajax.Request(
this.url, Object.extend({
parameters: this.options.callback(form, value),
onComplete: this.onComplete.bind(this),
onFailure: this.onFailure.bind(this),
asynchronous:true,
evalScripts:true
}, this.options.ajaxOptions));
} else {
new Ajax.Updater(
{ success: this.element,
// don't update on failure (this could be an option)
failure: null },
this.url, Object.extend({
parameters: this.options.callback(form, value),
onComplete: this.onComplete.bind(this),
onFailure: this.onFailure.bind(this)
}, this.options.ajaxOptions));
}
// stop the event to avoid a page refresh in Safari
if (arguments.length > 1) {
Event.stop(arguments[0]);
}
return false;
},
onLoading: function() {
this.saving = true;
this.removeForm();
this.leaveHover();
this.showSaving();
},
showSaving: function() {
this.oldInnerHTML = this.element.innerHTML;
this.element.innerHTML = this.options.savingText;
Element.addClassName(this.element, this.options.savingClassName);
this.element.style.backgroundColor = this.originalBackground;
Element.show(this.element);
},
removeForm: function() {
if(this.form) {
if (this.form.parentNode) Element.remove(this.form);
this.form = null;
}
},
enterHover: function() {
if (this.saving) return;
this.element.style.backgroundColor = this.options.highlightcolor;
if (this.effect) {
this.effect.cancel();
}
Element.addClassName(this.element, this.options.hoverClassName)
},
leaveHover: function() {
if (this.options.backgroundColor) {
this.element.style.backgroundColor = this.oldBackground;
}
Element.removeClassName(this.element, this.options.hoverClassName)
if (this.saving) return;
this.effect = new Effect.Highlight(this.element, {
startcolor: this.options.highlightcolor,
endcolor: this.options.highlightendcolor,
restorecolor: this.originalBackground
});
},
leaveEditMode: function() {
Element.removeClassName(this.element, this.options.savingClassName);
this.removeForm();
this.leaveHover();
this.element.style.backgroundColor = this.originalBackground;
Element.show(this.element);
if (this.options.externalControl) {
Element.show(this.options.externalControl);
}
this.editing = false;
this.saving = false;
this.oldInnerHTML = null;
this.onLeaveEditMode();
},
onComplete: function(transport) {
this.leaveEditMode();
this.options.onComplete.bind(this)(transport, this.element);
},
onEnterEditMode: function() {},
onLeaveEditMode: function() {},
dispose: function() {
if (this.oldInnerHTML) {
this.element.innerHTML = this.oldInnerHTML;
}
this.leaveEditMode();
Event.stopObserving(this.element, 'click', this.onclickListener);
Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
if (this.options.externalControl) {
Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
}
}
};
Ajax.InPlaceCollectionEditor = Class.create();
Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
createEditField: function() {
if (!this.cached_selectTag) {
var selectTag = document.createElement("select");
var collection = this.options.collection || [];
var optionTag;
collection.each(function(e,i) {
optionTag = document.createElement("option");
optionTag.value = (e instanceof Array) ? e[0] : e;
if(this.options.value==optionTag.value) optionTag.selected = true;
optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
selectTag.appendChild(optionTag);
}.bind(this));
this.cached_selectTag = selectTag;
}
this.editField = this.cached_selectTag;
if(this.options.loadTextURL) this.loadExternalText();
this.form.appendChild(this.editField);
this.options.callback = function(form, value) {
return "value=" + encodeURIComponent(value);
}
}
});
// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields
Form.Element.DelayedObserver = Class.create();
Form.Element.DelayedObserver.prototype = {
initialize: function(element, delay, callback) {
this.delay = delay || 0.5;
this.element = $(element);
this.callback = callback;
this.timer = null;
this.lastValue = $F(this.element);
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
},
delayedListener: function(event) {
if(this.lastValue == $F(this.element)) return;
if(this.timer) clearTimeout(this.timer);
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
this.lastValue = $F(this.element);
},
onTimerEvent: function() {
this.timer = null;
this.callback(this.element, $F(this.element));
}
};

915
library/cropper/lib/dragdrop.js vendored Normal file
View file

@ -0,0 +1,915 @@
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
//
// See scriptaculous.js for full license.
/*--------------------------------------------------------------------------*/
var Droppables = {
drops: [],
remove: function(element) {
this.drops = this.drops.reject(function(d) { return d.element==$(element) });
},
add: function(element) {
element = $(element);
var options = Object.extend({
greedy: true,
hoverclass: null,
tree: false
}, arguments[1] || {});
// cache containers
if(options.containment) {
options._containers = [];
var containment = options.containment;
if((typeof containment == 'object') &&
(containment.constructor == Array)) {
containment.each( function(c) { options._containers.push($(c)) });
} else {
options._containers.push($(containment));
}
}
if(options.accept) options.accept = [options.accept].flatten();
Element.makePositioned(element); // fix IE
options.element = element;
this.drops.push(options);
},
findDeepestChild: function(drops) {
deepest = drops[0];
for (i = 1; i < drops.length; ++i)
if (Element.isParent(drops[i].element, deepest.element))
deepest = drops[i];
return deepest;
},
isContained: function(element, drop) {
var containmentNode;
if(drop.tree) {
containmentNode = element.treeNode;
} else {
containmentNode = element.parentNode;
}
return drop._containers.detect(function(c) { return containmentNode == c });
},
isAffected: function(point, element, drop) {
return (
(drop.element!=element) &&
((!drop._containers) ||
this.isContained(element, drop)) &&
((!drop.accept) ||
(Element.classNames(element).detect(
function(v) { return drop.accept.include(v) } ) )) &&
Position.within(drop.element, point[0], point[1]) );
},
deactivate: function(drop) {
if(drop.hoverclass)
Element.removeClassName(drop.element, drop.hoverclass);
this.last_active = null;
},
activate: function(drop) {
if(drop.hoverclass)
Element.addClassName(drop.element, drop.hoverclass);
this.last_active = drop;
},
show: function(point, element) {
if(!this.drops.length) return;
var affected = [];
if(this.last_active) this.deactivate(this.last_active);
this.drops.each( function(drop) {
if(Droppables.isAffected(point, element, drop))
affected.push(drop);
});
if(affected.length>0) {
drop = Droppables.findDeepestChild(affected);
Position.within(drop.element, point[0], point[1]);
if(drop.onHover)
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
Droppables.activate(drop);
}
},
fire: function(event, element) {
if(!this.last_active) return;
Position.prepare();
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
if (this.last_active.onDrop)
this.last_active.onDrop(element, this.last_active.element, event);
},
reset: function() {
if(this.last_active)
this.deactivate(this.last_active);
}
}
var Draggables = {
drags: [],
observers: [],
register: function(draggable) {
if(this.drags.length == 0) {
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
this.eventKeypress = this.keyPress.bindAsEventListener(this);
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
Event.observe(document, "keypress", this.eventKeypress);
}
this.drags.push(draggable);
},
unregister: function(draggable) {
this.drags = this.drags.reject(function(d) { return d==draggable });
if(this.drags.length == 0) {
Event.stopObserving(document, "mouseup", this.eventMouseUp);
Event.stopObserving(document, "mousemove", this.eventMouseMove);
Event.stopObserving(document, "keypress", this.eventKeypress);
}
},
activate: function(draggable) {
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
this.activeDraggable = draggable;
},
deactivate: function() {
this.activeDraggable = null;
},
updateDrag: function(event) {
if(!this.activeDraggable) return;
var pointer = [Event.pointerX(event), Event.pointerY(event)];
// Mozilla-based browsers fire successive mousemove events with
// the same coordinates, prevent needless redrawing (moz bug?)
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
this._lastPointer = pointer;
this.activeDraggable.updateDrag(event, pointer);
},
endDrag: function(event) {
if(!this.activeDraggable) return;
this._lastPointer = null;
this.activeDraggable.endDrag(event);
this.activeDraggable = null;
},
keyPress: function(event) {
if(this.activeDraggable)
this.activeDraggable.keyPress(event);
},
addObserver: function(observer) {
this.observers.push(observer);
this._cacheObserverCallbacks();
},
removeObserver: function(element) { // element instead of observer fixes mem leaks
this.observers = this.observers.reject( function(o) { return o.element==element });
this._cacheObserverCallbacks();
},
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
if(this[eventName+'Count'] > 0)
this.observers.each( function(o) {
if(o[eventName]) o[eventName](eventName, draggable, event);
});
},
_cacheObserverCallbacks: function() {
['onStart','onEnd','onDrag'].each( function(eventName) {
Draggables[eventName+'Count'] = Draggables.observers.select(
function(o) { return o[eventName]; }
).length;
});
}
}
/*--------------------------------------------------------------------------*/
var Draggable = Class.create();
Draggable.prototype = {
initialize: function(element) {
var options = Object.extend({
handle: false,
starteffect: function(element) {
element._opacity = Element.getOpacity(element);
new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
},
reverteffect: function(element, top_offset, left_offset) {
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
},
endeffect: function(element) {
var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity});
},
zindex: 1000,
revert: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
}, arguments[1] || {});
this.element = $(element);
if(options.handle && (typeof options.handle == 'string')) {
var h = Element.childrenWithClassName(this.element, options.handle, true);
if(h.length>0) this.handle = h[0];
}
if(!this.handle) this.handle = $(options.handle);
if(!this.handle) this.handle = this.element;
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
options.scroll = $(options.scroll);
Element.makePositioned(this.element); // fix IE
this.delta = this.currentDelta();
this.options = options;
this.dragging = false;
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
Event.observe(this.handle, "mousedown", this.eventMouseDown);
Draggables.register(this);
},
destroy: function() {
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
Draggables.unregister(this);
},
currentDelta: function() {
return([
parseInt(Element.getStyle(this.element,'left') || '0'),
parseInt(Element.getStyle(this.element,'top') || '0')]);
},
initDrag: function(event) {
if(Event.isLeftClick(event)) {
// abort on form elements, fixes a Firefox issue
var src = Event.element(event);
if(src.tagName && (
src.tagName=='INPUT' ||
src.tagName=='SELECT' ||
src.tagName=='OPTION' ||
src.tagName=='BUTTON' ||
src.tagName=='TEXTAREA')) return;
if(this.element._revert) {
this.element._revert.cancel();
this.element._revert = null;
}
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var pos = Position.cumulativeOffset(this.element);
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
Draggables.activate(this);
Event.stop(event);
}
},
startDrag: function(event) {
this.dragging = true;
if(this.options.zindex) {
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
this.element.style.zIndex = this.options.zindex;
}
if(this.options.ghosting) {
this._clone = this.element.cloneNode(true);
Position.absolutize(this.element);
this.element.parentNode.insertBefore(this._clone, this.element);
}
if(this.options.scroll) {
if (this.options.scroll == window) {
var where = this._getWindowScroll(this.options.scroll);
this.originalScrollLeft = where.left;
this.originalScrollTop = where.top;
} else {
this.originalScrollLeft = this.options.scroll.scrollLeft;
this.originalScrollTop = this.options.scroll.scrollTop;
}
}
Draggables.notify('onStart', this, event);
if(this.options.starteffect) this.options.starteffect(this.element);
},
updateDrag: function(event, pointer) {
if(!this.dragging) this.startDrag(event);
Position.prepare();
Droppables.show(pointer, this.element);
Draggables.notify('onDrag', this, event);
this.draw(pointer);
if(this.options.change) this.options.change(this);
if(this.options.scroll) {
this.stopScrolling();
var p;
if (this.options.scroll == window) {
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
} else {
p = Position.page(this.options.scroll);
p[0] += this.options.scroll.scrollLeft;
p[1] += this.options.scroll.scrollTop;
p.push(p[0]+this.options.scroll.offsetWidth);
p.push(p[1]+this.options.scroll.offsetHeight);
}
var speed = [0,0];
if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
this.startScrolling(speed);
}
// fix AppleWebKit rendering
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
Event.stop(event);
},
finishDrag: function(event, success) {
this.dragging = false;
if(this.options.ghosting) {
Position.relativize(this.element);
Element.remove(this._clone);
this._clone = null;
}
if(success) Droppables.fire(event, this.element);
Draggables.notify('onEnd', this, event);
var revert = this.options.revert;
if(revert && typeof revert == 'function') revert = revert(this.element);
var d = this.currentDelta();
if(revert && this.options.reverteffect) {
this.options.reverteffect(this.element,
d[1]-this.delta[1], d[0]-this.delta[0]);
} else {
this.delta = d;
}
if(this.options.zindex)
this.element.style.zIndex = this.originalZ;
if(this.options.endeffect)
this.options.endeffect(this.element);
Draggables.deactivate(this);
Droppables.reset();
},
keyPress: function(event) {
if(event.keyCode!=Event.KEY_ESC) return;
this.finishDrag(event, false);
Event.stop(event);
},
endDrag: function(event) {
if(!this.dragging) return;
this.stopScrolling();
this.finishDrag(event, true);
Event.stop(event);
},
draw: function(point) {
var pos = Position.cumulativeOffset(this.element);
var d = this.currentDelta();
pos[0] -= d[0]; pos[1] -= d[1];
if(this.options.scroll && (this.options.scroll != window)) {
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
}
var p = [0,1].map(function(i){
return (point[i]-pos[i]-this.offset[i])
}.bind(this));
if(this.options.snap) {
if(typeof this.options.snap == 'function') {
p = this.options.snap(p[0],p[1],this);
} else {
if(this.options.snap instanceof Array) {
p = p.map( function(v, i) {
return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
} else {
p = p.map( function(v) {
return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
}
}}
var style = this.element.style;
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
style.left = p[0] + "px";
if((!this.options.constraint) || (this.options.constraint=='vertical'))
style.top = p[1] + "px";
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
},
stopScrolling: function() {
if(this.scrollInterval) {
clearInterval(this.scrollInterval);
this.scrollInterval = null;
Draggables._lastScrollPointer = null;
}
},
startScrolling: function(speed) {
this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
this.lastScrolled = new Date();
this.scrollInterval = setInterval(this.scroll.bind(this), 10);
},
scroll: function() {
var current = new Date();
var delta = current - this.lastScrolled;
this.lastScrolled = current;
if(this.options.scroll == window) {
with (this._getWindowScroll(this.options.scroll)) {
if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
var d = delta / 1000;
this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
}
}
} else {
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
}
Position.prepare();
Droppables.show(Draggables._lastPointer, this.element);
Draggables.notify('onDrag', this);
Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
if (Draggables._lastScrollPointer[0] < 0)
Draggables._lastScrollPointer[0] = 0;
if (Draggables._lastScrollPointer[1] < 0)
Draggables._lastScrollPointer[1] = 0;
this.draw(Draggables._lastScrollPointer);
if(this.options.change) this.options.change(this);
},
_getWindowScroll: function(w) {
var T, L, W, H;
with (w.document) {
if (w.document.documentElement && documentElement.scrollTop) {
T = documentElement.scrollTop;
L = documentElement.scrollLeft;
} else if (w.document.body) {
T = body.scrollTop;
L = body.scrollLeft;
}
if (w.innerWidth) {
W = w.innerWidth;
H = w.innerHeight;
} else if (w.document.documentElement && documentElement.clientWidth) {
W = documentElement.clientWidth;
H = documentElement.clientHeight;
} else {
W = body.offsetWidth;
H = body.offsetHeight
}
}
return { top: T, left: L, width: W, height: H };
}
}
/*--------------------------------------------------------------------------*/
var SortableObserver = Class.create();
SortableObserver.prototype = {
initialize: function(element, observer) {
this.element = $(element);
this.observer = observer;
this.lastValue = Sortable.serialize(this.element);
},
onStart: function() {
this.lastValue = Sortable.serialize(this.element);
},
onEnd: function() {
Sortable.unmark();
if(this.lastValue != Sortable.serialize(this.element))
this.observer(this.element)
}
}
var Sortable = {
sortables: {},
_findRootElement: function(element) {
while (element.tagName != "BODY") {
if(element.id && Sortable.sortables[element.id]) return element;
element = element.parentNode;
}
},
options: function(element) {
element = Sortable._findRootElement($(element));
if(!element) return;
return Sortable.sortables[element.id];
},
destroy: function(element){
var s = Sortable.options(element);
if(s) {
Draggables.removeObserver(s.element);
s.droppables.each(function(d){ Droppables.remove(d) });
s.draggables.invoke('destroy');
delete Sortable.sortables[s.element.id];
}
},
create: function(element) {
element = $(element);
var options = Object.extend({
element: element,
tag: 'li', // assumes li children, override with tag: 'tagname'
dropOnEmpty: false,
tree: false,
treeTag: 'ul',
overlap: 'vertical', // one of 'vertical', 'horizontal'
constraint: 'vertical', // one of 'vertical', 'horizontal', false
containment: element, // also takes array of elements (or id's); or false
handle: false, // or a CSS class
only: false,
hoverclass: null,
ghosting: false,
scroll: false,
scrollSensitivity: 20,
scrollSpeed: 15,
format: /^[^_]*_(.*)$/,
onChange: Prototype.emptyFunction,
onUpdate: Prototype.emptyFunction
}, arguments[1] || {});
// clear any old sortable with same element
this.destroy(element);
// build options for the draggables
var options_for_draggable = {
revert: true,
scroll: options.scroll,
scrollSpeed: options.scrollSpeed,
scrollSensitivity: options.scrollSensitivity,
ghosting: options.ghosting,
constraint: options.constraint,
handle: options.handle };
if(options.starteffect)
options_for_draggable.starteffect = options.starteffect;
if(options.reverteffect)
options_for_draggable.reverteffect = options.reverteffect;
else
if(options.ghosting) options_for_draggable.reverteffect = function(element) {
element.style.top = 0;
element.style.left = 0;
};
if(options.endeffect)
options_for_draggable.endeffect = options.endeffect;
if(options.zindex)
options_for_draggable.zindex = options.zindex;
// build options for the droppables
var options_for_droppable = {
overlap: options.overlap,
containment: options.containment,
tree: options.tree,
hoverclass: options.hoverclass,
onHover: Sortable.onHover
//greedy: !options.dropOnEmpty
}
var options_for_tree = {
onHover: Sortable.onEmptyHover,
overlap: options.overlap,
containment: options.containment,
hoverclass: options.hoverclass
}
// fix for gecko engine
Element.cleanWhitespace(element);
options.draggables = [];
options.droppables = [];
// drop on empty handling
if(options.dropOnEmpty || options.tree) {
Droppables.add(element, options_for_tree);
options.droppables.push(element);
}
(this.findElements(element, options) || []).each( function(e) {
// handles are per-draggable
var handle = options.handle ?
Element.childrenWithClassName(e, options.handle)[0] : e;
options.draggables.push(
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
Droppables.add(e, options_for_droppable);
if(options.tree) e.treeNode = element;
options.droppables.push(e);
});
if(options.tree) {
(Sortable.findTreeElements(element, options) || []).each( function(e) {
Droppables.add(e, options_for_tree);
e.treeNode = element;
options.droppables.push(e);
});
}
// keep reference
this.sortables[element.id] = options;
// for onupdate
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
},
// return all suitable-for-sortable elements in a guaranteed order
findElements: function(element, options) {
return Element.findChildren(
element, options.only, options.tree ? true : false, options.tag);
},
findTreeElements: function(element, options) {
return Element.findChildren(
element, options.only, options.tree ? true : false, options.treeTag);
},
onHover: function(element, dropon, overlap) {
if(Element.isParent(dropon, element)) return;
if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
return;
} else if(overlap>0.5) {
Sortable.mark(dropon, 'before');
if(dropon.previousSibling != element) {
var oldParentNode = element.parentNode;
element.style.visibility = "hidden"; // fix gecko rendering
dropon.parentNode.insertBefore(element, dropon);
if(dropon.parentNode!=oldParentNode)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
} else {
Sortable.mark(dropon, 'after');
var nextElement = dropon.nextSibling || null;
if(nextElement != element) {
var oldParentNode = element.parentNode;
element.style.visibility = "hidden"; // fix gecko rendering
dropon.parentNode.insertBefore(element, nextElement);
if(dropon.parentNode!=oldParentNode)
Sortable.options(oldParentNode).onChange(element);
Sortable.options(dropon.parentNode).onChange(element);
}
}
},
onEmptyHover: function(element, dropon, overlap) {
var oldParentNode = element.parentNode;
var droponOptions = Sortable.options(dropon);
if(!Element.isParent(dropon, element)) {
var index;
var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
var child = null;
if(children) {
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
for (index = 0; index < children.length; index += 1) {
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
offset -= Element.offsetSize (children[index], droponOptions.overlap);
} else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
child = index + 1 < children.length ? children[index + 1] : null;
break;
} else {
child = children[index];
break;
}
}
}
dropon.insertBefore(element, child);
Sortable.options(oldParentNode).onChange(element);
droponOptions.onChange(element);
}
},
unmark: function() {
if(Sortable._marker) Element.hide(Sortable._marker);
},
mark: function(dropon, position) {
// mark on ghosting only
var sortable = Sortable.options(dropon.parentNode);
if(sortable && !sortable.ghosting) return;
if(!Sortable._marker) {
Sortable._marker = $('dropmarker') || document.createElement('DIV');
Element.hide(Sortable._marker);
Element.addClassName(Sortable._marker, 'dropmarker');
Sortable._marker.style.position = 'absolute';
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
}
var offsets = Position.cumulativeOffset(dropon);
Sortable._marker.style.left = offsets[0] + 'px';
Sortable._marker.style.top = offsets[1] + 'px';
if(position=='after')
if(sortable.overlap == 'horizontal')
Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
else
Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
Element.show(Sortable._marker);
},
_tree: function(element, options, parent) {
var children = Sortable.findElements(element, options) || [];
for (var i = 0; i < children.length; ++i) {
var match = children[i].id.match(options.format);
if (!match) continue;
var child = {
id: encodeURIComponent(match ? match[1] : null),
element: element,
parent: parent,
children: new Array,
position: parent.children.length,
container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
}
/* Get the element containing the children and recurse over it */
if (child.container)
this._tree(child.container, options, child)
parent.children.push (child);
}
return parent;
},
/* Finds the first element of the given tag type within a parent element.
Used for finding the first LI[ST] within a L[IST]I[TEM].*/
_findChildrenElement: function (element, containerTag) {
if (element && element.hasChildNodes)
for (var i = 0; i < element.childNodes.length; ++i)
if (element.childNodes[i].tagName == containerTag)
return element.childNodes[i];
return null;
},
tree: function(element) {
element = $(element);
var sortableOptions = this.options(element);
var options = Object.extend({
tag: sortableOptions.tag,
treeTag: sortableOptions.treeTag,
only: sortableOptions.only,
name: element.id,
format: sortableOptions.format
}, arguments[1] || {});
var root = {
id: null,
parent: null,
children: new Array,
container: element,
position: 0
}
return Sortable._tree (element, options, root);
},
/* Construct a [i] index for a particular node */
_constructIndex: function(node) {
var index = '';
do {
if (node.id) index = '[' + node.position + ']' + index;
} while ((node = node.parent) != null);
return index;
},
sequence: function(element) {
element = $(element);
var options = Object.extend(this.options(element), arguments[1] || {});
return $(this.findElements(element, options) || []).map( function(item) {
return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
});
},
setSequence: function(element, new_sequence) {
element = $(element);
var options = Object.extend(this.options(element), arguments[2] || {});
var nodeMap = {};
this.findElements(element, options).each( function(n) {
if (n.id.match(options.format))
nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
n.parentNode.removeChild(n);
});
new_sequence.each(function(ident) {
var n = nodeMap[ident];
if (n) {
n[1].appendChild(n[0]);
delete nodeMap[ident];
}
});
},
serialize: function(element) {
element = $(element);
var options = Object.extend(Sortable.options(element), arguments[1] || {});
var name = encodeURIComponent(
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
if (options.tree) {
return Sortable.tree(element, arguments[1]).children.map( function (item) {
return [name + Sortable._constructIndex(item) + "=" +
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
}).flatten().join('&');
} else {
return Sortable.sequence(element, arguments[1]).map( function(item) {
return name + "[]=" + encodeURIComponent(item);
}).join('&');
}
}
}
/* Returns true if child is contained within element */
Element.isParent = function(child, element) {
if (!child.parentNode || child == element) return false;
if (child.parentNode == element) return true;
return Element.isParent(child.parentNode, element);
}
Element.findChildren = function(element, only, recursive, tagName) {
if(!element.hasChildNodes()) return null;
tagName = tagName.toUpperCase();
if(only) only = [only].flatten();
var elements = [];
$A(element.childNodes).each( function(e) {
if(e.tagName && e.tagName.toUpperCase()==tagName &&
(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
elements.push(e);
if(recursive) {
var grandchildren = Element.findChildren(e, only, recursive, tagName);
if(grandchildren) elements.push(grandchildren);
}
});
return (elements.length>0 ? elements.flatten() : []);
}
Element.offsetSize = function (element, type) {
if (type == 'vertical' || type == 'height')
return element.offsetHeight;
else
return element.offsetWidth;
}

958
library/cropper/lib/effects.js vendored Normal file
View file

@ -0,0 +1,958 @@
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
// Justin Palmer (http://encytemedia.com/)
// Mark Pilgrim (http://diveintomark.org/)
// Martin Bialasinki
//
// See scriptaculous.js for full license.
// converts rgb() and #xxx to #xxxxxx format,
// returns self (or first argument) if not convertable
String.prototype.parseColor = function() {
var color = '#';
if(this.slice(0,4) == 'rgb(') {
var cols = this.slice(4,this.length-1).split(',');
var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
} else {
if(this.slice(0,1) == '#') {
if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
if(this.length==7) color = this.toLowerCase();
}
}
return(color.length==7 ? color : (arguments[0] || this));
}
/*--------------------------------------------------------------------------*/
Element.collectTextNodes = function(element) {
return $A($(element).childNodes).collect( function(node) {
return (node.nodeType==3 ? node.nodeValue :
(node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
}).flatten().join('');
}
Element.collectTextNodesIgnoreClass = function(element, className) {
return $A($(element).childNodes).collect( function(node) {
return (node.nodeType==3 ? node.nodeValue :
((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
Element.collectTextNodesIgnoreClass(node, className) : ''));
}).flatten().join('');
}
Element.setContentZoom = function(element, percent) {
element = $(element);
Element.setStyle(element, {fontSize: (percent/100) + 'em'});
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
}
Element.getOpacity = function(element){
var opacity;
if (opacity = Element.getStyle(element, 'opacity'))
return parseFloat(opacity);
if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
if(opacity[1]) return parseFloat(opacity[1]) / 100;
return 1.0;
}
Element.setOpacity = function(element, value){
element= $(element);
if (value == 1){
Element.setStyle(element, { opacity:
(/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
0.999999 : null });
if(/MSIE/.test(navigator.userAgent))
Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
} else {
if(value < 0.00001) value = 0;
Element.setStyle(element, {opacity: value});
if(/MSIE/.test(navigator.userAgent))
Element.setStyle(element,
{ filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
'alpha(opacity='+value*100+')' });
}
}
Element.getInlineOpacity = function(element){
return $(element).style.opacity || '';
}
Element.childrenWithClassName = function(element, className, findFirst) {
var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) {
return (c.className && c.className.match(classNameRegExp));
});
if(!results) results = [];
return results;
}
Element.forceRerendering = function(element) {
try {
element = $(element);
var n = document.createTextNode(' ');
element.appendChild(n);
element.removeChild(n);
} catch(e) { }
};
/*--------------------------------------------------------------------------*/
Array.prototype.call = function() {
var args = arguments;
this.each(function(f){ f.apply(this, args) });
}
/*--------------------------------------------------------------------------*/
var Effect = {
tagifyText: function(element) {
var tagifyStyle = 'position:relative';
if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
element = $(element);
$A(element.childNodes).each( function(child) {
if(child.nodeType==3) {
child.nodeValue.toArray().each( function(character) {
element.insertBefore(
Builder.node('span',{style: tagifyStyle},
character == ' ' ? String.fromCharCode(160) : character),
child);
});
Element.remove(child);
}
});
},
multiple: function(element, effect) {
var elements;
if(((typeof element == 'object') ||
(typeof element == 'function')) &&
(element.length))
elements = element;
else
elements = $(element).childNodes;
var options = Object.extend({
speed: 0.1,
delay: 0.0
}, arguments[2] || {});
var masterDelay = options.delay;
$A(elements).each( function(element, index) {
new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
});
},
PAIRS: {
'slide': ['SlideDown','SlideUp'],
'blind': ['BlindDown','BlindUp'],
'appear': ['Appear','Fade']
},
toggle: function(element, effect) {
element = $(element);
effect = (effect || 'appear').toLowerCase();
var options = Object.extend({
queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
}, arguments[2] || {});
Effect[element.visible() ?
Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
}
};
var Effect2 = Effect; // deprecated
/* ------------- transitions ------------- */
Effect.Transitions = {}
Effect.Transitions.linear = function(pos) {
return pos;
}
Effect.Transitions.sinoidal = function(pos) {
return (-Math.cos(pos*Math.PI)/2) + 0.5;
}
Effect.Transitions.reverse = function(pos) {
return 1-pos;
}
Effect.Transitions.flicker = function(pos) {
return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
}
Effect.Transitions.wobble = function(pos) {
return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
}
Effect.Transitions.pulse = function(pos) {
return (Math.floor(pos*10) % 2 == 0 ?
(pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
}
Effect.Transitions.none = function(pos) {
return 0;
}
Effect.Transitions.full = function(pos) {
return 1;
}
/* ------------- core effects ------------- */
Effect.ScopedQueue = Class.create();
Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
initialize: function() {
this.effects = [];
this.interval = null;
},
_each: function(iterator) {
this.effects._each(iterator);
},
add: function(effect) {
var timestamp = new Date().getTime();
var position = (typeof effect.options.queue == 'string') ?
effect.options.queue : effect.options.queue.position;
switch(position) {
case 'front':
// move unstarted effects after this effect
this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
e.startOn += effect.finishOn;
e.finishOn += effect.finishOn;
});
break;
case 'end':
// start effect after last queued effect has finished
timestamp = this.effects.pluck('finishOn').max() || timestamp;
break;
}
effect.startOn += timestamp;
effect.finishOn += timestamp;
if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
this.effects.push(effect);
if(!this.interval)
this.interval = setInterval(this.loop.bind(this), 40);
},
remove: function(effect) {
this.effects = this.effects.reject(function(e) { return e==effect });
if(this.effects.length == 0) {
clearInterval(this.interval);
this.interval = null;
}
},
loop: function() {
var timePos = new Date().getTime();
this.effects.invoke('loop', timePos);
}
});
Effect.Queues = {
instances: $H(),
get: function(queueName) {
if(typeof queueName != 'string') return queueName;
if(!this.instances[queueName])
this.instances[queueName] = new Effect.ScopedQueue();
return this.instances[queueName];
}
}
Effect.Queue = Effect.Queues.get('global');
Effect.DefaultOptions = {
transition: Effect.Transitions.sinoidal,
duration: 1.0, // seconds
fps: 25.0, // max. 25fps due to Effect.Queue implementation
sync: false, // true for combining
from: 0.0,
to: 1.0,
delay: 0.0,
queue: 'parallel'
}
Effect.Base = function() {};
Effect.Base.prototype = {
position: null,
start: function(options) {
this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
this.currentFrame = 0;
this.state = 'idle';
this.startOn = this.options.delay*1000;
this.finishOn = this.startOn + (this.options.duration*1000);
this.event('beforeStart');
if(!this.options.sync)
Effect.Queues.get(typeof this.options.queue == 'string' ?
'global' : this.options.queue.scope).add(this);
},
loop: function(timePos) {
if(timePos >= this.startOn) {
if(timePos >= this.finishOn) {
this.render(1.0);
this.cancel();
this.event('beforeFinish');
if(this.finish) this.finish();
this.event('afterFinish');
return;
}
var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
var frame = Math.round(pos * this.options.fps * this.options.duration);
if(frame > this.currentFrame) {
this.render(pos);
this.currentFrame = frame;
}
}
},
render: function(pos) {
if(this.state == 'idle') {
this.state = 'running';
this.event('beforeSetup');
if(this.setup) this.setup();
this.event('afterSetup');
}
if(this.state == 'running') {
if(this.options.transition) pos = this.options.transition(pos);
pos *= (this.options.to-this.options.from);
pos += this.options.from;
this.position = pos;
this.event('beforeUpdate');
if(this.update) this.update(pos);
this.event('afterUpdate');
}
},
cancel: function() {
if(!this.options.sync)
Effect.Queues.get(typeof this.options.queue == 'string' ?
'global' : this.options.queue.scope).remove(this);
this.state = 'finished';
},
event: function(eventName) {
if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
if(this.options[eventName]) this.options[eventName](this);
},
inspect: function() {
return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
}
}
Effect.Parallel = Class.create();
Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
initialize: function(effects) {
this.effects = effects || [];
this.start(arguments[1]);
},
update: function(position) {
this.effects.invoke('render', position);
},
finish: function(position) {
this.effects.each( function(effect) {
effect.render(1.0);
effect.cancel();
effect.event('beforeFinish');
if(effect.finish) effect.finish(position);
effect.event('afterFinish');
});
}
});
Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
// make this work on IE on elements without 'layout'
if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
this.element.setStyle({zoom: 1});
var options = Object.extend({
from: this.element.getOpacity() || 0.0,
to: 1.0
}, arguments[1] || {});
this.start(options);
},
update: function(position) {
this.element.setOpacity(position);
}
});
Effect.Move = Class.create();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
var options = Object.extend({
x: 0,
y: 0,
mode: 'relative'
}, arguments[1] || {});
this.start(options);
},
setup: function() {
// Bug in Opera: Opera returns the "real" position of a static element or
// relative element that does not have top/left explicitly set.
// ==> Always set top and left for position relative elements in your stylesheets
// (to 0 if you do not need them)
this.element.makePositioned();
this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
this.originalTop = parseFloat(this.element.getStyle('top') || '0');
if(this.options.mode == 'absolute') {
// absolute movement, so we need to calc deltaX and deltaY
this.options.x = this.options.x - this.originalLeft;
this.options.y = this.options.y - this.originalTop;
}
},
update: function(position) {
this.element.setStyle({
left: this.options.x * position + this.originalLeft + 'px',
top: this.options.y * position + this.originalTop + 'px'
});
}
});
// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
return new Effect.Move(element,
Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
};
Effect.Scale = Class.create();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
initialize: function(element, percent) {
this.element = $(element)
var options = Object.extend({
scaleX: true,
scaleY: true,
scaleContent: true,
scaleFromCenter: false,
scaleMode: 'box', // 'box' or 'contents' or {} with provided values
scaleFrom: 100.0,
scaleTo: percent
}, arguments[2] || {});
this.start(options);
},
setup: function() {
this.restoreAfterFinish = this.options.restoreAfterFinish || false;
this.elementPositioning = this.element.getStyle('position');
this.originalStyle = {};
['top','left','width','height','fontSize'].each( function(k) {
this.originalStyle[k] = this.element.style[k];
}.bind(this));
this.originalTop = this.element.offsetTop;
this.originalLeft = this.element.offsetLeft;
var fontSize = this.element.getStyle('font-size') || '100%';
['em','px','%'].each( function(fontSizeType) {
if(fontSize.indexOf(fontSizeType)>0) {
this.fontSize = parseFloat(fontSize);
this.fontSizeType = fontSizeType;
}
}.bind(this));
this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
this.dims = null;
if(this.options.scaleMode=='box')
this.dims = [this.element.offsetHeight, this.element.offsetWidth];
if(/^content/.test(this.options.scaleMode))
this.dims = [this.element.scrollHeight, this.element.scrollWidth];
if(!this.dims)
this.dims = [this.options.scaleMode.originalHeight,
this.options.scaleMode.originalWidth];
},
update: function(position) {
var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
if(this.options.scaleContent && this.fontSize)
this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
},
finish: function(position) {
if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
},
setDimensions: function(height, width) {
var d = {};
if(this.options.scaleX) d.width = width + 'px';
if(this.options.scaleY) d.height = height + 'px';
if(this.options.scaleFromCenter) {
var topd = (height - this.dims[0])/2;
var leftd = (width - this.dims[1])/2;
if(this.elementPositioning == 'absolute') {
if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
} else {
if(this.options.scaleY) d.top = -topd + 'px';
if(this.options.scaleX) d.left = -leftd + 'px';
}
}
this.element.setStyle(d);
}
});
Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
this.start(options);
},
setup: function() {
// Prevent executing on elements not in the layout flow
if(this.element.getStyle('display')=='none') { this.cancel(); return; }
// Disable background image during the effect
this.oldStyle = {
backgroundImage: this.element.getStyle('background-image') };
this.element.setStyle({backgroundImage: 'none'});
if(!this.options.endcolor)
this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
if(!this.options.restorecolor)
this.options.restorecolor = this.element.getStyle('background-color');
// init color calculations
this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
},
update: function(position) {
this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
},
finish: function() {
this.element.setStyle(Object.extend(this.oldStyle, {
backgroundColor: this.options.restorecolor
}));
}
});
Effect.ScrollTo = Class.create();
Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
initialize: function(element) {
this.element = $(element);
this.start(arguments[1] || {});
},
setup: function() {
Position.prepare();
var offsets = Position.cumulativeOffset(this.element);
if(this.options.offset) offsets[1] += this.options.offset;
var max = window.innerHeight ?
window.height - window.innerHeight :
document.body.scrollHeight -
(document.documentElement.clientHeight ?
document.documentElement.clientHeight : document.body.clientHeight);
this.scrollStart = Position.deltaY;
this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
},
update: function(position) {
Position.prepare();
window.scrollTo(Position.deltaX,
this.scrollStart + (position*this.delta));
}
});
/* ------------- combination effects ------------- */
Effect.Fade = function(element) {
element = $(element);
var oldOpacity = element.getInlineOpacity();
var options = Object.extend({
from: element.getOpacity() || 1.0,
to: 0.0,
afterFinishInternal: function(effect) {
if(effect.options.to!=0) return;
effect.element.hide();
effect.element.setStyle({opacity: oldOpacity});
}}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
Effect.Appear = function(element) {
element = $(element);
var options = Object.extend({
from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
to: 1.0,
// force Safari to render floated elements properly
afterFinishInternal: function(effect) {
effect.element.forceRerendering();
},
beforeSetup: function(effect) {
effect.element.setOpacity(effect.options.from);
effect.element.show();
}}, arguments[1] || {});
return new Effect.Opacity(element,options);
}
Effect.Puff = function(element) {
element = $(element);
var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
return new Effect.Parallel(
[ new Effect.Scale(element, 200,
{ sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
Object.extend({ duration: 1.0,
beforeSetupInternal: function(effect) {
effect.effects[0].element.setStyle({position: 'absolute'}); },
afterFinishInternal: function(effect) {
effect.effects[0].element.hide();
effect.effects[0].element.setStyle(oldStyle); }
}, arguments[1] || {})
);
}
Effect.BlindUp = function(element) {
element = $(element);
element.makeClipping();
return new Effect.Scale(element, 0,
Object.extend({ scaleContent: false,
scaleX: false,
restoreAfterFinish: true,
afterFinishInternal: function(effect) {
effect.element.hide();
effect.element.undoClipping();
}
}, arguments[1] || {})
);
}
Effect.BlindDown = function(element) {
element = $(element);
var elementDimensions = element.getDimensions();
return new Effect.Scale(element, 100,
Object.extend({ scaleContent: false,
scaleX: false,
scaleFrom: 0,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
effect.element.makeClipping();
effect.element.setStyle({height: '0px'});
effect.element.show();
},
afterFinishInternal: function(effect) {
effect.element.undoClipping();
}
}, arguments[1] || {})
);
}
Effect.SwitchOff = function(element) {
element = $(element);
var oldOpacity = element.getInlineOpacity();
return new Effect.Appear(element, {
duration: 0.4,
from: 0,
transition: Effect.Transitions.flicker,
afterFinishInternal: function(effect) {
new Effect.Scale(effect.element, 1, {
duration: 0.3, scaleFromCenter: true,
scaleX: false, scaleContent: false, restoreAfterFinish: true,
beforeSetup: function(effect) {
effect.element.makePositioned();
effect.element.makeClipping();
},
afterFinishInternal: function(effect) {
effect.element.hide();
effect.element.undoClipping();
effect.element.undoPositioned();
effect.element.setStyle({opacity: oldOpacity});
}
})
}
});
}
Effect.DropOut = function(element) {
element = $(element);
var oldStyle = {
top: element.getStyle('top'),
left: element.getStyle('left'),
opacity: element.getInlineOpacity() };
return new Effect.Parallel(
[ new Effect.Move(element, {x: 0, y: 100, sync: true }),
new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
Object.extend(
{ duration: 0.5,
beforeSetup: function(effect) {
effect.effects[0].element.makePositioned();
},
afterFinishInternal: function(effect) {
effect.effects[0].element.hide();
effect.effects[0].element.undoPositioned();
effect.effects[0].element.setStyle(oldStyle);
}
}, arguments[1] || {}));
}
Effect.Shake = function(element) {
element = $(element);
var oldStyle = {
top: element.getStyle('top'),
left: element.getStyle('left') };
return new Effect.Move(element,
{ x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
new Effect.Move(effect.element,
{ x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
effect.element.undoPositioned();
effect.element.setStyle(oldStyle);
}}) }}) }}) }}) }}) }});
}
Effect.SlideDown = function(element) {
element = $(element);
element.cleanWhitespace();
// SlideDown need to have the content of the element wrapped in a container element with fixed height!
var oldInnerBottom = $(element.firstChild).getStyle('bottom');
var elementDimensions = element.getDimensions();
return new Effect.Scale(element, 100, Object.extend({
scaleContent: false,
scaleX: false,
scaleFrom: window.opera ? 0 : 1,
scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
restoreAfterFinish: true,
afterSetup: function(effect) {
effect.element.makePositioned();
effect.element.firstChild.makePositioned();
if(window.opera) effect.element.setStyle({top: ''});
effect.element.makeClipping();
effect.element.setStyle({height: '0px'});
effect.element.show(); },
afterUpdateInternal: function(effect) {
effect.element.firstChild.setStyle({bottom:
(effect.dims[0] - effect.element.clientHeight) + 'px' });
},
afterFinishInternal: function(effect) {
effect.element.undoClipping();
// IE will crash if child is undoPositioned first
if(/MSIE/.test(navigator.userAgent)){
effect.element.undoPositioned();
effect.element.firstChild.undoPositioned();
}else{
effect.element.firstChild.undoPositioned();
effect.element.undoPositioned();
}
effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
}, arguments[1] || {})
);
}
Effect.SlideUp = function(element) {
element = $(element);
element.cleanWhitespace();
var oldInnerBottom = $(element.firstChild).getStyle('bottom');
return new Effect.Scale(element, window.opera ? 0 : 1,
Object.extend({ scaleContent: false,
scaleX: false,
scaleMode: 'box',
scaleFrom: 100,
restoreAfterFinish: true,
beforeStartInternal: function(effect) {
effect.element.makePositioned();
effect.element.firstChild.makePositioned();
if(window.opera) effect.element.setStyle({top: ''});
effect.element.makeClipping();
effect.element.show(); },
afterUpdateInternal: function(effect) {
effect.element.firstChild.setStyle({bottom:
(effect.dims[0] - effect.element.clientHeight) + 'px' }); },
afterFinishInternal: function(effect) {
effect.element.hide();
effect.element.undoClipping();
effect.element.firstChild.undoPositioned();
effect.element.undoPositioned();
effect.element.setStyle({bottom: oldInnerBottom}); }
}, arguments[1] || {})
);
}
// Bug in opera makes the TD containing this element expand for a instance after finish
Effect.Squish = function(element) {
return new Effect.Scale(element, window.opera ? 1 : 0,
{ restoreAfterFinish: true,
beforeSetup: function(effect) {
effect.element.makeClipping(effect.element); },
afterFinishInternal: function(effect) {
effect.element.hide(effect.element);
effect.element.undoClipping(effect.element); }
});
}
Effect.Grow = function(element) {
element = $(element);
var options = Object.extend({
direction: 'center',
moveTransition: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.full
}, arguments[1] || {});
var oldStyle = {
top: element.style.top,
left: element.style.left,
height: element.style.height,
width: element.style.width,
opacity: element.getInlineOpacity() };
var dims = element.getDimensions();
var initialMoveX, initialMoveY;
var moveX, moveY;
switch (options.direction) {
case 'top-left':
initialMoveX = initialMoveY = moveX = moveY = 0;
break;
case 'top-right':
initialMoveX = dims.width;
initialMoveY = moveY = 0;
moveX = -dims.width;
break;
case 'bottom-left':
initialMoveX = moveX = 0;
initialMoveY = dims.height;
moveY = -dims.height;
break;
case 'bottom-right':
initialMoveX = dims.width;
initialMoveY = dims.height;
moveX = -dims.width;
moveY = -dims.height;
break;
case 'center':
initialMoveX = dims.width / 2;
initialMoveY = dims.height / 2;
moveX = -dims.width / 2;
moveY = -dims.height / 2;
break;
}
return new Effect.Move(element, {
x: initialMoveX,
y: initialMoveY,
duration: 0.01,
beforeSetup: function(effect) {
effect.element.hide();
effect.element.makeClipping();
effect.element.makePositioned();
},
afterFinishInternal: function(effect) {
new Effect.Parallel(
[ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
new Effect.Scale(effect.element, 100, {
scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
], Object.extend({
beforeSetup: function(effect) {
effect.effects[0].element.setStyle({height: '0px'});
effect.effects[0].element.show();
},
afterFinishInternal: function(effect) {
effect.effects[0].element.undoClipping();
effect.effects[0].element.undoPositioned();
effect.effects[0].element.setStyle(oldStyle);
}
}, options)
)
}
});
}
Effect.Shrink = function(element) {
element = $(element);
var options = Object.extend({
direction: 'center',
moveTransition: Effect.Transitions.sinoidal,
scaleTransition: Effect.Transitions.sinoidal,
opacityTransition: Effect.Transitions.none
}, arguments[1] || {});
var oldStyle = {
top: element.style.top,
left: element.style.left,
height: element.style.height,
width: element.style.width,
opacity: element.getInlineOpacity() };
var dims = element.getDimensions();
var moveX, moveY;
switch (options.direction) {
case 'top-left':
moveX = moveY = 0;
break;
case 'top-right':
moveX = dims.width;
moveY = 0;
break;
case 'bottom-left':
moveX = 0;
moveY = dims.height;
break;
case 'bottom-right':
moveX = dims.width;
moveY = dims.height;
break;
case 'center':
moveX = dims.width / 2;
moveY = dims.height / 2;
break;
}
return new Effect.Parallel(
[ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
], Object.extend({
beforeStartInternal: function(effect) {
effect.effects[0].element.makePositioned();
effect.effects[0].element.makeClipping(); },
afterFinishInternal: function(effect) {
effect.effects[0].element.hide();
effect.effects[0].element.undoClipping();
effect.effects[0].element.undoPositioned();
effect.effects[0].element.setStyle(oldStyle); }
}, options)
);
}
Effect.Pulsate = function(element) {
element = $(element);
var options = arguments[1] || {};
var oldOpacity = element.getInlineOpacity();
var transition = options.transition || Effect.Transitions.sinoidal;
var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
reverser.bind(transition);
return new Effect.Opacity(element,
Object.extend(Object.extend({ duration: 3.0, from: 0,
afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
}, options), {transition: reverser}));
}
Effect.Fold = function(element) {
element = $(element);
var oldStyle = {
top: element.style.top,
left: element.style.left,
width: element.style.width,
height: element.style.height };
Element.makeClipping(element);
return new Effect.Scale(element, 5, Object.extend({
scaleContent: false,
scaleX: false,
afterFinishInternal: function(effect) {
new Effect.Scale(element, 1, {
scaleContent: false,
scaleY: false,
afterFinishInternal: function(effect) {
effect.element.hide();
effect.element.undoClipping();
effect.element.setStyle(oldStyle);
} });
}}, arguments[1] || {}));
};
['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(
function(f) { Element.Methods[f] = Element[f]; }
);
Element.Methods.visualEffect = function(element, effect, options) {
s = effect.gsub(/_/, '-').camelize();
effect_class = s.charAt(0).toUpperCase() + s.substring(1);
new Effect[effect_class](element, options);
return $(element);
};
Element.addMethods();

2006
library/cropper/lib/prototype.js vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
var Scriptaculous = {
Version: '1.6.1',
require: function(libraryName) {
// inserting via DOM fails in Safari 2.0, so brute force approach
document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
},
load: function() {
if((typeof Prototype=='undefined') ||
(typeof Element == 'undefined') ||
(typeof Element.Methods=='undefined') ||
parseFloat(Prototype.Version.split(".")[0] + "." +
Prototype.Version.split(".")[1]) < 1.5)
throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
$A(document.getElementsByTagName("script")).findAll( function(s) {
return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
}).each( function(s) {
var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
var includes = s.src.match(/\?.*load=([a-z,]*)/);
(includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
function(include) { Scriptaculous.require(path+include+'.js') });
});
}
}
Scriptaculous.load();

View file

@ -0,0 +1,283 @@
// Copyright (c) 2005 Marty Haught, Thomas Fuchs
//
// See http://script.aculo.us for more info
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
if(!Control) var Control = {};
Control.Slider = Class.create();
// options:
// axis: 'vertical', or 'horizontal' (default)
//
// callbacks:
// onChange(value)
// onSlide(value)
Control.Slider.prototype = {
initialize: function(handle, track, options) {
var slider = this;
if(handle instanceof Array) {
this.handles = handle.collect( function(e) { return $(e) });
} else {
this.handles = [$(handle)];
}
this.track = $(track);
this.options = options || {};
this.axis = this.options.axis || 'horizontal';
this.increment = this.options.increment || 1;
this.step = parseInt(this.options.step || '1');
this.range = this.options.range || $R(0,1);
this.value = 0; // assure backwards compat
this.values = this.handles.map( function() { return 0 });
this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
this.options.startSpan = $(this.options.startSpan || null);
this.options.endSpan = $(this.options.endSpan || null);
this.restricted = this.options.restricted || false;
this.maximum = this.options.maximum || this.range.end;
this.minimum = this.options.minimum || this.range.start;
// Will be used to align the handle onto the track, if necessary
this.alignX = parseInt(this.options.alignX || '0');
this.alignY = parseInt(this.options.alignY || '0');
this.trackLength = this.maximumOffset() - this.minimumOffset();
this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
this.active = false;
this.dragging = false;
this.disabled = false;
if(this.options.disabled) this.setDisabled();
// Allowed values array
this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
if(this.allowedValues) {
this.minimum = this.allowedValues.min();
this.maximum = this.allowedValues.max();
}
this.eventMouseDown = this.startDrag.bindAsEventListener(this);
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
this.eventMouseMove = this.update.bindAsEventListener(this);
// Initialize handles in reverse (make sure first handle is active)
this.handles.each( function(h,i) {
i = slider.handles.length-1-i;
slider.setValue(parseFloat(
(slider.options.sliderValue instanceof Array ?
slider.options.sliderValue[i] : slider.options.sliderValue) ||
slider.range.start), i);
Element.makePositioned(h); // fix IE
Event.observe(h, "mousedown", slider.eventMouseDown);
});
Event.observe(this.track, "mousedown", this.eventMouseDown);
Event.observe(document, "mouseup", this.eventMouseUp);
Event.observe(document, "mousemove", this.eventMouseMove);
this.initialized = true;
},
dispose: function() {
var slider = this;
Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
Event.stopObserving(document, "mouseup", this.eventMouseUp);
Event.stopObserving(document, "mousemove", this.eventMouseMove);
this.handles.each( function(h) {
Event.stopObserving(h, "mousedown", slider.eventMouseDown);
});
},
setDisabled: function(){
this.disabled = true;
},
setEnabled: function(){
this.disabled = false;
},
getNearestValue: function(value){
if(this.allowedValues){
if(value >= this.allowedValues.max()) return(this.allowedValues.max());
if(value <= this.allowedValues.min()) return(this.allowedValues.min());
var offset = Math.abs(this.allowedValues[0] - value);
var newValue = this.allowedValues[0];
this.allowedValues.each( function(v) {
var currentOffset = Math.abs(v - value);
if(currentOffset <= offset){
newValue = v;
offset = currentOffset;
}
});
return newValue;
}
if(value > this.range.end) return this.range.end;
if(value < this.range.start) return this.range.start;
return value;
},
setValue: function(sliderValue, handleIdx){
if(!this.active) {
this.activeHandle = this.handles[handleIdx];
this.activeHandleIdx = handleIdx;
this.updateStyles();
}
handleIdx = handleIdx || this.activeHandleIdx || 0;
if(this.initialized && this.restricted) {
if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
sliderValue = this.values[handleIdx-1];
if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
sliderValue = this.values[handleIdx+1];
}
sliderValue = this.getNearestValue(sliderValue);
this.values[handleIdx] = sliderValue;
this.value = this.values[0]; // assure backwards compat
this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
this.translateToPx(sliderValue);
this.drawSpans();
if(!this.dragging || !this.event) this.updateFinished();
},
setValueBy: function(delta, handleIdx) {
this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
handleIdx || this.activeHandleIdx || 0);
},
translateToPx: function(value) {
return Math.round(
((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
(value - this.range.start)) + "px";
},
translateToValue: function(offset) {
return ((offset/(this.trackLength-this.handleLength) *
(this.range.end-this.range.start)) + this.range.start);
},
getRange: function(range) {
var v = this.values.sortBy(Prototype.K);
range = range || 0;
return $R(v[range],v[range+1]);
},
minimumOffset: function(){
return(this.isVertical() ? this.alignY : this.alignX);
},
maximumOffset: function(){
return(this.isVertical() ?
this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX);
},
isVertical: function(){
return (this.axis == 'vertical');
},
drawSpans: function() {
var slider = this;
if(this.spans)
$R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
if(this.options.startSpan)
this.setSpan(this.options.startSpan,
$R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
if(this.options.endSpan)
this.setSpan(this.options.endSpan,
$R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
},
setSpan: function(span, range) {
if(this.isVertical()) {
span.style.top = this.translateToPx(range.start);
span.style.height = this.translateToPx(range.end - range.start + this.range.start);
} else {
span.style.left = this.translateToPx(range.start);
span.style.width = this.translateToPx(range.end - range.start + this.range.start);
}
},
updateStyles: function() {
this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
Element.addClassName(this.activeHandle, 'selected');
},
startDrag: function(event) {
if(Event.isLeftClick(event)) {
if(!this.disabled){
this.active = true;
var handle = Event.element(event);
var pointer = [Event.pointerX(event), Event.pointerY(event)];
if(handle==this.track) {
var offsets = Position.cumulativeOffset(this.track);
this.event = event;
this.setValue(this.translateToValue(
(this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
));
var offsets = Position.cumulativeOffset(this.activeHandle);
this.offsetX = (pointer[0] - offsets[0]);
this.offsetY = (pointer[1] - offsets[1]);
} else {
// find the handle (prevents issues with Safari)
while((this.handles.indexOf(handle) == -1) && handle.parentNode)
handle = handle.parentNode;
this.activeHandle = handle;
this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
this.updateStyles();
var offsets = Position.cumulativeOffset(this.activeHandle);
this.offsetX = (pointer[0] - offsets[0]);
this.offsetY = (pointer[1] - offsets[1]);
}
}
Event.stop(event);
}
},
update: function(event) {
if(this.active) {
if(!this.dragging) this.dragging = true;
this.draw(event);
// fix AppleWebKit rendering
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
Event.stop(event);
}
},
draw: function(event) {
var pointer = [Event.pointerX(event), Event.pointerY(event)];
var offsets = Position.cumulativeOffset(this.track);
pointer[0] -= this.offsetX + offsets[0];
pointer[1] -= this.offsetY + offsets[1];
this.event = event;
this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
if(this.initialized && this.options.onSlide)
this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
},
endDrag: function(event) {
if(this.active && this.dragging) {
this.finishDrag(event, true);
Event.stop(event);
}
this.active = false;
this.dragging = false;
},
finishDrag: function(event, success) {
this.active = false;
this.dragging = false;
this.updateFinished();
},
updateFinished: function() {
if(this.initialized && this.options.onChange)
this.options.onChange(this.values.length>1 ? this.values : this.value, this);
this.event = null;
}
}

View file

@ -0,0 +1,383 @@
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005 Jon Tirsen (http://www.tirsen.com)
// (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// experimental, Firefox-only
Event.simulateMouse = function(element, eventName) {
var options = Object.extend({
pointerX: 0,
pointerY: 0,
buttons: 0
}, arguments[2] || {});
var oEvent = document.createEvent("MouseEvents");
oEvent.initMouseEvent(eventName, true, true, document.defaultView,
options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
false, false, false, false, 0, $(element));
if(this.mark) Element.remove(this.mark);
this.mark = document.createElement('div');
this.mark.appendChild(document.createTextNode(" "));
document.body.appendChild(this.mark);
this.mark.style.position = 'absolute';
this.mark.style.top = options.pointerY + "px";
this.mark.style.left = options.pointerX + "px";
this.mark.style.width = "5px";
this.mark.style.height = "5px;";
this.mark.style.borderTop = "1px solid red;"
this.mark.style.borderLeft = "1px solid red;"
if(this.step)
alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
$(element).dispatchEvent(oEvent);
};
// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
// You need to downgrade to 1.0.4 for now to get this working
// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
Event.simulateKey = function(element, eventName) {
var options = Object.extend({
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
keyCode: 0,
charCode: 0
}, arguments[2] || {});
var oEvent = document.createEvent("KeyEvents");
oEvent.initKeyEvent(eventName, true, true, window,
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
options.keyCode, options.charCode );
$(element).dispatchEvent(oEvent);
};
Event.simulateKeys = function(element, command) {
for(var i=0; i<command.length; i++) {
Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
}
};
var Test = {}
Test.Unit = {};
// security exception workaround
Test.Unit.inspect = Object.inspect;
Test.Unit.Logger = Class.create();
Test.Unit.Logger.prototype = {
initialize: function(log) {
this.log = $(log);
if (this.log) {
this._createLogTable();
}
},
start: function(testName) {
if (!this.log) return;
this.testName = testName;
this.lastLogLine = document.createElement('tr');
this.statusCell = document.createElement('td');
this.nameCell = document.createElement('td');
this.nameCell.appendChild(document.createTextNode(testName));
this.messageCell = document.createElement('td');
this.lastLogLine.appendChild(this.statusCell);
this.lastLogLine.appendChild(this.nameCell);
this.lastLogLine.appendChild(this.messageCell);
this.loglines.appendChild(this.lastLogLine);
},
finish: function(status, summary) {
if (!this.log) return;
this.lastLogLine.className = status;
this.statusCell.innerHTML = status;
this.messageCell.innerHTML = this._toHTML(summary);
},
message: function(message) {
if (!this.log) return;
this.messageCell.innerHTML = this._toHTML(message);
},
summary: function(summary) {
if (!this.log) return;
this.logsummary.innerHTML = this._toHTML(summary);
},
_createLogTable: function() {
this.log.innerHTML =
'<div id="logsummary"></div>' +
'<table id="logtable">' +
'<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
'<tbody id="loglines"></tbody>' +
'</table>';
this.logsummary = $('logsummary')
this.loglines = $('loglines');
},
_toHTML: function(txt) {
return txt.escapeHTML().replace(/\n/g,"<br/>");
}
}
Test.Unit.Runner = Class.create();
Test.Unit.Runner.prototype = {
initialize: function(testcases) {
this.options = Object.extend({
testLog: 'testlog'
}, arguments[1] || {});
this.options.resultsURL = this.parseResultsURLQueryParameter();
if (this.options.testLog) {
this.options.testLog = $(this.options.testLog) || null;
}
if(this.options.tests) {
this.tests = [];
for(var i = 0; i < this.options.tests.length; i++) {
if(/^test/.test(this.options.tests[i])) {
this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
}
}
} else {
if (this.options.test) {
this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
} else {
this.tests = [];
for(var testcase in testcases) {
if(/^test/.test(testcase)) {
this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"]));
}
}
}
}
this.currentTest = 0;
this.logger = new Test.Unit.Logger(this.options.testLog);
setTimeout(this.runTests.bind(this), 1000);
},
parseResultsURLQueryParameter: function() {
return window.location.search.parseQuery()["resultsURL"];
},
// Returns:
// "ERROR" if there was an error,
// "FAILURE" if there was a failure, or
// "SUCCESS" if there was neither
getResult: function() {
var hasFailure = false;
for(var i=0;i<this.tests.length;i++) {
if (this.tests[i].errors > 0) {
return "ERROR";
}
if (this.tests[i].failures > 0) {
hasFailure = true;
}
}
if (hasFailure) {
return "FAILURE";
} else {
return "SUCCESS";
}
},
postResults: function() {
if (this.options.resultsURL) {
new Ajax.Request(this.options.resultsURL,
{ method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
}
},
runTests: function() {
var test = this.tests[this.currentTest];
if (!test) {
// finished!
this.postResults();
this.logger.summary(this.summary());
return;
}
if(!test.isWaiting) {
this.logger.start(test.name);
}
test.run();
if(test.isWaiting) {
this.logger.message("Waiting for " + test.timeToWait + "ms");
setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
} else {
this.logger.finish(test.status(), test.summary());
this.currentTest++;
// tail recursive, hopefully the browser will skip the stackframe
this.runTests();
}
},
summary: function() {
var assertions = 0;
var failures = 0;
var errors = 0;
var messages = [];
for(var i=0;i<this.tests.length;i++) {
assertions += this.tests[i].assertions;
failures += this.tests[i].failures;
errors += this.tests[i].errors;
}
return (
this.tests.length + " tests, " +
assertions + " assertions, " +
failures + " failures, " +
errors + " errors");
}
}
Test.Unit.Assertions = Class.create();
Test.Unit.Assertions.prototype = {
initialize: function() {
this.assertions = 0;
this.failures = 0;
this.errors = 0;
this.messages = [];
},
summary: function() {
return (
this.assertions + " assertions, " +
this.failures + " failures, " +
this.errors + " errors" + "\n" +
this.messages.join("\n"));
},
pass: function() {
this.assertions++;
},
fail: function(message) {
this.failures++;
this.messages.push("Failure: " + message);
},
info: function(message) {
this.messages.push("Info: " + message);
},
error: function(error) {
this.errors++;
this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
},
status: function() {
if (this.failures > 0) return 'failed';
if (this.errors > 0) return 'error';
return 'passed';
},
assert: function(expression) {
var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
try { expression ? this.pass() :
this.fail(message); }
catch(e) { this.error(e); }
},
assertEqual: function(expected, actual) {
var message = arguments[2] || "assertEqual";
try { (expected == actual) ? this.pass() :
this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
'", actual "' + Test.Unit.inspect(actual) + '"'); }
catch(e) { this.error(e); }
},
assertEnumEqual: function(expected, actual) {
var message = arguments[2] || "assertEnumEqual";
try { $A(expected).length == $A(actual).length &&
expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) +
', actual ' + Test.Unit.inspect(actual)); }
catch(e) { this.error(e); }
},
assertNotEqual: function(expected, actual) {
var message = arguments[2] || "assertNotEqual";
try { (expected != actual) ? this.pass() :
this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
catch(e) { this.error(e); }
},
assertNull: function(obj) {
var message = arguments[1] || 'assertNull'
try { (obj==null) ? this.pass() :
this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
catch(e) { this.error(e); }
},
assertHidden: function(element) {
var message = arguments[1] || 'assertHidden';
this.assertEqual("none", element.style.display, message);
},
assertNotNull: function(object) {
var message = arguments[1] || 'assertNotNull';
this.assert(object != null, message);
},
assertInstanceOf: function(expected, actual) {
var message = arguments[2] || 'assertInstanceOf';
try {
(actual instanceof expected) ? this.pass() :
this.fail(message + ": object was not an instance of the expected type"); }
catch(e) { this.error(e); }
},
assertNotInstanceOf: function(expected, actual) {
var message = arguments[2] || 'assertNotInstanceOf';
try {
!(actual instanceof expected) ? this.pass() :
this.fail(message + ": object was an instance of the not expected type"); }
catch(e) { this.error(e); }
},
_isVisible: function(element) {
element = $(element);
if(!element.parentNode) return true;
this.assertNotNull(element);
if(element.style && Element.getStyle(element, 'display') == 'none')
return false;
return this._isVisible(element.parentNode);
},
assertNotVisible: function(element) {
this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
},
assertVisible: function(element) {
this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
},
benchmark: function(operation, iterations) {
var startAt = new Date();
(iterations || 1).times(operation);
var timeTaken = ((new Date())-startAt);
this.info((arguments[2] || 'Operation') + ' finished ' +
iterations + ' iterations in ' + (timeTaken/1000)+'s' );
return timeTaken;
}
}
Test.Unit.Testcase = Class.create();
Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
initialize: function(name, test, setup, teardown) {
Test.Unit.Assertions.prototype.initialize.bind(this)();
this.name = name;
this.test = test || function() {};
this.setup = setup || function() {};
this.teardown = teardown || function() {};
this.isWaiting = false;
this.timeToWait = 1000;
},
wait: function(time, nextPart) {
this.isWaiting = true;
this.test = nextPart;
this.timeToWait = time;
},
run: function() {
try {
try {
if (!this.isWaiting) this.setup.bind(this)();
this.isWaiting = false;
this.test.bind(this)();
} finally {
if(!this.isWaiting) {
this.teardown.bind(this)();
}
}
}
catch(e) { this.error(e); }
}
});

View file

@ -0,0 +1,12 @@
Copyright (c) 2006, David Spurr (www.defusion.org.uk)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the David Spurr nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
http://www.opensource.org/licenses/bsd-license.php

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View file

@ -0,0 +1,106 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>Basic cropper test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
// setup the callback function
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// basic example
Event.observe(
window,
'load',
function() {
new Cropper.Img(
'testImage',
{
onEndCrop: onEndCrop
}
)
}
);
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
html, body {
margin: 0;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
</style>
</head>
<body>
<h2>Basic cropper test</h2>
<p>
Some test content before the image
</p>
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</body>
</html>

View file

@ -0,0 +1,162 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>CSS - Absolute positioned (and draggable) test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop,effects" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
// setup the callback function
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// Absolute positioned example
Event.observe(
window,
'load',
function() {
new Cropper.Img( 'testAbsImage', { onEndCrop: onEndCrop } );
new Draggable( 'test-abs' );
}
);
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
html, body {
margin: 0;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
#test-abs {
width: 510px;
position: absolute;
top: 50px;
left: 25%;
background-color: #dee;
border: 3px solid #ccc;
z-index: 10;
}
</style>
</head>
<body>
<h2>CSS - Absolute positioned (and draggable) test</h2>
<p>
Some test content before the image
</p>
<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque consequat risus cursus ipsum. Etiam libero. Integer vel mauris. Donec vulputate. In ut augue vitae nibh lobortis tempor. Aliquam hendrerit quam. Phasellus sed orci. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut sed urna. Donec nunc urna, porttitor a, feugiat pellentesque, varius id, justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Sed sollicitudin. Integer enim. Aenean sollicitudin.
</p>
<p>
Integer lorem turpis, dapibus sed, vulputate nec, volutpat a, sem. Sed malesuada laoreet lorem. Duis mauris ipsum, fringilla nec, tristique vel, imperdiet vel, neque. Nulla vel purus. Fusce non lectus. Mauris pulvinar. Curabitur eget eros. Nunc ultrices, risus vitae adipiscing scelerisque, quam mi auctor lacus, non pellentesque augue sapien a magna. Etiam rutrum posuere tortor. Mauris rhoncus sagittis dolor. Donec sed quam. Vivamus vel diam id massa adipiscing bibendum. Suspendisse potenti. Integer arcu est, adipiscing sit amet, convallis eu, sollicitudin tincidunt, quam.
</p>
<p>
Etiam ligula lorem, imperdiet ac, luctus eget, ultrices at, odio. Vivamus malesuada, justo eu adipiscing semper, nisi dui tempus magna, quis ultrices nunc tellus id massa. Nullam lobortis auctor sapien. Quisque non nulla. Donec lobortis pellentesque nisl. Sed lacus sapien, viverra vitae, blandit ut, fermentum quis, leo. Morbi augue turpis, hendrerit non, feugiat vel, laoreet sed, est. Nunc velit. Praesent lobortis. Integer enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur faucibus lacus ac ante. Donec odio odio, tincidunt a, egestas nec, scelerisque nec, dui. Cras sollicitudin. Donec lacus enim, mollis sit amet, interdum quis, euismod et, nulla. Nunc sit amet dui eu magna dapibus mollis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla facilisi.
</p>
<p>
In hac habitasse platea dictumst. Nunc neque urna, dapibus ut, tristique ut, bibendum ac, felis. Donec dictum est ut dolor. Etiam accumsan, velit sit amet blandit vestibulum, turpis quam hendrerit risus, vel interdum eros orci in nunc. Curabitur tellus sapien, rutrum ac, euismod ac, malesuada nec, pede. Proin sit amet ipsum. Praesent quam nisl, adipiscing nec, tristique eget, fermentum sed, est. Praesent ac est sit amet orci facilisis placerat. Sed consequat, est sit amet consectetuer viverra, risus urna porttitor tellus, ut convallis nibh libero in lectus. Pellentesque molestie, erat non vehicula pretium, turpis nisi eleifend eros, sed scelerisque tortor odio non tellus. Nunc leo tellus, faucibus vitae, placerat a, accumsan vel, arcu. In et orci. Ut tristique euismod nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Sed nulla nunc, placerat vitae, pellentesque non, interdum non, sapien. Quisque faucibus, eros sed venenatis sagittis, leo risus rhoncus risus, in pretium sem purus a lacus. Aliquam aliquam leo et diam.
</p>
<p>
Nulla sagittis diam. Phasellus vitae enim tristique libero molestie tristique. Nam mauris sem, elementum nec, cursus in, fringilla ac, neque. Nunc metus nisi, dictum vel, vulputate quis, porttitor bibendum, tortor. Vestibulum vehicula. Nulla facilisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla ac magna sed purus ultricies euismod. Aliquam dictum. Sed mauris. Suspendisse justo. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi purus lorem, auctor non, porta ac, vehicula vel, orci. Morbi pharetra massa nec leo. Maecenas et mauris. Aliquam porttitor tincidunt nulla. Vestibulum pede.
</p>
<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque consequat risus cursus ipsum. Etiam libero. Integer vel mauris. Donec vulputate. In ut augue vitae nibh lobortis tempor. Aliquam hendrerit quam. Phasellus sed orci. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut sed urna. Donec nunc urna, porttitor a, feugiat pellentesque, varius id, justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Sed sollicitudin. Integer enim. Aenean sollicitudin.
</p>
<p>
Integer lorem turpis, dapibus sed, vulputate nec, volutpat a, sem. Sed malesuada laoreet lorem. Duis mauris ipsum, fringilla nec, tristique vel, imperdiet vel, neque. Nulla vel purus. Fusce non lectus. Mauris pulvinar. Curabitur eget eros. Nunc ultrices, risus vitae adipiscing scelerisque, quam mi auctor lacus, non pellentesque augue sapien a magna. Etiam rutrum posuere tortor. Mauris rhoncus sagittis dolor. Donec sed quam. Vivamus vel diam id massa adipiscing bibendum. Suspendisse potenti. Integer arcu est, adipiscing sit amet, convallis eu, sollicitudin tincidunt, quam.
</p>
<p>
Etiam ligula lorem, imperdiet ac, luctus eget, ultrices at, odio. Vivamus malesuada, justo eu adipiscing semper, nisi dui tempus magna, quis ultrices nunc tellus id massa. Nullam lobortis auctor sapien. Quisque non nulla. Donec lobortis pellentesque nisl. Sed lacus sapien, viverra vitae, blandit ut, fermentum quis, leo. Morbi augue turpis, hendrerit non, feugiat vel, laoreet sed, est. Nunc velit. Praesent lobortis. Integer enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur faucibus lacus ac ante. Donec odio odio, tincidunt a, egestas nec, scelerisque nec, dui. Cras sollicitudin. Donec lacus enim, mollis sit amet, interdum quis, euismod et, nulla. Nunc sit amet dui eu magna dapibus mollis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla facilisi.
</p>
<p>
In hac habitasse platea dictumst. Nunc neque urna, dapibus ut, tristique ut, bibendum ac, felis. Donec dictum est ut dolor. Etiam accumsan, velit sit amet blandit vestibulum, turpis quam hendrerit risus, vel interdum eros orci in nunc. Curabitur tellus sapien, rutrum ac, euismod ac, malesuada nec, pede. Proin sit amet ipsum. Praesent quam nisl, adipiscing nec, tristique eget, fermentum sed, est. Praesent ac est sit amet orci facilisis placerat. Sed consequat, est sit amet consectetuer viverra, risus urna porttitor tellus, ut convallis nibh libero in lectus. Pellentesque molestie, erat non vehicula pretium, turpis nisi eleifend eros, sed scelerisque tortor odio non tellus. Nunc leo tellus, faucibus vitae, placerat a, accumsan vel, arcu. In et orci. Ut tristique euismod nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Sed nulla nunc, placerat vitae, pellentesque non, interdum non, sapien. Quisque faucibus, eros sed venenatis sagittis, leo risus rhoncus risus, in pretium sem purus a lacus. Aliquam aliquam leo et diam.
</p>
<p>
Nulla sagittis diam. Phasellus vitae enim tristique libero molestie tristique. Nam mauris sem, elementum nec, cursus in, fringilla ac, neque. Nunc metus nisi, dictum vel, vulputate quis, porttitor bibendum, tortor. Vestibulum vehicula. Nulla facilisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla ac magna sed purus ultricies euismod. Aliquam dictum. Sed mauris. Suspendisse justo. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi purus lorem, auctor non, porta ac, vehicula vel, orci. Morbi pharetra massa nec leo. Maecenas et mauris. Aliquam porttitor tincidunt nulla. Vestibulum pede.
</p>
<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Pellentesque consequat risus cursus ipsum. Etiam libero. Integer vel mauris. Donec vulputate. In ut augue vitae nibh lobortis tempor. Aliquam hendrerit quam. Phasellus sed orci. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut sed urna. Donec nunc urna, porttitor a, feugiat pellentesque, varius id, justo. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla facilisi. Sed sollicitudin. Integer enim. Aenean sollicitudin.
</p>
<p>
Integer lorem turpis, dapibus sed, vulputate nec, volutpat a, sem. Sed malesuada laoreet lorem. Duis mauris ipsum, fringilla nec, tristique vel, imperdiet vel, neque. Nulla vel purus. Fusce non lectus. Mauris pulvinar. Curabitur eget eros. Nunc ultrices, risus vitae adipiscing scelerisque, quam mi auctor lacus, non pellentesque augue sapien a magna. Etiam rutrum posuere tortor. Mauris rhoncus sagittis dolor. Donec sed quam. Vivamus vel diam id massa adipiscing bibendum. Suspendisse potenti. Integer arcu est, adipiscing sit amet, convallis eu, sollicitudin tincidunt, quam.
</p>
<p>
Etiam ligula lorem, imperdiet ac, luctus eget, ultrices at, odio. Vivamus malesuada, justo eu adipiscing semper, nisi dui tempus magna, quis ultrices nunc tellus id massa. Nullam lobortis auctor sapien. Quisque non nulla. Donec lobortis pellentesque nisl. Sed lacus sapien, viverra vitae, blandit ut, fermentum quis, leo. Morbi augue turpis, hendrerit non, feugiat vel, laoreet sed, est. Nunc velit. Praesent lobortis. Integer enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Curabitur faucibus lacus ac ante. Donec odio odio, tincidunt a, egestas nec, scelerisque nec, dui. Cras sollicitudin. Donec lacus enim, mollis sit amet, interdum quis, euismod et, nulla. Nunc sit amet dui eu magna dapibus mollis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla facilisi.
</p>
<p>
In hac habitasse platea dictumst. Nunc neque urna, dapibus ut, tristique ut, bibendum ac, felis. Donec dictum est ut dolor. Etiam accumsan, velit sit amet blandit vestibulum, turpis quam hendrerit risus, vel interdum eros orci in nunc. Curabitur tellus sapien, rutrum ac, euismod ac, malesuada nec, pede. Proin sit amet ipsum. Praesent quam nisl, adipiscing nec, tristique eget, fermentum sed, est. Praesent ac est sit amet orci facilisis placerat. Sed consequat, est sit amet consectetuer viverra, risus urna porttitor tellus, ut convallis nibh libero in lectus. Pellentesque molestie, erat non vehicula pretium, turpis nisi eleifend eros, sed scelerisque tortor odio non tellus. Nunc leo tellus, faucibus vitae, placerat a, accumsan vel, arcu. In et orci. Ut tristique euismod nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Sed nulla nunc, placerat vitae, pellentesque non, interdum non, sapien. Quisque faucibus, eros sed venenatis sagittis, leo risus rhoncus risus, in pretium sem purus a lacus. Aliquam aliquam leo et diam.
</p>
<p>
Nulla sagittis diam. Phasellus vitae enim tristique libero molestie tristique. Nam mauris sem, elementum nec, cursus in, fringilla ac, neque. Nunc metus nisi, dictum vel, vulputate quis, porttitor bibendum, tortor. Vestibulum vehicula. Nulla facilisi. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla ac magna sed purus ultricies euismod. Aliquam dictum. Sed mauris. Suspendisse justo. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi purus lorem, auctor non, porta ac, vehicula vel, orci. Morbi pharetra massa nec leo. Maecenas et mauris. Aliquam porttitor tincidunt nulla. Vestibulum pede.
</p>
<div id="test-abs">
<h2>Absolute test</h2>
<div id="testAbsWrap">
<img src="castle.jpg" alt="test image" id="testAbsImage" width="500" height="333" />
</div>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</div>
</body>
</html>

View file

@ -0,0 +1,124 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>CSS - Float test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
// setup the callback function
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// float example
Event.observe(
window,
'load',
function() {
new Cropper.Img(
'testFloatImage',
{
onEndCrop: function( coords, dimensions ) {
$( 'floatX1' ).value = coords.x1;
$( 'floatY1' ).value = coords.y1;
$( 'floatX2' ).value = coords.x2;
$( 'floatY2' ).value = coords.y2;
$( 'floatWidth' ).value = dimensions.width;
$( 'floatHeight' ).value = dimensions.height;
}
}
);
}
);
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
html, body {
margin: 0;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
#test-float {
width: 600px;
float: right;
background-color: #eee;
border: 3px solid #000;
margin: 10px;
padding: 5px;
}
</style>
</head>
<body>
<h2>Test page with floating wrapper</h2>
<p>
Some test content before the image
</p>
<div id="test-float">
<h2>Float test</h2>
<div id="testFloatWrap">
<img src="castle.jpg" alt="test image" id="testFloatImage" width="500" height="333" />
</div>
<p>
<label for="floatX1">x1:</label>
<input type="text" name="floatX1" id="floatX1" />
</p>
<p>
<label for="floatY1">y1:</label>
<input type="text" name="floatY1" id="floatY1" />
</p>
<p>
<label for="floatX2">x2:</label>
<input type="text" name="floatX2" id="floatX2" />
</p>
<p>
<label for="floatY2">y2:</label>
<input type="text" name="floatY2" id="floatY2" />
</p>
<p>
<label for="floatWidth">width:</label>
<input type="text" name="floatWidth" id="floatWidth" />
</p>
<p>
<label for="floatHeight">height</label>
<input type="text" name="floatHeight" id="floatHeight" />
</p>
</div>
</body>
</html>

View file

@ -0,0 +1,116 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>CSS - Relative test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
// setup the callback function
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// relative example
Event.observe(
window,
'load',
function() {
new Cropper.Img(
'testImage',
{
onEndCrop: onEndCrop
}
)
}
);
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
html, body {
margin: 0;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
#test-relative {
background-color: #ccc;
border: 3px solid #ddd;
position: relative;
top: 25px;
left: 25px;
}
</style>
</head>
<body>
<h2>Test page with relatively positioned wrapper</h2>
<p>
Some test content before the image
</p>
<div id="test-relative">
<h2>Relative test</h2>
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</div>
</body>
</html>

View file

@ -0,0 +1,108 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>Loading &amp; displaying co-ordinates of crop area on attachment test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
// setup the callback function
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// basic example
Event.observe(
window,
'load',
function() {
new Cropper.Img(
'testImage',
{
onEndCrop: onEndCrop,
displayOnInit: true,
onloadCoords: { x1: 10, y1: 10, x2: 250, y2: 100 }
}
)
}
);
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
html, body {
margin: 0;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
</style>
</head>
<body>
<h2>Loading &amp; displaying co-ordinates of crop area on attachment test</h2>
<p>
Some test content before the image
</p>
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</body>
</html>

View file

@ -0,0 +1,109 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>Loading &amp; displaying co-ordinates (with ratio) of crop area on attachment test<</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
// setup the callback function
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// basic example
Event.observe(
window,
'load',
function() {
new Cropper.Img(
'testImage',
{
onEndCrop: onEndCrop,
displayOnInit: true,
onloadCoords: { x1: 10, y1: 10, x2: 210, y2: 110 },
ratioDim: { x: 200, y: 100 }
}
)
}
);
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
html, body {
margin: 0;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
</style>
</head>
<body>
<h2>Loading &amp; displaying co-ordinates (with ratio) of crop area on attachment test</h2>
<p>
Some test content before the image
</p>
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</body>
</html>

View file

@ -0,0 +1,225 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>Different dimensions test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
/*
// example with minimum dimensions
Event.observe(
window,
'load',
function() {
new Cropper.Img(
'testImage',
{
minWidth: 200,
minHeight: 120,
maxWidth: 200,
//maxHeight: 120,
displayOnInit: true,
onEndCrop: onEndCrop
}
)
}
);
*/
Event.observe( window, 'load',
function() {
Event.observe( 'dimensionsForm', 'submit', CropManager.attachCropper.bindAsEventListener( CropManager ) );
CropManager.attachCropper();
}
);
/**
* A little manager that allows us to reset the options dynamically
*/
var CropManager = {
/**
* Holds the current Cropper.Img object
* @var obj
*/
curCrop: null,
/**
* Gets a min/max parameter from the form
*
* @access private
* @param string Form element ID
* @return int
*/
getParam: function( name ) {
var val = $F( name );
console.log( name + ' :: ' + val );
return parseInt( val );
},
/**
* Attaches/resets the image cropper
*
* @access private
* @param obj Event object
* @return void
*/
attachCropper: function( e ) {
if( this.curCrop == null ) {
this.curCrop = new Cropper.Img(
'testImage',
{
minWidth: this.getParam( 'minWidth' ),
minHeight: this.getParam( 'minHeight' ),
maxWidth: this.getParam( 'maxWidth' ),
maxHeight: this.getParam( 'maxHeight' ),
onEndCrop: onEndCrop
}
);
} else {
this.removeCropper();
this.curCrop.initialize(
'testImage',
{
minWidth: this.getParam( 'minWidth' ),
minHeight: this.getParam( 'minHeight' ),
maxWidth: this.getParam( 'maxWidth' ),
maxHeight: this.getParam( 'maxHeight' ),
onEndCrop: onEndCrop
}
);
}
if( e != null ) Event.stop( e );
},
/**
* Removes the cropper
*
* @access public
* @return void
*/
removeCropper: function() {
if( this.curCrop != null ) {
this.curCrop.remove();
}
},
/**
* Resets the cropper, either re-setting or re-applying
*
* @access public
* @return void
*/
resetCropper: function() {
this.attachCropper();
}
};
/*
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
// Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
*/
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
#dimensionsForm {
float: right;
width: 350px;
}
</style>
</head>
<body>
<h2>Multiple dimensions tests</h2>
<p>
Test of applying different dimension restrictions to the cropper
</p>
<form action="#" id="dimensionsForm">
<fieldset>
Set the cropper with the following dimension restrictions:
<p>
<label for="minWidth">Min Width</label>
<input type="text" size="10" maxlength="3" value="200" id="minWidth" name="minWidth" />
</p>
<p>
<label for="maxWidth">Max Width</label>
<input type="text" size="10" maxlength="3" value="200" id="maxWidth" name="maxWidth" />
</p>
<p>
<label for="minHeight">Min Height</label>
<input type="text" size="10" maxlength="3" value="120" id="minHeight" name="minHeight" />
</p>
<p>
<label for="maxHeight">Max Height</label>
<input type="text" size="10" maxlength="3" value="120" id="maxHeight" name="maxHeight" />
</p>
<input type="submit" value="Set Cropper" />
</fieldset>
</form>
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</body>
</html>

View file

@ -0,0 +1,203 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>Dynamic image test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
/**
* A little manager that allows us to swap the image dynamically
*
*/
var CropImageManager = {
/**
* Holds the current Cropper.Img object
* @var obj
*/
curCrop: null,
/**
* Initialises the cropImageManager
*
* @access public
* @return void
*/
init: function() {
this.attachCropper();
},
/**
* Handles the changing of the select to change the image, the option value
* is a pipe seperated list of imgSrc|width|height
*
* @access public
* @param obj event
* @return void
*/
onChange: function( e ) {
var vals = $F( Event.element( e ) ).split('|');
this.setImage( vals[0], vals[1], vals[2] );
},
/**
* Sets the image within the element & attaches/resets the image cropper
*
* @access private
* @param string Source path of new image
* @param int Width of new image in pixels
* @param int Height of new image in pixels
* @return void
*/
setImage: function( imgSrc, w, h ) {
$( 'testImage' ).src = imgSrc;
$( 'testImage' ).width = w;
$( 'testImage' ).height = h;
this.attachCropper();
},
/**
* Attaches/resets the image cropper
*
* @access private
* @return void
*/
attachCropper: function() {
if( this.curCrop == null ) this.curCrop = new Cropper.Img( 'testImage', { onEndCrop: onEndCrop } );
else this.curCrop.reset();
},
/**
* Removes the cropper
*
* @access public
* @return void
*/
removeCropper: function() {
if( this.curCrop != null ) {
this.curCrop.remove();
}
},
/**
* Resets the cropper, either re-setting or re-applying
*
* @access public
* @return void
*/
resetCropper: function() {
this.attachCropper();
}
};
// setup the callback function
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// basic example
Event.observe(
window,
'load',
function() {
CropImageManager.init();
Event.observe( $('removeCropper'), 'click', CropImageManager.removeCropper.bindAsEventListener( CropImageManager ), false );
Event.observe( $('resetCropper'), 'click', CropImageManager.resetCropper.bindAsEventListener( CropImageManager ), false );
Event.observe( $('imageChoice'), 'change', CropImageManager.onChange.bindAsEventListener( CropImageManager ), false );
}
);
/*
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
*/
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
html, body {
margin: 0;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
</style>
</head>
<body>
<h2>Dynamic image test</h2>
<p>
Test of dynamically changing images or removing & re-applying the cropper
</p>
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<p>
<label for="imageChoice">image:</label>
<select name="imageChoice" id="imageChoice">
<option value="castle.jpg|500|333">Castle</option>
<option value="poppy.jpg|311|466">Flower</option>
</select>
</p>
<p>
<input type="button" id="removeCropper" value="Remove Cropper" />
<input type="button" id="resetCropper" value="Reset Cropper" />
</p>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</body>
</html>

View file

@ -0,0 +1,104 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>Fixed ratio test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// with a supplied ratio
Event.observe(
window,
'load',
function() {
new Cropper.Img(
'testImage',
{
ratioDim: { x: 220, y: 124 },
displayOnInit: true,
onEndCrop: onEndCrop
}
)
}
);
/*
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
// Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
*/
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
</style>
</head>
<body>
<h2>Fixed ratio test</h2>
<p>
Test of applying a fixed ratio to the cropper
</p>
<br />
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</body>
</html>

View file

@ -0,0 +1,105 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>Min dimensions test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// example with minimum dimensions
Event.observe(
window,
'load',
function() {
new Cropper.Img(
'testImage',
{
minWidth: 200,
minHeight: 120,
displayOnInit: true,
onEndCrop: onEndCrop
}
)
}
);
/*
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
// Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
*/
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
</style>
</head>
<body>
<h2>Minimum (both axes ) dimension test</h2>
<p>
Test of applying a minimum dimension to both axes to the cropper
</p>
<br />
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</body>
</html>

View file

@ -0,0 +1,105 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title>Min (single axis) dimensions test</title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// example with minimum dimensions
Event.observe(
window,
'load',
function() {
new Cropper.Img(
'testImage',
{
minWidth: 200,
displayOnInit: true,
onEndCrop: onEndCrop
}
)
}
);
/*
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
// Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
*/
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
#testWrap {
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
</style>
</head>
<body>
<h2>Minimum (single axis) dimension test</h2>
<p>
Test of applying a minimum dimension to only one axis (width in this case) to the cropper
</p>
<br />
<br /><br />
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</body>
</html>

View file

@ -0,0 +1,117 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title></title>
<script src="../lib/prototype.js" type="text/javascript"></script>
<script src="../lib/scriptaculous.js?load=builder,dragdrop" type="text/javascript"></script>
<script src="../cropper.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
function onEndCrop( coords, dimensions ) {
$( 'x1' ).value = coords.x1;
$( 'y1' ).value = coords.y1;
$( 'x2' ).value = coords.x2;
$( 'y2' ).value = coords.y2;
$( 'width' ).value = dimensions.width;
$( 'height' ).value = dimensions.height;
}
// example with a preview of crop results, must have minimumm dimensions
Event.observe(
window,
'load',
function() {
new Cropper.ImgWithPreview(
'testImage',
{
minWidth: 200,
minHeight: 120,
ratioDim: { x: 200, y: 120 },
displayOnInit: true,
onEndCrop: onEndCrop,
previewWrap: 'previewArea'
}
)
}
);
/*
if( typeof(dump) != 'function' ) {
Debug.init(true, '/');
function dump( msg ) {
// Debug.raise( msg );
};
} else dump( '---------------------------------------\n' );
*/
</script>
<link rel="stylesheet" type="text/css" href="debug.css" media="all" />
<style type="text/css">
label {
clear: left;
margin-left: 50px;
float: left;
width: 5em;
}
#testWrap {
width: 500px;
float: left;
margin: 20px 0 0 50px; /* Just while testing, to make sure we return the correct positions for the image & not the window */
}
#previewArea {
margin: 20px; 0 0 20px;
float: left;
}
#results {
clear: both;
}
</style>
</head>
<body>
<br /><br />
<div id="testWrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
</div>
<div id="previewArea"></div>
<div id="results">
<p>
<label for="x1">x1:</label>
<input type="text" name="x1" id="x1" />
</p>
<p>
<label for="y1">y1:</label>
<input type="text" name="y1" id="y1" />
</p>
<p>
<label for="x2">x2:</label>
<input type="text" name="x2" id="x2" />
</p>
<p>
<label for="y2">y2:</label>
<input type="text" name="y2" id="y2" />
</p>
<p>
<label for="width">width:</label>
<input type="text" name="width" id="width" />
</p>
<p>
<label for="height">height</label>
<input type="text" name="height" id="height" />
</p>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,236 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<title></title>
</head>
<body>
<!--
This is a static test file for the HTML & CSS structure employed, tested in
the following browsers:
PC:
IE 6: working
IE 5.5: working
IE 5.0: opacity issues
FF 1.5: working
Opera 9: working
MAC:
Camino 1.0: working
FF 1.5: working
Safari 2.0: working
-->
<style type="text/css">
.imgCrop_wrap {
width: 500px; /* @TODO IN JS */
height: 333px; /* @TODO IN JS */
position: relative;
cursor: crosshair;
}
/* fix for IE displaying all boxes at line-height by default */
.imgCrop_wrap,
.imgCrop_wrap * {
font-size: 0;
}
.imgCrop_overlay {
background-color: #000;
opacity: 0.5;
filter:alpha(opacity=50);
position: absolute;
width: 100%;
height: 100%;
}
.imgCrop_selArea {
position: absolute;
cursor: move;
/* @TODO: rest to be done via JS when selecting areas */
top: 110px;
left: 210px;
width: 200px;
height: 200px;
z-index: 2;
background: transparent url(castle.jpg) no-repeat -210px -110px;
}
/* imgCrop_clickArea is all a fix for IE 5.5 & 6 to allow the user to click on the given area */
.imgCrop_clickArea {
width: 100%;
height: 100%;
background-color: #FFF;
opacity: 0.01;
filter:alpha(opacity=01);
}
.imgCrop_marqueeHoriz {
position: absolute;
width: 100%;
height: 1px;
background: transparent url(marqueeHoriz.gif) repeat-x 0 0;
}
.imgCrop_marqueeVert {
position: absolute;
height: 100%;
width: 1px;
background: transparent url(marqueeVert.gif) repeat-y 0 0;
}
.imgCrop_marqueeNorth { top: 0; left: 0; }
.imgCrop_marqueeEast { top: 0; right: 0; }
.imgCrop_marqueeSouth { bottom: 0px; left: 0; }
.imgCrop_marqueeWest { top: 0; left: 0; }
.imgCrop_handle {
position: absolute;
border: 1px solid #333;
width: 6px;
height: 6px;
background: #FFF;
opacity: 0.5;
filter:alpha(opacity=50);
z-index: 3;
}
.imgCrop_handleN {
top: -3px;
left: 0;
margin-left: 49%; /* @TODO : in JS */
cursor: n-resize;
}
.imgCrop_handleNE {
top: -3px;
right: -3px;
cursor: ne-resize;
}
.imgCrop_handleE {
top: 0;
right: -3px;
margin-top: 49%; /* @TODO : in JS */
cursor: e-resize;
}
.imgCrop_handleSE {
right: -3px;
bottom: -3px;
cursor: se-resize;
}
.imgCrop_handleS {
right: 0;
bottom: -3px;
margin-right: 49%; /* @TODO : in JS */
cursor: s-resize;
}
.imgCrop_handleSW {
left: -3px;
bottom: -3px;
cursor: sw-resize;
}
.imgCrop_handleW {
top: 0;
left: -3px;
margin-top: 49%; /* @TODO : in JS */
cursor: e-resize;
}
.imgCrop_handleNW {
top: -3px;
left: -3px;
cursor: nw-resize;
}
/**
* Create an area to click & drag around on as the default browser behaviour is to let you drag the image
*/
.imgCrop_dragArea {
width: 100%;
height: 100%;
z-index: 200;
position: absolute;
top: 0;
left: 0;
}
.imgCrop_previewWrap {
width: 200px; /* @TODO : in JS */
height: 200px; /* @TODO : in JS */
overflow: hidden;
position: relative;
}
/* @TODO : all in JS */
.imgCrop_previewWrap img {
position: absolute;
width: 500px;
height: 333px;
left: -210px;
top: -110px;
}
/**
* These are just for the static test
*/
.imgCrop_wrap {
margin: 20px 0 0 50px;
float: left;
}
#previewWrapper {
float: left;
margin-left: 20px;
}
</style>
<br /><br />
<!-- This is all attached to the image dynamically -->
<div class="imgCrop_wrap">
<img src="castle.jpg" alt="test image" id="testImage" width="500" height="333" />
<div class="imgCrop_dragArea">
<div class="imgCrop_overlay"></div>
<div class="imgCrop_selArea">
<!-- marquees -->
<!-- the inner spans are only required for IE to stop it making the divs 1px high/wide -->
<div class="imgCrop_marqueeHoriz imgCrop_marqueeNorth"><span></span></div>
<div class="imgCrop_marqueeVert imgCrop_marqueeEast"><span></span></div>
<div class="imgCrop_marqueeHoriz imgCrop_marqueeSouth"><span></span></div>
<div class="imgCrop_marqueeVert imgCrop_marqueeWest"><span></span></div>
<!-- handles -->
<div class="imgCrop_handle imgCrop_handleN"></div>
<div class="imgCrop_handle imgCrop_handleNE"></div>
<div class="imgCrop_handle imgCrop_handleE"></div>
<div class="imgCrop_handle imgCrop_handleSE"></div>
<div class="imgCrop_handle imgCrop_handleS"></div>
<div class="imgCrop_handle imgCrop_handleSW"></div>
<div class="imgCrop_handle imgCrop_handleW"></div>
<div class="imgCrop_handle imgCrop_handleNW"></div>
<div class="imgCrop_clickArea"></div>
</div>
<div class="imgCrop_clickArea"></div>
</div>
</div>
<div id="previewWrapper">
<h3>Preview:</h3>
<div class="imgCrop_previewWrap">
<img src="castle.jpg" alt="test image" id="previewImage" />
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,479 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Pure-PHP implementation of AES.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP versions 4 and 5
*
* If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
* {@link Crypt_AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
* it'll be null-padded to 160-bits and 160 bits will be the key length until {@link Crypt_Rijndael::setKey() setKey()}
* is called, again, at which point, it'll be recalculated.
*
* Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't
* make a whole lot of sense. {@link Crypt_AES::setBlockLength() setBlockLength()}, for instance. Calling that function,
* however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include('Crypt/AES.php');
*
* $aes = new Crypt_AES();
*
* $aes->setKey('abcdefghijklmnop');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $aes->decrypt($aes->encrypt($plaintext));
* ?>
* </code>
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Crypt
* @package Crypt_AES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVIII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
/**
* Include Crypt_Rijndael
*/
require_once 'Rijndael.php';
/**#@+
* @access public
* @see Crypt_AES::encrypt()
* @see Crypt_AES::decrypt()
*/
/**
* Encrypt / decrypt using the Counter mode.
*
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
*/
define('CRYPT_AES_MODE_CTR', -1);
/**
* Encrypt / decrypt using the Electronic Code Book mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
*/
define('CRYPT_AES_MODE_ECB', 1);
/**
* Encrypt / decrypt using the Code Book Chaining mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
*/
define('CRYPT_AES_MODE_CBC', 2);
/**#@-*/
/**#@+
* @access private
* @see Crypt_AES::Crypt_AES()
*/
/**
* Toggles the internal implementation
*/
define('CRYPT_AES_MODE_INTERNAL', 1);
/**
* Toggles the mcrypt implementation
*/
define('CRYPT_AES_MODE_MCRYPT', 2);
/**#@-*/
/**
* Pure-PHP implementation of AES.
*
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Crypt_AES
*/
class Crypt_AES extends Crypt_Rijndael {
/**
* mcrypt resource for encryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::encrypt()
* @var String
* @access private
*/
var $enmcrypt;
/**
* mcrypt resource for decryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::decrypt()
* @var String
* @access private
*/
var $demcrypt;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
* CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used.
*
* @param optional Integer $mode
* @return Crypt_AES
* @access public
*/
function Crypt_AES($mode = CRYPT_AES_MODE_CBC)
{
if ( !defined('CRYPT_AES_MODE') ) {
switch (true) {
case extension_loaded('mcrypt'):
// i'd check to see if aes was supported, by doing in_array('des', mcrypt_list_algorithms('')),
// but since that can be changed after the object has been created, there doesn't seem to be
// a lot of point...
define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT);
break;
default:
define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL);
}
}
switch ( CRYPT_AES_MODE ) {
case CRYPT_AES_MODE_MCRYPT:
switch ($mode) {
case CRYPT_AES_MODE_ECB:
$this->mode = MCRYPT_MODE_ECB;
break;
case CRYPT_AES_MODE_CTR:
// ctr doesn't have a constant associated with it even though it appears to be fairly widely
// supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to
// include a compatibility layer. the layer has been implemented but, for now, is commented out.
$this->mode = 'ctr';
//$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;
break;
case CRYPT_AES_MODE_CBC:
default:
$this->mode = MCRYPT_MODE_CBC;
}
break;
default:
switch ($mode) {
case CRYPT_AES_MODE_ECB:
$this->mode = CRYPT_RIJNDAEL_MODE_ECB;
break;
case CRYPT_AES_MODE_CTR:
$this->mode = CRYPT_RIJNDAEL_MODE_CTR;
break;
case CRYPT_AES_MODE_CBC:
default:
$this->mode = CRYPT_RIJNDAEL_MODE_CBC;
}
}
if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) {
parent::Crypt_Rijndael($this->mode);
}
}
/**
* Dummy function
*
* Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.
*
* @access public
* @param Integer $length
*/
function setBlockLength($length)
{
return;
}
/**
* Encrypts a message.
*
* $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the
* same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
* URL:
*
* {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
*
* An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
* strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that
* length.
*
* @see Crypt_AES::decrypt()
* @access public
* @param String $plaintext
*/
function encrypt($plaintext)
{
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$this->_mcryptSetup();
/*
if ($this->mode == CRYPT_AES_MODE_CTR) {
$iv = $this->encryptIV;
$xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($plaintext), $iv));
$ciphertext = $plaintext ^ $xor;
if ($this->continuousBuffer) {
$this->encryptIV = $iv;
}
return $ciphertext;
}
*/
if ($this->mode != 'ctr') {
$plaintext = $this->_pad($plaintext);
}
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
}
return $ciphertext;
}
return parent::encrypt($plaintext);
}
/**
* Decrypts a message.
*
* If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is.
*
* @see Crypt_AES::encrypt()
* @access public
* @param String $ciphertext
*/
function decrypt($ciphertext)
{
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
$this->_mcryptSetup();
/*
if ($this->mode == CRYPT_AES_MODE_CTR) {
$iv = $this->decryptIV;
$xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($ciphertext), $iv));
$plaintext = $ciphertext ^ $xor;
if ($this->continuousBuffer) {
$this->decryptIV = $iv;
}
return $plaintext;
}
*/
if ($this->mode != 'ctr') {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
$ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0));
}
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
}
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
}
return parent::decrypt($ciphertext);
}
/**
* Setup mcrypt
*
* Validates all the variables.
*
* @access private
*/
function _mcryptSetup()
{
if (!$this->changed) {
return;
}
if (!$this->explicit_key_length) {
// this just copied from Crypt_Rijndael::_setup()
$length = strlen($this->key) >> 2;
if ($length > 8) {
$length = 8;
} else if ($length < 4) {
$length = 4;
}
$this->Nk = $length;
$this->key_size = $length << 2;
}
switch ($this->Nk) {
case 4: // 128
$this->key_size = 16;
break;
case 5: // 160
case 6: // 192
$this->key_size = 24;
break;
case 7: // 224
case 8: // 256
$this->key_size = 32;
}
$this->key = substr($this->key, 0, $this->key_size);
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));
if (!isset($this->enmcrypt)) {
$mode = $this->mode;
//$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode;
$this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
$this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
} // else should mcrypt_generic_deinit be called?
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
$this->changed = false;
}
/**
* Encrypts a block
*
* Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
*
* @see Crypt_Rijndael::_encryptBlock()
* @access private
* @param String $in
* @return String
*/
function _encryptBlock($in)
{
$state = unpack('N*word', $in);
$Nr = $this->Nr;
$w = $this->w;
$t0 = $this->t0;
$t1 = $this->t1;
$t2 = $this->t2;
$t3 = $this->t3;
// addRoundKey and reindex $state
$state = array(
$state['word1'] ^ $w[0][0],
$state['word2'] ^ $w[0][1],
$state['word3'] ^ $w[0][2],
$state['word4'] ^ $w[0][3]
);
// shiftRows + subWord + mixColumns + addRoundKey
// we could loop unroll this and use if statements to do more rounds as necessary, but, in my tests, that yields
// only a marginal improvement. since that also, imho, hinders the readability of the code, i've opted not to do it.
for ($round = 1; $round < $this->Nr; $round++) {
$state = array(
$t0[$state[0] & 0xFF000000] ^ $t1[$state[1] & 0x00FF0000] ^ $t2[$state[2] & 0x0000FF00] ^ $t3[$state[3] & 0x000000FF] ^ $w[$round][0],
$t0[$state[1] & 0xFF000000] ^ $t1[$state[2] & 0x00FF0000] ^ $t2[$state[3] & 0x0000FF00] ^ $t3[$state[0] & 0x000000FF] ^ $w[$round][1],
$t0[$state[2] & 0xFF000000] ^ $t1[$state[3] & 0x00FF0000] ^ $t2[$state[0] & 0x0000FF00] ^ $t3[$state[1] & 0x000000FF] ^ $w[$round][2],
$t0[$state[3] & 0xFF000000] ^ $t1[$state[0] & 0x00FF0000] ^ $t2[$state[1] & 0x0000FF00] ^ $t3[$state[2] & 0x000000FF] ^ $w[$round][3]
);
}
// subWord
$state = array(
$this->_subWord($state[0]),
$this->_subWord($state[1]),
$this->_subWord($state[2]),
$this->_subWord($state[3])
);
// shiftRows + addRoundKey
$state = array(
($state[0] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[3] & 0x000000FF) ^ $this->w[$this->Nr][0],
($state[1] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[0] & 0x000000FF) ^ $this->w[$this->Nr][1],
($state[2] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[1] & 0x000000FF) ^ $this->w[$this->Nr][2],
($state[3] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[2] & 0x000000FF) ^ $this->w[$this->Nr][3]
);
return pack('N*', $state[0], $state[1], $state[2], $state[3]);
}
/**
* Decrypts a block
*
* Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
*
* @see Crypt_Rijndael::_decryptBlock()
* @access private
* @param String $in
* @return String
*/
function _decryptBlock($in)
{
$state = unpack('N*word', $in);
$Nr = $this->Nr;
$dw = $this->dw;
$dt0 = $this->dt0;
$dt1 = $this->dt1;
$dt2 = $this->dt2;
$dt3 = $this->dt3;
// addRoundKey and reindex $state
$state = array(
$state['word1'] ^ $dw[$this->Nr][0],
$state['word2'] ^ $dw[$this->Nr][1],
$state['word3'] ^ $dw[$this->Nr][2],
$state['word4'] ^ $dw[$this->Nr][3]
);
// invShiftRows + invSubBytes + invMixColumns + addRoundKey
for ($round = $this->Nr - 1; $round > 0; $round--) {
$state = array(
$dt0[$state[0] & 0xFF000000] ^ $dt1[$state[3] & 0x00FF0000] ^ $dt2[$state[2] & 0x0000FF00] ^ $dt3[$state[1] & 0x000000FF] ^ $dw[$round][0],
$dt0[$state[1] & 0xFF000000] ^ $dt1[$state[0] & 0x00FF0000] ^ $dt2[$state[3] & 0x0000FF00] ^ $dt3[$state[2] & 0x000000FF] ^ $dw[$round][1],
$dt0[$state[2] & 0xFF000000] ^ $dt1[$state[1] & 0x00FF0000] ^ $dt2[$state[0] & 0x0000FF00] ^ $dt3[$state[3] & 0x000000FF] ^ $dw[$round][2],
$dt0[$state[3] & 0xFF000000] ^ $dt1[$state[2] & 0x00FF0000] ^ $dt2[$state[1] & 0x0000FF00] ^ $dt3[$state[0] & 0x000000FF] ^ $dw[$round][3]
);
}
// invShiftRows + invSubWord + addRoundKey
$state = array(
$this->_invSubWord(($state[0] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[1] & 0x000000FF)) ^ $dw[0][0],
$this->_invSubWord(($state[1] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[2] & 0x000000FF)) ^ $dw[0][1],
$this->_invSubWord(($state[2] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[3] & 0x000000FF)) ^ $dw[0][2],
$this->_invSubWord(($state[3] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[0] & 0x000000FF)) ^ $dw[0][3]
);
return pack('N*', $state[0], $state[1], $state[2], $state[3]);
}
}
// vim: ts=4:sw=4:et:
// vim6: fdl=1:

View file

@ -0,0 +1,945 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Pure-PHP implementation of DES.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP versions 4 and 5
*
* Useful resources are as follows:
*
* - {@link http://en.wikipedia.org/wiki/DES_supplementary_material Wikipedia: DES supplementary material}
* - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 - (DES), Data Encryption Standard}
* - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html JavaScript DES Example}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include('Crypt/DES.php');
*
* $des = new Crypt_DES();
*
* $des->setKey('abcdefgh');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $des->decrypt($des->encrypt($plaintext));
* ?>
* </code>
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Crypt
* @package Crypt_DES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: DES.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
/**#@+
* @access private
* @see Crypt_DES::_prepareKey()
* @see Crypt_DES::_processBlock()
*/
/**
* Contains array_reverse($keys[CRYPT_DES_DECRYPT])
*/
define('CRYPT_DES_ENCRYPT', 0);
/**
* Contains array_reverse($keys[CRYPT_DES_ENCRYPT])
*/
define('CRYPT_DES_DECRYPT', 1);
/**#@-*/
/**#@+
* @access public
* @see Crypt_DES::encrypt()
* @see Crypt_DES::decrypt()
*/
/**
* Encrypt / decrypt using the Counter mode.
*
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
*/
define('CRYPT_DES_MODE_CTR', -1);
/**
* Encrypt / decrypt using the Electronic Code Book mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
*/
define('CRYPT_DES_MODE_ECB', 1);
/**
* Encrypt / decrypt using the Code Book Chaining mode.
*
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
*/
define('CRYPT_DES_MODE_CBC', 2);
/**#@-*/
/**#@+
* @access private
* @see Crypt_DES::Crypt_DES()
*/
/**
* Toggles the internal implementation
*/
define('CRYPT_DES_MODE_INTERNAL', 1);
/**
* Toggles the mcrypt implementation
*/
define('CRYPT_DES_MODE_MCRYPT', 2);
/**#@-*/
/**
* Pure-PHP implementation of DES.
*
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Crypt_DES
*/
class Crypt_DES {
/**
* The Key Schedule
*
* @see Crypt_DES::setKey()
* @var Array
* @access private
*/
var $keys = "\0\0\0\0\0\0\0\0";
/**
* The Encryption Mode
*
* @see Crypt_DES::Crypt_DES()
* @var Integer
* @access private
*/
var $mode;
/**
* Continuous Buffer status
*
* @see Crypt_DES::enableContinuousBuffer()
* @var Boolean
* @access private
*/
var $continuousBuffer = false;
/**
* Padding status
*
* @see Crypt_DES::enablePadding()
* @var Boolean
* @access private
*/
var $padding = true;
/**
* The Initialization Vector
*
* @see Crypt_DES::setIV()
* @var String
* @access private
*/
var $iv = "\0\0\0\0\0\0\0\0";
/**
* A "sliding" Initialization Vector
*
* @see Crypt_DES::enableContinuousBuffer()
* @var String
* @access private
*/
var $encryptIV = "\0\0\0\0\0\0\0\0";
/**
* A "sliding" Initialization Vector
*
* @see Crypt_DES::enableContinuousBuffer()
* @var String
* @access private
*/
var $decryptIV = "\0\0\0\0\0\0\0\0";
/**
* mcrypt resource for encryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::encrypt()
* @var String
* @access private
*/
var $enmcrypt;
/**
* mcrypt resource for decryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::decrypt()
* @var String
* @access private
*/
var $demcrypt;
/**
* Does the (en|de)mcrypt resource need to be (re)initialized?
*
* @see setKey()
* @see setIV()
* @var Boolean
* @access private
*/
var $changed = true;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
* CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
*
* @param optional Integer $mode
* @return Crypt_DES
* @access public
*/
function Crypt_DES($mode = CRYPT_MODE_DES_CBC)
{
if ( !defined('CRYPT_DES_MODE') ) {
switch (true) {
case extension_loaded('mcrypt'):
// i'd check to see if des was supported, by doing in_array('des', mcrypt_list_algorithms('')),
// but since that can be changed after the object has been created, there doesn't seem to be
// a lot of point...
define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
break;
default:
define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
}
}
switch ( CRYPT_DES_MODE ) {
case CRYPT_DES_MODE_MCRYPT:
switch ($mode) {
case CRYPT_DES_MODE_ECB:
$this->mode = MCRYPT_MODE_ECB;
break;
case CRYPT_DES_MODE_CTR:
$this->mode = 'ctr';
//$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR;
break;
case CRYPT_DES_MODE_CBC:
default:
$this->mode = MCRYPT_MODE_CBC;
}
break;
default:
switch ($mode) {
case CRYPT_DES_MODE_ECB:
case CRYPT_DES_MODE_CTR:
case CRYPT_DES_MODE_CBC:
$this->mode = $mode;
break;
default:
$this->mode = CRYPT_DES_MODE_CBC;
}
}
}
/**
* Sets the key.
*
* Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we
* only use the first eight, if $key has more then eight characters in it, and pad $key with the
* null byte if it is less then eight characters long.
*
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
*
* If the key is not explicitly set, it'll be assumed to be all zero's.
*
* @access public
* @param String $key
*/
function setKey($key)
{
$this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? substr($key, 0, 8) : $this->_prepareKey($key);
$this->changed = true;
}
/**
* Sets the initialization vector. (optional)
*
* SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
* to be all zero's.
*
* @access public
* @param String $iv
*/
function setIV($iv)
{
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
$this->changed = true;
}
/**
* Generate CTR XOR encryption key
*
* Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
* plaintext / ciphertext in CTR mode.
*
* @see Crypt_DES::decrypt()
* @see Crypt_DES::encrypt()
* @access public
* @param Integer $length
* @param String $iv
*/
function _generate_xor($length, &$iv)
{
$xor = '';
$num_blocks = ($length + 7) >> 3;
for ($i = 0; $i < $num_blocks; $i++) {
$xor.= $iv;
for ($j = 4; $j <= 8; $j+=4) {
$temp = substr($iv, -$j, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
break;
case "\x7F\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
break 2;
default:
extract(unpack('Ncount', $temp));
$iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
break 2;
}
}
}
return $xor;
}
/**
* Encrypts a message.
*
* $plaintext will be padded with up to 8 additional bytes. Other DES implementations may or may not pad in the
* same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
* URL:
*
* {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
*
* An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
* strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that
* length.
*
* @see Crypt_DES::decrypt()
* @access public
* @param String $plaintext
*/
function encrypt($plaintext)
{
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
$plaintext = $this->_pad($plaintext);
}
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->changed) {
if (!isset($this->enmcrypt)) {
$this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
}
mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
$this->changed = false;
}
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
}
return $ciphertext;
}
if (!is_array($this->keys)) {
$this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
}
$ciphertext = '';
switch ($this->mode) {
case CRYPT_DES_MODE_ECB:
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$ciphertext.= $this->_processBlock(substr($plaintext, $i, 8), CRYPT_DES_ENCRYPT);
}
break;
case CRYPT_DES_MODE_CBC:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
$block = $this->_processBlock($block ^ $xor, CRYPT_DES_ENCRYPT);
$xor = $block;
$ciphertext.= $block;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
$key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
$ciphertext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
}
return $ciphertext;
}
/**
* Decrypts a message.
*
* If strlen($ciphertext) is not a multiple of 8, null bytes will be added to the end of the string until it is.
*
* @see Crypt_DES::encrypt()
* @access public
* @param String $ciphertext
*/
function decrypt($ciphertext)
{
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
$ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
}
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->changed) {
if (!isset($this->demcrypt)) {
$this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
}
mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
$this->changed = false;
}
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
}
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
}
if (!is_array($this->keys)) {
$this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
}
$plaintext = '';
switch ($this->mode) {
case CRYPT_DES_MODE_ECB:
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$plaintext.= $this->_processBlock(substr($ciphertext, $i, 8), CRYPT_DES_DECRYPT);
}
break;
case CRYPT_DES_MODE_CBC:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
$plaintext.= $this->_processBlock($block, CRYPT_DES_DECRYPT) ^ $xor;
$xor = $block;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
$key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
$plaintext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
}
return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
}
/**
* Treat consecutive "packets" as if they are a continuous buffer.
*
* Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
* will yield different outputs:
*
* <code>
* echo $des->encrypt(substr($plaintext, 0, 8));
* echo $des->encrypt(substr($plaintext, 8, 8));
* </code>
* <code>
* echo $des->encrypt($plaintext);
* </code>
*
* The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
* another, as demonstrated with the following:
*
* <code>
* $des->encrypt(substr($plaintext, 0, 8));
* echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
* </code>
* <code>
* echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
* </code>
*
* With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
*
* Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
* however, they are also less intuitive and more likely to cause you problems.
*
* @see Crypt_DES::disableContinuousBuffer()
* @access public
*/
function enableContinuousBuffer()
{
$this->continuousBuffer = true;
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* @see Crypt_DES::enableContinuousBuffer()
* @access public
*/
function disableContinuousBuffer()
{
$this->continuousBuffer = false;
$this->encryptIV = $this->iv;
$this->decryptIV = $this->iv;
}
/**
* Pad "packets".
*
* DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
* a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
*
* Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
* where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
* away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
* transmitted separately)
*
* @see Crypt_DES::disablePadding()
* @access public
*/
function enablePadding()
{
$this->padding = true;
}
/**
* Do not pad packets.
*
* @see Crypt_DES::enablePadding()
* @access public
*/
function disablePadding()
{
$this->padding = false;
}
/**
* Pads a string
*
* Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
* 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
*
* If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
* and padding will, hence forth, be enabled.
*
* @see Crypt_DES::_unpad()
* @access private
*/
function _pad($text)
{
$length = strlen($text);
if (!$this->padding) {
if (($length & 7) == 0) {
return $text;
} else {
user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE);
$this->padding = true;
}
}
$pad = 8 - ($length & 7);
return str_pad($text, $length + $pad, chr($pad));
}
/**
* Unpads a string
*
* If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
* and false will be returned.
*
* @see Crypt_DES::_pad()
* @access private
*/
function _unpad($text)
{
if (!$this->padding) {
return $text;
}
$length = ord($text[strlen($text) - 1]);
if (!$length || $length > 8) {
return false;
}
return substr($text, 0, -$length);
}
/**
* Encrypts or decrypts a 64-bit block
*
* $mode should be either CRYPT_DES_ENCRYPT or CRYPT_DES_DECRYPT. See
* {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general
* idea of what this function does.
*
* @access private
* @param String $block
* @param Integer $mode
* @return String
*/
function _processBlock($block, $mode)
{
// s-boxes. in the official DES docs, they're described as being matrices that
// one accesses by using the first and last bits to determine the row and the
// middle four bits to determine the column. in this implementation, they've
// been converted to vectors
static $sbox = array(
array(
14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1,
3, 10 ,10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8,
4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7,
15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13
),
array(
15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9
),
array(
10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10,
1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1,
13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7,
11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12
),
array(
7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3,
1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9,
10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8,
15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14
),
array(
2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1,
8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6,
4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13,
15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3
),
array(
12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5,
0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8,
9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10,
7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13
),
array(
4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10,
3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6,
1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12
),
array(
13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4,
10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2,
7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13,
0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11
)
);
$keys = $this->keys;
$temp = unpack('Na/Nb', $block);
$block = array($temp['a'], $temp['b']);
// because php does arithmetic right shifts, if the most significant bits are set, right
// shifting those into the correct position will add 1's - not 0's. this will intefere
// with the | operation unless a second & is done. so we isolate these bits and left shift
// them into place. we then & each block with 0x7FFFFFFF to prevennt 1's from being added
// for any other shifts.
$msb = array(
($block[0] >> 31) & 1,
($block[1] >> 31) & 1
);
$block[0] &= 0x7FFFFFFF;
$block[1] &= 0x7FFFFFFF;
// we isolate the appropriate bit in the appropriate integer and shift as appropriate. in
// some cases, there are going to be multiple bits in the same integer that need to be shifted
// in the same way. we combine those into one shift operation.
$block = array(
(($block[1] & 0x00000040) << 25) | (($block[1] & 0x00004000) << 16) |
(($block[1] & 0x00400001) << 7) | (($block[1] & 0x40000100) >> 2) |
(($block[0] & 0x00000040) << 21) | (($block[0] & 0x00004000) << 12) |
(($block[0] & 0x00400001) << 3) | (($block[0] & 0x40000100) >> 6) |
(($block[1] & 0x00000010) << 19) | (($block[1] & 0x00001000) << 10) |
(($block[1] & 0x00100000) << 1) | (($block[1] & 0x10000000) >> 8) |
(($block[0] & 0x00000010) << 15) | (($block[0] & 0x00001000) << 6) |
(($block[0] & 0x00100000) >> 3) | (($block[0] & 0x10000000) >> 12) |
(($block[1] & 0x00000004) << 13) | (($block[1] & 0x00000400) << 4) |
(($block[1] & 0x00040000) >> 5) | (($block[1] & 0x04000000) >> 14) |
(($block[0] & 0x00000004) << 9) | ( $block[0] & 0x00000400 ) |
(($block[0] & 0x00040000) >> 9) | (($block[0] & 0x04000000) >> 18) |
(($block[1] & 0x00010000) >> 11) | (($block[1] & 0x01000000) >> 20) |
(($block[0] & 0x00010000) >> 15) | (($block[0] & 0x01000000) >> 24)
,
(($block[1] & 0x00000080) << 24) | (($block[1] & 0x00008000) << 15) |
(($block[1] & 0x00800002) << 6) | (($block[0] & 0x00000080) << 20) |
(($block[0] & 0x00008000) << 11) | (($block[0] & 0x00800002) << 2) |
(($block[1] & 0x00000020) << 18) | (($block[1] & 0x00002000) << 9) |
( $block[1] & 0x00200000 ) | (($block[1] & 0x20000000) >> 9) |
(($block[0] & 0x00000020) << 14) | (($block[0] & 0x00002000) << 5) |
(($block[0] & 0x00200000) >> 4) | (($block[0] & 0x20000000) >> 13) |
(($block[1] & 0x00000008) << 12) | (($block[1] & 0x00000800) << 3) |
(($block[1] & 0x00080000) >> 6) | (($block[1] & 0x08000000) >> 15) |
(($block[0] & 0x00000008) << 8) | (($block[0] & 0x00000800) >> 1) |
(($block[0] & 0x00080000) >> 10) | (($block[0] & 0x08000000) >> 19) |
(($block[1] & 0x00000200) >> 3) | (($block[0] & 0x00000200) >> 7) |
(($block[1] & 0x00020000) >> 12) | (($block[1] & 0x02000000) >> 21) |
(($block[0] & 0x00020000) >> 16) | (($block[0] & 0x02000000) >> 25) |
($msb[1] << 28) | ($msb[0] << 24)
);
for ($i = 0; $i < 16; $i++) {
// start of "the Feistel (F) function" - see the following URL:
// http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
$temp = (($sbox[0][((($block[1] >> 27) & 0x1F) | (($block[1] & 1) << 5)) ^ $keys[$mode][$i][0]]) << 28)
| (($sbox[1][(($block[1] & 0x1F800000) >> 23) ^ $keys[$mode][$i][1]]) << 24)
| (($sbox[2][(($block[1] & 0x01F80000) >> 19) ^ $keys[$mode][$i][2]]) << 20)
| (($sbox[3][(($block[1] & 0x001F8000) >> 15) ^ $keys[$mode][$i][3]]) << 16)
| (($sbox[4][(($block[1] & 0x0001F800) >> 11) ^ $keys[$mode][$i][4]]) << 12)
| (($sbox[5][(($block[1] & 0x00001F80) >> 7) ^ $keys[$mode][$i][5]]) << 8)
| (($sbox[6][(($block[1] & 0x000001F8) >> 3) ^ $keys[$mode][$i][6]]) << 4)
| ( $sbox[7][((($block[1] & 0x1F) << 1) | (($block[1] >> 31) & 1)) ^ $keys[$mode][$i][7]]);
$msb = ($temp >> 31) & 1;
$temp &= 0x7FFFFFFF;
$newBlock = (($temp & 0x00010000) << 15) | (($temp & 0x02020120) << 5)
| (($temp & 0x00001800) << 17) | (($temp & 0x01000000) >> 10)
| (($temp & 0x00000008) << 24) | (($temp & 0x00100000) << 6)
| (($temp & 0x00000010) << 21) | (($temp & 0x00008000) << 9)
| (($temp & 0x00000200) << 12) | (($temp & 0x10000000) >> 27)
| (($temp & 0x00000040) << 14) | (($temp & 0x08000000) >> 8)
| (($temp & 0x00004000) << 4) | (($temp & 0x00000002) << 16)
| (($temp & 0x00442000) >> 6) | (($temp & 0x40800000) >> 15)
| (($temp & 0x00000001) << 11) | (($temp & 0x20000000) >> 20)
| (($temp & 0x00080000) >> 13) | (($temp & 0x00000004) << 3)
| (($temp & 0x04000000) >> 22) | (($temp & 0x00000480) >> 7)
| (($temp & 0x00200000) >> 19) | ($msb << 23);
// end of "the Feistel (F) function" - $newBlock is F's output
$temp = $block[1];
$block[1] = $block[0] ^ $newBlock;
$block[0] = $temp;
}
$msb = array(
($block[0] >> 31) & 1,
($block[1] >> 31) & 1
);
$block[0] &= 0x7FFFFFFF;
$block[1] &= 0x7FFFFFFF;
$block = array(
(($block[0] & 0x01000004) << 7) | (($block[1] & 0x01000004) << 6) |
(($block[0] & 0x00010000) << 13) | (($block[1] & 0x00010000) << 12) |
(($block[0] & 0x00000100) << 19) | (($block[1] & 0x00000100) << 18) |
(($block[0] & 0x00000001) << 25) | (($block[1] & 0x00000001) << 24) |
(($block[0] & 0x02000008) >> 2) | (($block[1] & 0x02000008) >> 3) |
(($block[0] & 0x00020000) << 4) | (($block[1] & 0x00020000) << 3) |
(($block[0] & 0x00000200) << 10) | (($block[1] & 0x00000200) << 9) |
(($block[0] & 0x00000002) << 16) | (($block[1] & 0x00000002) << 15) |
(($block[0] & 0x04000000) >> 11) | (($block[1] & 0x04000000) >> 12) |
(($block[0] & 0x00040000) >> 5) | (($block[1] & 0x00040000) >> 6) |
(($block[0] & 0x00000400) << 1) | ( $block[1] & 0x00000400 ) |
(($block[0] & 0x08000000) >> 20) | (($block[1] & 0x08000000) >> 21) |
(($block[0] & 0x00080000) >> 14) | (($block[1] & 0x00080000) >> 15) |
(($block[0] & 0x00000800) >> 8) | (($block[1] & 0x00000800) >> 9)
,
(($block[0] & 0x10000040) << 3) | (($block[1] & 0x10000040) << 2) |
(($block[0] & 0x00100000) << 9) | (($block[1] & 0x00100000) << 8) |
(($block[0] & 0x00001000) << 15) | (($block[1] & 0x00001000) << 14) |
(($block[0] & 0x00000010) << 21) | (($block[1] & 0x00000010) << 20) |
(($block[0] & 0x20000080) >> 6) | (($block[1] & 0x20000080) >> 7) |
( $block[0] & 0x00200000 ) | (($block[1] & 0x00200000) >> 1) |
(($block[0] & 0x00002000) << 6) | (($block[1] & 0x00002000) << 5) |
(($block[0] & 0x00000020) << 12) | (($block[1] & 0x00000020) << 11) |
(($block[0] & 0x40000000) >> 15) | (($block[1] & 0x40000000) >> 16) |
(($block[0] & 0x00400000) >> 9) | (($block[1] & 0x00400000) >> 10) |
(($block[0] & 0x00004000) >> 3) | (($block[1] & 0x00004000) >> 4) |
(($block[0] & 0x00800000) >> 18) | (($block[1] & 0x00800000) >> 19) |
(($block[0] & 0x00008000) >> 12) | (($block[1] & 0x00008000) >> 13) |
($msb[0] << 7) | ($msb[1] << 6)
);
return pack('NN', $block[0], $block[1]);
}
/**
* Creates the key schedule.
*
* @access private
* @param String $key
* @return Array
*/
function _prepareKey($key)
{
static $shifts = array( // number of key bits shifted per round
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
);
// pad the key and remove extra characters as appropriate.
$key = str_pad(substr($key, 0, 8), 8, chr(0));
$temp = unpack('Na/Nb', $key);
$key = array($temp['a'], $temp['b']);
$msb = array(
($key[0] >> 31) & 1,
($key[1] >> 31) & 1
);
$key[0] &= 0x7FFFFFFF;
$key[1] &= 0x7FFFFFFF;
$key = array(
(($key[1] & 0x00000002) << 26) | (($key[1] & 0x00000204) << 17) |
(($key[1] & 0x00020408) << 8) | (($key[1] & 0x02040800) >> 1) |
(($key[0] & 0x00000002) << 22) | (($key[0] & 0x00000204) << 13) |
(($key[0] & 0x00020408) << 4) | (($key[0] & 0x02040800) >> 5) |
(($key[1] & 0x04080000) >> 10) | (($key[0] & 0x04080000) >> 14) |
(($key[1] & 0x08000000) >> 19) | (($key[0] & 0x08000000) >> 23) |
(($key[0] & 0x00000010) >> 1) | (($key[0] & 0x00001000) >> 10) |
(($key[0] & 0x00100000) >> 19) | (($key[0] & 0x10000000) >> 28)
,
(($key[1] & 0x00000080) << 20) | (($key[1] & 0x00008000) << 11) |
(($key[1] & 0x00800000) << 2) | (($key[0] & 0x00000080) << 16) |
(($key[0] & 0x00008000) << 7) | (($key[0] & 0x00800000) >> 2) |
(($key[1] & 0x00000040) << 13) | (($key[1] & 0x00004000) << 4) |
(($key[1] & 0x00400000) >> 5) | (($key[1] & 0x40000000) >> 14) |
(($key[0] & 0x00000040) << 9) | ( $key[0] & 0x00004000 ) |
(($key[0] & 0x00400000) >> 9) | (($key[0] & 0x40000000) >> 18) |
(($key[1] & 0x00000020) << 6) | (($key[1] & 0x00002000) >> 3) |
(($key[1] & 0x00200000) >> 12) | (($key[1] & 0x20000000) >> 21) |
(($key[0] & 0x00000020) << 2) | (($key[0] & 0x00002000) >> 7) |
(($key[0] & 0x00200000) >> 16) | (($key[0] & 0x20000000) >> 25) |
(($key[1] & 0x00000010) >> 1) | (($key[1] & 0x00001000) >> 10) |
(($key[1] & 0x00100000) >> 19) | (($key[1] & 0x10000000) >> 28) |
($msb[1] << 24) | ($msb[0] << 20)
);
$keys = array();
for ($i = 0; $i < 16; $i++) {
$key[0] <<= $shifts[$i];
$temp = ($key[0] & 0xF0000000) >> 28;
$key[0] = ($key[0] | $temp) & 0x0FFFFFFF;
$key[1] <<= $shifts[$i];
$temp = ($key[1] & 0xF0000000) >> 28;
$key[1] = ($key[1] | $temp) & 0x0FFFFFFF;
$temp = array(
(($key[1] & 0x00004000) >> 9) | (($key[1] & 0x00000800) >> 7) |
(($key[1] & 0x00020000) >> 14) | (($key[1] & 0x00000010) >> 2) |
(($key[1] & 0x08000000) >> 26) | (($key[1] & 0x00800000) >> 23)
,
(($key[1] & 0x02400000) >> 20) | (($key[1] & 0x00000001) << 4) |
(($key[1] & 0x00002000) >> 10) | (($key[1] & 0x00040000) >> 18) |
(($key[1] & 0x00000080) >> 6)
,
( $key[1] & 0x00000020 ) | (($key[1] & 0x00000200) >> 5) |
(($key[1] & 0x00010000) >> 13) | (($key[1] & 0x01000000) >> 22) |
(($key[1] & 0x00000004) >> 1) | (($key[1] & 0x00100000) >> 20)
,
(($key[1] & 0x00001000) >> 7) | (($key[1] & 0x00200000) >> 17) |
(($key[1] & 0x00000002) << 2) | (($key[1] & 0x00000100) >> 6) |
(($key[1] & 0x00008000) >> 14) | (($key[1] & 0x04000000) >> 26)
,
(($key[0] & 0x00008000) >> 10) | ( $key[0] & 0x00000010 ) |
(($key[0] & 0x02000000) >> 22) | (($key[0] & 0x00080000) >> 17) |
(($key[0] & 0x00000200) >> 8) | (($key[0] & 0x00000002) >> 1)
,
(($key[0] & 0x04000000) >> 21) | (($key[0] & 0x00010000) >> 12) |
(($key[0] & 0x00000020) >> 2) | (($key[0] & 0x00000800) >> 9) |
(($key[0] & 0x00800000) >> 22) | (($key[0] & 0x00000100) >> 8)
,
(($key[0] & 0x00001000) >> 7) | (($key[0] & 0x00000088) >> 3) |
(($key[0] & 0x00020000) >> 14) | (($key[0] & 0x00000001) << 2) |
(($key[0] & 0x00400000) >> 21)
,
(($key[0] & 0x00000400) >> 5) | (($key[0] & 0x00004000) >> 10) |
(($key[0] & 0x00000040) >> 3) | (($key[0] & 0x00100000) >> 18) |
(($key[0] & 0x08000000) >> 26) | (($key[0] & 0x01000000) >> 24)
);
$keys[] = $temp;
}
$temp = array(
CRYPT_DES_ENCRYPT => $keys,
CRYPT_DES_DECRYPT => array_reverse($keys)
);
return $temp;
}
}
// vim: ts=4:sw=4:et:
// vim6: fdl=1:

View file

@ -0,0 +1,816 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
*
* Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following:
*
* md2, md5, md5-96, sha1, sha1-96, sha256, sha384, and sha512
*
* If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as opposed to
* the hash. If no valid algorithm is provided, sha1 will be used.
*
* PHP versions 4 and 5
*
* {@internal The variable names are the same as those in
* {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include('Crypt/Hash.php');
*
* $hash = new Crypt_Hash('sha1');
*
* $hash->setKey('abcdefg');
*
* echo base64_encode($hash->hash('abcdefg'));
* ?>
* </code>
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Crypt
* @package Crypt_Hash
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: Hash.php,v 1.6 2009/11/23 23:37:07 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
/**#@+
* @access private
* @see Crypt_Hash::Crypt_Hash()
*/
/**
* Toggles the internal implementation
*/
define('CRYPT_HASH_MODE_INTERNAL', 1);
/**
* Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+.
*/
define('CRYPT_HASH_MODE_MHASH', 2);
/**
* Toggles the hash() implementation, which works on PHP 5.1.2+.
*/
define('CRYPT_HASH_MODE_HASH', 3);
/**#@-*/
/**
* Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
*
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Crypt_Hash
*/
class Crypt_Hash {
/**
* Byte-length of compression blocks / key (Internal HMAC)
*
* @see Crypt_Hash::setAlgorithm()
* @var Integer
* @access private
*/
var $b;
/**
* Byte-length of hash output (Internal HMAC)
*
* @see Crypt_Hash::setHash()
* @var Integer
* @access private
*/
var $l = false;
/**
* Hash Algorithm
*
* @see Crypt_Hash::setHash()
* @var String
* @access private
*/
var $hash;
/**
* Key
*
* @see Crypt_Hash::setKey()
* @var String
* @access private
*/
var $key = '';
/**
* Outer XOR (Internal HMAC)
*
* @see Crypt_Hash::setKey()
* @var String
* @access private
*/
var $opad;
/**
* Inner XOR (Internal HMAC)
*
* @see Crypt_Hash::setKey()
* @var String
* @access private
*/
var $ipad;
/**
* Default Constructor.
*
* @param optional String $hash
* @return Crypt_Hash
* @access public
*/
function Crypt_Hash($hash = 'sha1')
{
if ( !defined('CRYPT_HASH_MODE') ) {
switch (true) {
case extension_loaded('hash'):
define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH);
break;
case extension_loaded('mhash'):
define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH);
break;
default:
define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL);
}
}
$this->setHash($hash);
}
/**
* Sets the key for HMACs
*
* Keys can be of any length.
*
* @access public
* @param String $key
*/
function setKey($key)
{
$this->key = $key;
}
/**
* Sets the hash function.
*
* @access public
* @param String $hash
*/
function setHash($hash)
{
switch ($hash) {
case 'md5-96':
case 'sha1-96':
$this->l = 12; // 96 / 8 = 12
break;
case 'md2':
case 'md5':
$this->l = 16;
break;
case 'sha1':
$this->l = 20;
break;
case 'sha256':
$this->l = 32;
break;
case 'sha384':
$this->l = 48;
break;
case 'sha512':
$this->l = 64;
}
switch ($hash) {
case 'md2':
$mode = CRYPT_HASH_MODE_INTERNAL;
break;
case 'sha384':
case 'sha512':
$mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
break;
default:
$mode = CRYPT_HASH_MODE;
}
switch ( $mode ) {
case CRYPT_HASH_MODE_MHASH:
switch ($hash) {
case 'md5':
case 'md5-96':
$this->hash = MHASH_MD5;
break;
case 'sha256':
$this->hash = MHASH_SHA256;
break;
case 'sha1':
case 'sha1-96':
default:
$this->hash = MHASH_SHA1;
}
return;
case CRYPT_HASH_MODE_HASH:
switch ($hash) {
case 'md5':
case 'md5-96':
$this->hash = 'md5';
return;
case 'sha256':
case 'sha384':
case 'sha512':
$this->hash = $hash;
return;
case 'sha1':
case 'sha1-96':
default:
$this->hash = 'sha1';
}
return;
}
switch ($hash) {
case 'md2':
$this->b = 16;
$this->hash = array($this, '_md2');
break;
case 'md5':
case 'md5-96':
$this->b = 64;
$this->hash = array($this, '_md5');
break;
case 'sha256':
$this->b = 64;
$this->hash = array($this, '_sha256');
break;
case 'sha384':
case 'sha512':
$this->b = 128;
$this->hash = array($this, '_sha512');
break;
case 'sha1':
case 'sha1-96':
default:
$this->b = 64;
$this->hash = array($this, '_sha1');
}
$this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b);
}
/**
* Compute the HMAC.
*
* @access public
* @param String $text
* @return String
*/
function hash($text)
{
$mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
if (!empty($this->key)) {
switch ( $mode ) {
case CRYPT_HASH_MODE_MHASH:
$output = mhash($this->hash, $text, $this->key);
break;
case CRYPT_HASH_MODE_HASH:
$output = hash_hmac($this->hash, $text, $this->key, true);
break;
case CRYPT_HASH_MODE_INTERNAL:
/* "Applications that use keys longer than B bytes will first hash the key using H and then use the
resultant L byte string as the actual key to HMAC."
-- http://tools.ietf.org/html/rfc2104#section-2 */
$key = strlen($this->key) > $this->b ? call_user_func($this->$hash, $this->key) : $this->key;
$key = str_pad($key, $this->b, chr(0)); // step 1
$temp = $this->ipad ^ $key; // step 2
$temp .= $text; // step 3
$temp = call_user_func($this->hash, $temp); // step 4
$output = $this->opad ^ $key; // step 5
$output.= $temp; // step 6
$output = call_user_func($this->hash, $output); // step 7
}
} else {
switch ( $mode ) {
case CRYPT_HASH_MODE_MHASH:
$output = mhash($this->hash, $text);
break;
case CRYPT_HASH_MODE_HASH:
$output = hash($this->hash, $text, true);
break;
case CRYPT_HASH_MODE_INTERNAL:
$output = call_user_func($this->hash, $text);
}
}
return substr($output, 0, $this->l);
}
/**
* Returns the hash length (in bytes)
*
* @access private
* @return Integer
*/
function getLength()
{
return $this->l;
}
/**
* Wrapper for MD5
*
* @access private
* @param String $text
*/
function _md5($m)
{
return pack('H*', md5($m));
}
/**
* Wrapper for SHA1
*
* @access private
* @param String $text
*/
function _sha1($m)
{
return pack('H*', sha1($m));
}
/**
* Pure-PHP implementation of MD2
*
* See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
*
* @access private
* @param String $text
*/
function _md2($m)
{
static $s = array(
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
);
// Step 1. Append Padding Bytes
$pad = 16 - (strlen($m) & 0xF);
$m.= str_repeat(chr($pad), $pad);
$length = strlen($m);
// Step 2. Append Checksum
$c = str_repeat(chr(0), 16);
$l = chr(0);
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
$l = $c[$j];
}
}
$m.= $c;
$length+= 16;
// Step 3. Initialize MD Buffer
$x = str_repeat(chr(0), 48);
// Step 4. Process Message in 16-Byte Blocks
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
$x[$j + 16] = $m[$i + $j];
$x[$j + 32] = $x[$j + 16] ^ $x[$j];
}
$t = chr(0);
for ($j = 0; $j < 18; $j++) {
for ($k = 0; $k < 48; $k++) {
$x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);
//$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]);
}
$t = chr(ord($t) + $j);
}
}
// Step 5. Output
return substr($x, 0, 16);
}
/**
* Pure-PHP implementation of SHA256
*
* See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}.
*
* @access private
* @param String $text
*/
function _sha256($m)
{
if (extension_loaded('suhosin')) {
return pack('H*', sha256($m));
}
// Initialize variables
$hash = array(
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
);
// Initialize table of round constants
// (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
static $k = array(
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
);
// Pre-processing
$length = strlen($m);
// to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64
$m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
$m.= pack('N2', 0, $length << 3);
// Process the message in successive 512-bit chunks
$chunks = str_split($m, 64);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
extract(unpack('Ntemp', $this->_string_shift($chunk, 4)));
$w[] = $temp;
}
// Extend the sixteen 32-bit words into sixty-four 32-bit words
for ($i = 16; $i < 64; $i++) {
$s0 = $this->_rightRotate($w[$i - 15], 7) ^
$this->_rightRotate($w[$i - 15], 18) ^
$this->_rightShift( $w[$i - 15], 3);
$s1 = $this->_rightRotate($w[$i - 2], 17) ^
$this->_rightRotate($w[$i - 2], 19) ^
$this->_rightShift( $w[$i - 2], 10);
$w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
}
// Initialize hash value for this chunk
list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;
// Main loop
for ($i = 0; $i < 64; $i++) {
$s0 = $this->_rightRotate($a, 2) ^
$this->_rightRotate($a, 13) ^
$this->_rightRotate($a, 22);
$maj = ($a & $b) ^
($a & $c) ^
($b & $c);
$t2 = $this->_add($s0, $maj);
$s1 = $this->_rightRotate($e, 6) ^
$this->_rightRotate($e, 11) ^
$this->_rightRotate($e, 25);
$ch = ($e & $f) ^
($this->_not($e) & $g);
$t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);
$h = $g;
$g = $f;
$f = $e;
$e = $this->_add($d, $t1);
$d = $c;
$c = $b;
$b = $a;
$a = $this->_add($t1, $t2);
}
// Add this chunk's hash to result so far
$hash = array(
$this->_add($hash[0], $a),
$this->_add($hash[1], $b),
$this->_add($hash[2], $c),
$this->_add($hash[3], $d),
$this->_add($hash[4], $e),
$this->_add($hash[5], $f),
$this->_add($hash[6], $g),
$this->_add($hash[7], $h)
);
}
// Produce the final hash value (big-endian)
return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]);
}
/**
* Pure-PHP implementation of SHA384 and SHA512
*
* @access private
* @param String $text
*/
function _sha512($m)
{
if (!class_exists('Math_BigInteger')) {
require_once('Math/BigInteger.php');
}
static $init384, $init512, $k;
if (!isset($k)) {
// Initialize variables
$init384 = array( // initial values for SHA384
'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939',
'67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
);
$init512 = array( // initial values for SHA512
'6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
'510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'
);
for ($i = 0; $i < 8; $i++) {
$init384[$i] = new Math_BigInteger($init384[$i], 16);
$init384[$i]->setPrecision(64);
$init512[$i] = new Math_BigInteger($init512[$i], 16);
$init512[$i]->setPrecision(64);
}
// Initialize table of round constants
// (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
$k = array(
'428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
'3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
'72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694',
'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
'2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5',
'983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4',
'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70',
'27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df',
'650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b',
'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30',
'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8',
'19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8',
'391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3',
'748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec',
'90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b',
'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
'06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
'28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
'4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
);
for ($i = 0; $i < 80; $i++) {
$k[$i] = new Math_BigInteger($k[$i], 16);
}
}
$hash = $this->l == 48 ? $init384 : $init512;
// Pre-processing
$length = strlen($m);
// to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128
$m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
$m.= pack('N4', 0, 0, 0, $length << 3);
// Process the message in successive 1024-bit chunks
$chunks = str_split($m, 128);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
$temp = new Math_BigInteger($this->_string_shift($chunk, 8), 256);
$temp->setPrecision(64);
$w[] = $temp;
}
// Extend the sixteen 32-bit words into eighty 32-bit words
for ($i = 16; $i < 80; $i++) {
$temp = array(
$w[$i - 15]->bitwise_rightRotate(1),
$w[$i - 15]->bitwise_rightRotate(8),
$w[$i - 15]->bitwise_rightShift(7)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$w[$i - 2]->bitwise_rightRotate(19),
$w[$i - 2]->bitwise_rightRotate(61),
$w[$i - 2]->bitwise_rightShift(6)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$w[$i] = $w[$i - 16]->copy();
$w[$i] = $w[$i]->add($s0);
$w[$i] = $w[$i]->add($w[$i - 7]);
$w[$i] = $w[$i]->add($s1);
}
// Initialize hash value for this chunk
$a = $hash[0]->copy();
$b = $hash[1]->copy();
$c = $hash[2]->copy();
$d = $hash[3]->copy();
$e = $hash[4]->copy();
$f = $hash[5]->copy();
$g = $hash[6]->copy();
$h = $hash[7]->copy();
// Main loop
for ($i = 0; $i < 80; $i++) {
$temp = array(
$a->bitwise_rightRotate(28),
$a->bitwise_rightRotate(34),
$a->bitwise_rightRotate(39)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$a->bitwise_and($b),
$a->bitwise_and($c),
$b->bitwise_and($c)
);
$maj = $temp[0]->bitwise_xor($temp[1]);
$maj = $maj->bitwise_xor($temp[2]);
$t2 = $s0->add($maj);
$temp = array(
$e->bitwise_rightRotate(14),
$e->bitwise_rightRotate(18),
$e->bitwise_rightRotate(41)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$temp = array(
$e->bitwise_and($f),
$g->bitwise_and($e->bitwise_not())
);
$ch = $temp[0]->bitwise_xor($temp[1]);
$t1 = $h->add($s1);
$t1 = $t1->add($ch);
$t1 = $t1->add($k[$i]);
$t1 = $t1->add($w[$i]);
$h = $g->copy();
$g = $f->copy();
$f = $e->copy();
$e = $d->add($t1);
$d = $c->copy();
$c = $b->copy();
$b = $a->copy();
$a = $t1->add($t2);
}
// Add this chunk's hash to result so far
$hash = array(
$hash[0]->add($a),
$hash[1]->add($b),
$hash[2]->add($c),
$hash[3]->add($d),
$hash[4]->add($e),
$hash[5]->add($f),
$hash[6]->add($g),
$hash[7]->add($h)
);
}
// Produce the final hash value (big-endian)
// (Crypt_Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here)
$temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
$hash[4]->toBytes() . $hash[5]->toBytes();
if ($this->l != 48) {
$temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
}
return $temp;
}
/**
* Right Rotate
*
* @access private
* @param Integer $int
* @param Integer $amt
* @see _sha256()
* @return Integer
*/
function _rightRotate($int, $amt)
{
$invamt = 32 - $amt;
$mask = (1 << $invamt) - 1;
return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask);
}
/**
* Right Shift
*
* @access private
* @param Integer $int
* @param Integer $amt
* @see _sha256()
* @return Integer
*/
function _rightShift($int, $amt)
{
$mask = (1 << (32 - $amt)) - 1;
return ($int >> $amt) & $mask;
}
/**
* Not
*
* @access private
* @param Integer $int
* @see _sha256()
* @return Integer
*/
function _not($int)
{
return ~$int & 0xFFFFFFFF;
}
/**
* Add
*
* _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the
* possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster.
*
* @param String $string
* @param optional Integer $index
* @return String
* @see _sha256()
* @access private
*/
function _add()
{
static $mod;
if (!isset($mod)) {
$mod = pow(2, 32);
}
$result = 0;
$arguments = func_get_args();
foreach ($arguments as $argument) {
$result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
}
return fmod($result, $mod);
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param String $string
* @param optional Integer $index
* @return String
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
}

View file

@ -0,0 +1,493 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Pure-PHP implementation of RC4.
*
* Uses mcrypt, if available, and an internal implementation, otherwise.
*
* PHP versions 4 and 5
*
* Useful resources are as follows:
*
* - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
* - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
*
* RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at Wikipedia. This class is named RC4 and not
* ARCFOUR or ARC4 because RC4 is how it is refered to in the SSH1 specification.
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include('Crypt/RC4.php');
*
* $rc4 = new Crypt_RC4();
*
* $rc4->setKey('abcdefgh');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $rc4->decrypt($rc4->encrypt($plaintext));
* ?>
* </code>
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Crypt
* @package Crypt_RC4
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
/**#@+
* @access private
* @see Crypt_RC4::Crypt_RC4()
*/
/**
* Toggles the internal implementation
*/
define('CRYPT_RC4_MODE_INTERNAL', 1);
/**
* Toggles the mcrypt implementation
*/
define('CRYPT_RC4_MODE_MCRYPT', 2);
/**#@-*/
/**#@+
* @access private
* @see Crypt_RC4::_crypt()
*/
define('CRYPT_RC4_ENCRYPT', 0);
define('CRYPT_RC4_DECRYPT', 1);
/**#@-*/
/**
* Pure-PHP implementation of RC4.
*
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Crypt_RC4
*/
class Crypt_RC4 {
/**
* The Key
*
* @see Crypt_RC4::setKey()
* @var String
* @access private
*/
var $key = "\0";
/**
* The Key Stream for encryption
*
* If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
*
* @see Crypt_RC4::setKey()
* @var Array
* @access private
*/
var $encryptStream = false;
/**
* The Key Stream for decryption
*
* If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
*
* @see Crypt_RC4::setKey()
* @var Array
* @access private
*/
var $decryptStream = false;
/**
* The $i and $j indexes for encryption
*
* @see Crypt_RC4::_crypt()
* @var Integer
* @access private
*/
var $encryptIndex = 0;
/**
* The $i and $j indexes for decryption
*
* @see Crypt_RC4::_crypt()
* @var Integer
* @access private
*/
var $decryptIndex = 0;
/**
* MCrypt parameters
*
* @see Crypt_RC4::setMCrypt()
* @var Array
* @access private
*/
var $mcrypt = array('', '');
/**
* The Encryption Algorithm
*
* Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.
*
* @see Crypt_RC4::Crypt_RC4()
* @var Integer
* @access private
*/
var $mode;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used.
*
* @param optional Integer $mode
* @return Crypt_RC4
* @access public
*/
function Crypt_RC4()
{
if ( !defined('CRYPT_RC4_MODE') ) {
switch (true) {
case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')):
// i'd check to see if rc4 was supported, by doing in_array('arcfour', mcrypt_list_algorithms('')),
// but since that can be changed after the object has been created, there doesn't seem to be
// a lot of point...
define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT);
break;
default:
define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL);
}
}
switch ( CRYPT_RC4_MODE ) {
case CRYPT_RC4_MODE_MCRYPT:
switch (true) {
case defined('MCRYPT_ARCFOUR'):
$this->mode = MCRYPT_ARCFOUR;
break;
case defined('MCRYPT_RC4');
$this->mode = MCRYPT_RC4;
}
}
}
/**
* Sets the key.
*
* Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
* be used. If no key is explicitly set, it'll be assumed to be a single null byte.
*
* @access public
* @param String $key
*/
function setKey($key)
{
$this->key = $key;
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
return;
}
$keyLength = strlen($key);
$keyStream = array();
for ($i = 0; $i < 256; $i++) {
$keyStream[$i] = $i;
}
$j = 0;
for ($i = 0; $i < 256; $i++) {
$j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
$temp = $keyStream[$i];
$keyStream[$i] = $keyStream[$j];
$keyStream[$j] = $temp;
}
$this->encryptIndex = $this->decryptIndex = array(0, 0);
$this->encryptStream = $this->decryptStream = $keyStream;
}
/**
* Dummy function.
*
* Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
* If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
* calling setKey().
*
* [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
* the IV's are relatively easy to predict, an attack described by
* {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
* can be used to quickly guess at the rest of the key. The following links elaborate:
*
* {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
* {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
*
* @param String $iv
* @see Crypt_RC4::setKey()
* @access public
*/
function setIV($iv)
{
}
/**
* Sets MCrypt parameters. (optional)
*
* If MCrypt is being used, empty strings will be used, unless otherwise specified.
*
* @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open
* @access public
* @param optional Integer $algorithm_directory
* @param optional Integer $mode_directory
*/
function setMCrypt($algorithm_directory = '', $mode_directory = '')
{
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
$this->mcrypt = array($algorithm_directory, $mode_directory);
$this->_closeMCrypt();
}
}
/**
* Encrypts a message.
*
* @see Crypt_RC4::_crypt()
* @access public
* @param String $plaintext
*/
function encrypt($plaintext)
{
return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);
}
/**
* Decrypts a message.
*
* $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
* Atleast if the continuous buffer is disabled.
*
* @see Crypt_RC4::_crypt()
* @access public
* @param String $ciphertext
*/
function decrypt($ciphertext)
{
return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);
}
/**
* Encrypts or decrypts a message.
*
* @see Crypt_RC4::encrypt()
* @see Crypt_RC4::decrypt()
* @access private
* @param String $text
* @param Integer $mode
*/
function _crypt($text, $mode)
{
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
$keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream';
if ($this->$keyStream === false) {
$this->$keyStream = mcrypt_module_open($this->mode, $this->mcrypt[0], MCRYPT_MODE_STREAM, $this->mcrypt[1]);
mcrypt_generic_init($this->$keyStream, $this->key, '');
} else if (!$this->continuousBuffer) {
mcrypt_generic_init($this->$keyStream, $this->key, '');
}
$newText = mcrypt_generic($this->$keyStream, $text);
if (!$this->continuousBuffer) {
mcrypt_generic_deinit($this->$keyStream);
}
return $newText;
}
if ($this->encryptStream === false) {
$this->setKey($this->key);
}
switch ($mode) {
case CRYPT_RC4_ENCRYPT:
$keyStream = $this->encryptStream;
list($i, $j) = $this->encryptIndex;
break;
case CRYPT_RC4_DECRYPT:
$keyStream = $this->decryptStream;
list($i, $j) = $this->decryptIndex;
}
$newText = '';
for ($k = 0; $k < strlen($text); $k++) {
$i = ($i + 1) & 255;
$j = ($j + $keyStream[$i]) & 255;
$temp = $keyStream[$i];
$keyStream[$i] = $keyStream[$j];
$keyStream[$j] = $temp;
$temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255];
$newText.= chr(ord($text[$k]) ^ $temp);
}
if ($this->continuousBuffer) {
switch ($mode) {
case CRYPT_RC4_ENCRYPT:
$this->encryptStream = $keyStream;
$this->encryptIndex = array($i, $j);
break;
case CRYPT_RC4_DECRYPT:
$this->decryptStream = $keyStream;
$this->decryptIndex = array($i, $j);
}
}
return $newText;
}
/**
* Treat consecutive "packets" as if they are a continuous buffer.
*
* Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
* will yield different outputs:
*
* <code>
* echo $rc4->encrypt(substr($plaintext, 0, 8));
* echo $rc4->encrypt(substr($plaintext, 8, 8));
* </code>
* <code>
* echo $rc4->encrypt($plaintext);
* </code>
*
* The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
* another, as demonstrated with the following:
*
* <code>
* $rc4->encrypt(substr($plaintext, 0, 8));
* echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
* </code>
* <code>
* echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
* </code>
*
* With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
*
* Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
* however, they are also less intuitive and more likely to cause you problems.
*
* @see Crypt_RC4::disableContinuousBuffer()
* @access public
*/
function enableContinuousBuffer()
{
$this->continuousBuffer = true;
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* @see Crypt_RC4::enableContinuousBuffer()
* @access public
*/
function disableContinuousBuffer()
{
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) {
$this->encryptIndex = $this->decryptIndex = array(0, 0);
$this->setKey($this->key);
}
$this->continuousBuffer = false;
}
/**
* Dummy function.
*
* Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is
* included is so that you can switch between a block cipher and a stream cipher transparently.
*
* @see Crypt_RC4::disablePadding()
* @access public
*/
function enablePadding()
{
}
/**
* Dummy function.
*
* @see Crypt_RC4::enablePadding()
* @access public
*/
function disablePadding()
{
}
/**
* Class destructor.
*
* Will be called, automatically, if you're using PHP5. If you're using PHP4, call it yourself. Only really
* needs to be called if mcrypt is being used.
*
* @access public
*/
function __destruct()
{
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
$this->_closeMCrypt();
}
}
/**
* Properly close the MCrypt objects.
*
* @access prviate
*/
function _closeMCrypt()
{
if ( $this->encryptStream !== false ) {
if ( $this->continuousBuffer ) {
mcrypt_generic_deinit($this->encryptStream);
}
mcrypt_module_close($this->encryptStream);
$this->encryptStream = false;
}
if ( $this->decryptStream !== false ) {
if ( $this->continuousBuffer ) {
mcrypt_generic_deinit($this->decryptStream);
}
mcrypt_module_close($this->decryptStream);
$this->decryptStream = false;
}
}
}

2119
library/phpsec/Crypt/RSA.php Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,130 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Random Number Generator
*
* PHP versions 4 and 5
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include('Crypt/Random.php');
*
* echo crypt_random();
* ?>
* </code>
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Crypt
* @package Crypt_Random
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
/**
* Generate a random value.
*
* On 32-bit machines, the largest distance that can exist between $min and $max is 2**31.
* If $min and $max are farther apart than that then the last ($max - range) numbers.
*
* Depending on how this is being used, it may be worth while to write a replacement. For example,
* a PHP-based web app that stores its data in an SQL database can collect more entropy than this function
* can.
*
* @param optional Integer $min
* @param optional Integer $max
* @return Integer
* @access public
*/
function crypt_random($min = 0, $max = 0x7FFFFFFF)
{
if ($min == $max) {
return $min;
}
// see http://en.wikipedia.org/wiki//dev/random
// if open_basedir is enabled file_exists() will ouput an "open_basedir restriction in effect" warning,
// so we suppress it.
if (@file_exists('/dev/urandom')) {
static $fp;
if (!$fp) {
$fp = fopen('/dev/urandom', 'rb');
}
extract(unpack('Nrandom', fread($fp, 4)));
// say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
// -4 % 3 + 0 = -1, even though -1 < $min
return abs($random) % ($max - $min) + $min;
}
/* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3_2/ext/standard/php_rand.h?view=markup */
if (version_compare(PHP_VERSION, '5.2.5', '<=')) {
static $seeded;
if (!isset($seeded)) {
$seeded = true;
mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
}
}
static $crypto;
// The CSPRNG's Yarrow and Fortuna periodically reseed. This function can be reseeded by hitting F5
// in the browser and reloading the page.
if (!isset($crypto)) {
$key = $iv = '';
for ($i = 0; $i < 8; $i++) {
$key.= pack('n', mt_rand(0, 0xFFFF));
$iv .= pack('n', mt_rand(0, 0xFFFF));
}
switch (true) {
case class_exists('Crypt_AES'):
$crypto = new Crypt_AES(CRYPT_AES_MODE_CTR);
break;
case class_exists('Crypt_TripleDES'):
$crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
break;
case class_exists('Crypt_DES'):
$crypto = new Crypt_DES(CRYPT_DES_MODE_CTR);
break;
case class_exists('Crypt_RC4'):
$crypto = new Crypt_RC4();
break;
default:
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF)))));
return abs($random) % ($max - $min) + $min;
}
$crypto->setKey($key);
$crypto->setIV($iv);
$crypto->enableContinuousBuffer();
}
extract(unpack('Nrandom', $crypto->encrypt("\0\0\0\0")));
return abs($random) % ($max - $min) + $min;
}
?>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,690 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Pure-PHP implementation of Triple DES.
*
* Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt).
*
* PHP versions 4 and 5
*
* Here's a short example of how to use this library:
* <code>
* <?php
* include('Crypt/TripleDES.php');
*
* $des = new Crypt_TripleDES();
*
* $des->setKey('abcdefghijklmnopqrstuvwx');
*
* $size = 10 * 1024;
* $plaintext = '';
* for ($i = 0; $i < $size; $i++) {
* $plaintext.= 'a';
* }
*
* echo $des->decrypt($des->encrypt($plaintext));
* ?>
* </code>
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Crypt
* @package Crypt_TripleDES
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
/**
* Include Crypt_DES
*/
require_once 'DES.php';
/**
* Encrypt / decrypt using inner chaining
*
* Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3).
*/
define('CRYPT_DES_MODE_3CBC', 3);
/**
* Encrypt / decrypt using outer chaining
*
* Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC.
*/
define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC);
/**
* Pure-PHP implementation of Triple DES.
*
* @author Jim Wigginton <terrafrost@php.net>
* @version 0.1.0
* @access public
* @package Crypt_TerraDES
*/
class Crypt_TripleDES {
/**
* The Three Keys
*
* @see Crypt_TripleDES::setKey()
* @var String
* @access private
*/
var $key = "\0\0\0\0\0\0\0\0";
/**
* The Encryption Mode
*
* @see Crypt_TripleDES::Crypt_TripleDES()
* @var Integer
* @access private
*/
var $mode = CRYPT_DES_MODE_CBC;
/**
* Continuous Buffer status
*
* @see Crypt_TripleDES::enableContinuousBuffer()
* @var Boolean
* @access private
*/
var $continuousBuffer = false;
/**
* Padding status
*
* @see Crypt_TripleDES::enablePadding()
* @var Boolean
* @access private
*/
var $padding = true;
/**
* The Initialization Vector
*
* @see Crypt_TripleDES::setIV()
* @var String
* @access private
*/
var $iv = "\0\0\0\0\0\0\0\0";
/**
* A "sliding" Initialization Vector
*
* @see Crypt_TripleDES::enableContinuousBuffer()
* @var String
* @access private
*/
var $encryptIV = "\0\0\0\0\0\0\0\0";
/**
* A "sliding" Initialization Vector
*
* @see Crypt_TripleDES::enableContinuousBuffer()
* @var String
* @access private
*/
var $decryptIV = "\0\0\0\0\0\0\0\0";
/**
* The Crypt_DES objects
*
* @var Array
* @access private
*/
var $des;
/**
* mcrypt resource for encryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::encrypt()
* @var String
* @access private
*/
var $enmcrypt;
/**
* mcrypt resource for decryption
*
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
*
* @see Crypt_AES::decrypt()
* @var String
* @access private
*/
var $demcrypt;
/**
* Does the (en|de)mcrypt resource need to be (re)initialized?
*
* @see setKey()
* @see setIV()
* @var Boolean
* @access private
*/
var $changed = true;
/**
* Default Constructor.
*
* Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
* CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
*
* @param optional Integer $mode
* @return Crypt_TripleDES
* @access public
*/
function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC)
{
if ( !defined('CRYPT_DES_MODE') ) {
switch (true) {
case extension_loaded('mcrypt'):
// i'd check to see if des was supported, by doing in_array('des', mcrypt_list_algorithms('')),
// but since that can be changed after the object has been created, there doesn't seem to be
// a lot of point...
define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
break;
default:
define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
}
}
if ( $mode == CRYPT_DES_MODE_3CBC ) {
$this->mode = CRYPT_DES_MODE_3CBC;
$this->des = array(
new Crypt_DES(CRYPT_DES_MODE_CBC),
new Crypt_DES(CRYPT_DES_MODE_CBC),
new Crypt_DES(CRYPT_DES_MODE_CBC)
);
// we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
$this->des[0]->disablePadding();
$this->des[1]->disablePadding();
$this->des[2]->disablePadding();
return;
}
switch ( CRYPT_DES_MODE ) {
case CRYPT_DES_MODE_MCRYPT:
switch ($mode) {
case CRYPT_DES_MODE_ECB:
$this->mode = MCRYPT_MODE_ECB;
break;
case CRYPT_DES_MODE_CTR:
$this->mode = 'ctr';
break;
case CRYPT_DES_MODE_CBC:
default:
$this->mode = MCRYPT_MODE_CBC;
}
break;
default:
$this->des = array(
new Crypt_DES(CRYPT_DES_MODE_ECB),
new Crypt_DES(CRYPT_DES_MODE_ECB),
new Crypt_DES(CRYPT_DES_MODE_ECB)
);
// we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
$this->des[0]->disablePadding();
$this->des[1]->disablePadding();
$this->des[2]->disablePadding();
switch ($mode) {
case CRYPT_DES_MODE_ECB:
case CRYPT_DES_MODE_CTR:
case CRYPT_DES_MODE_CBC:
$this->mode = $mode;
break;
default:
$this->mode = CRYPT_DES_MODE_CBC;
}
}
}
/**
* Sets the key.
*
* Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or
* 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate.
*
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
*
* If the key is not explicitly set, it'll be assumed to be all zero's.
*
* @access public
* @param String $key
*/
function setKey($key)
{
$length = strlen($key);
if ($length > 8) {
$key = str_pad($key, 24, chr(0));
// if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
// http://php.net/function.mcrypt-encrypt#47973
//$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
}
$this->key = $key;
switch (true) {
case CRYPT_DES_MODE == CRYPT_DES_MODE_INTERNAL:
case $this->mode == CRYPT_DES_MODE_3CBC:
$this->des[0]->setKey(substr($key, 0, 8));
$this->des[1]->setKey(substr($key, 8, 8));
$this->des[2]->setKey(substr($key, 16, 8));
}
$this->changed = true;
}
/**
* Sets the initialization vector. (optional)
*
* SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
* to be all zero's.
*
* @access public
* @param String $iv
*/
function setIV($iv)
{
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
if ($this->mode == CRYPT_DES_MODE_3CBC) {
$this->des[0]->setIV($iv);
$this->des[1]->setIV($iv);
$this->des[2]->setIV($iv);
}
$this->changed = true;
}
/**
* Generate CTR XOR encryption key
*
* Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
* plaintext / ciphertext in CTR mode.
*
* @see Crypt_DES::decrypt()
* @see Crypt_DES::encrypt()
* @access public
* @param Integer $length
* @param String $iv
*/
function _generate_xor($length, &$iv)
{
$xor = '';
$num_blocks = ($length + 7) >> 3;
for ($i = 0; $i < $num_blocks; $i++) {
$xor.= $iv;
for ($j = 4; $j <= 8; $j+=4) {
$temp = substr($iv, -$j, 4);
switch ($temp) {
case "\xFF\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
break;
case "\x7F\xFF\xFF\xFF":
$iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
break 2;
default:
extract(unpack('Ncount', $temp));
$iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
break 2;
}
}
}
return $xor;
}
/**
* Encrypts a message.
*
* @access public
* @param String $plaintext
*/
function encrypt($plaintext)
{
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
$plaintext = $this->_pad($plaintext);
}
// if the key is smaller then 8, do what we'd normally do
if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
$ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext)));
return $ciphertext;
}
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->changed) {
if (!isset($this->enmcrypt)) {
$this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
}
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
$this->changed = false;
}
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
}
return $ciphertext;
}
if (strlen($this->key) <= 8) {
$this->des[0]->mode = $this->mode;
return $this->des[0]->encrypt($plaintext);
}
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
$plaintext = str_pad($plaintext, ceil(strlen($plaintext) / 8) * 8, chr(0));
$des = $this->des;
$ciphertext = '';
switch ($this->mode) {
case CRYPT_DES_MODE_ECB:
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8);
$block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
$ciphertext.= $block;
}
break;
case CRYPT_DES_MODE_CBC:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$block = substr($plaintext, $i, 8) ^ $xor;
$block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
$xor = $block;
$ciphertext.= $block;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->encryptIV;
for ($i = 0; $i < strlen($plaintext); $i+=8) {
$key = $this->_generate_xor(8, $xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$block = substr($plaintext, $i, 8);
$ciphertext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->encryptIV = $xor;
}
}
return $ciphertext;
}
/**
* Decrypts a message.
*
* @access public
* @param String $ciphertext
*/
function decrypt($ciphertext)
{
if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
$plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext)));
return $this->_unpad($plaintext);
}
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
$ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
if ($this->changed) {
if (!isset($this->demcrypt)) {
$this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
}
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
$this->changed = false;
}
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
if (!$this->continuousBuffer) {
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
}
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
}
if (strlen($this->key) <= 8) {
$this->des[0]->mode = $this->mode;
return $this->_unpad($this->des[0]->decrypt($plaintext));
}
$des = $this->des;
$plaintext = '';
switch ($this->mode) {
case CRYPT_DES_MODE_ECB:
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$block = substr($ciphertext, $i, 8);
$block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
$plaintext.= $block;
}
break;
case CRYPT_DES_MODE_CBC:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$orig = $block = substr($ciphertext, $i, 8);
$block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
$block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
$block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
$plaintext.= $block ^ $xor;
$xor = $orig;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
break;
case CRYPT_DES_MODE_CTR:
$xor = $this->decryptIV;
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
$key = $this->_generate_xor(8, $xor);
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
$block = substr($ciphertext, $i, 8);
$plaintext.= $block ^ $key;
}
if ($this->continuousBuffer) {
$this->decryptIV = $xor;
}
}
return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
}
/**
* Treat consecutive "packets" as if they are a continuous buffer.
*
* Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
* will yield different outputs:
*
* <code>
* echo $des->encrypt(substr($plaintext, 0, 8));
* echo $des->encrypt(substr($plaintext, 8, 8));
* </code>
* <code>
* echo $des->encrypt($plaintext);
* </code>
*
* The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
* another, as demonstrated with the following:
*
* <code>
* $des->encrypt(substr($plaintext, 0, 8));
* echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
* </code>
* <code>
* echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
* </code>
*
* With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
*
* Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
* however, they are also less intuitive and more likely to cause you problems.
*
* @see Crypt_TripleDES::disableContinuousBuffer()
* @access public
*/
function enableContinuousBuffer()
{
$this->continuousBuffer = true;
if ($this->mode == CRYPT_DES_MODE_3CBC) {
$this->des[0]->enableContinuousBuffer();
$this->des[1]->enableContinuousBuffer();
$this->des[2]->enableContinuousBuffer();
}
}
/**
* Treat consecutive packets as if they are a discontinuous buffer.
*
* The default behavior.
*
* @see Crypt_TripleDES::enableContinuousBuffer()
* @access public
*/
function disableContinuousBuffer()
{
$this->continuousBuffer = false;
$this->encryptIV = $this->iv;
$this->decryptIV = $this->iv;
if ($this->mode == CRYPT_DES_MODE_3CBC) {
$this->des[0]->disableContinuousBuffer();
$this->des[1]->disableContinuousBuffer();
$this->des[2]->disableContinuousBuffer();
}
}
/**
* Pad "packets".
*
* DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
* a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
*
* Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
* where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
* away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
* transmitted separately)
*
* @see Crypt_TripleDES::disablePadding()
* @access public
*/
function enablePadding()
{
$this->padding = true;
}
/**
* Do not pad packets.
*
* @see Crypt_TripleDES::enablePadding()
* @access public
*/
function disablePadding()
{
$this->padding = false;
}
/**
* Pads a string
*
* Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
* 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
*
* If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
* and padding will, hence forth, be enabled.
*
* @see Crypt_TripleDES::_unpad()
* @access private
*/
function _pad($text)
{
$length = strlen($text);
if (!$this->padding) {
if (($length & 7) == 0) {
return $text;
} else {
user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE);
$this->padding = true;
}
}
$pad = 8 - ($length & 7);
return str_pad($text, $length + $pad, chr($pad));
}
/**
* Unpads a string
*
* If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
* and false will be returned.
*
* @see Crypt_TripleDES::_pad()
* @access private
*/
function _unpad($text)
{
if (!$this->padding) {
return $text;
}
$length = ord($text[strlen($text) - 1]);
if (!$length || $length > 8) {
return false;
}
return substr($text, 0, -$length);
}
}
// vim: ts=4:sw=4:et:
// vim6: fdl=1:

File diff suppressed because it is too large Load diff

1461
library/phpsec/Net/SFTP.php Normal file

File diff suppressed because it is too large Load diff

1160
library/phpsec/Net/SSH1.php Normal file

File diff suppressed because it is too large Load diff

2302
library/phpsec/Net/SSH2.php Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
<?php
// $Id: array_fill.php,v 1.1 2007/07/02 04:19:55 terrafrost Exp $
/**
* Replace array_fill()
*
* @category PHP
* @package PHP_Compat
* @license LGPL - http://www.gnu.org/licenses/lgpl.html
* @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
* @link http://php.net/function.array_fill
* @author Jim Wigginton <terrafrost@php.net>
* @version $Revision: 1.1 $
* @since PHP 4.2.0
*/
function php_compat_array_fill($start_index, $num, $value)
{
if ($num <= 0) {
user_error('array_fill(): Number of elements must be positive', E_USER_WARNING);
return false;
}
$temp = array();
$end_index = $start_index + $num;
for ($i = (int) $start_index; $i < $end_index; $i++) {
$temp[$i] = $value;
}
return $temp;
}
// Define
if (!function_exists('array_fill')) {
function array_fill($start_index, $num, $value)
{
return php_compat_array_fill($start_index, $num, $value);
}
}
?>

View file

@ -0,0 +1,67 @@
<?php
// $Id: bcpowmod.php,v 1.1 2007/07/02 04:19:55 terrafrost Exp $
/**
* Replace bcpowmod()
*
* @category PHP
* @package PHP_Compat
* @license LGPL - http://www.gnu.org/licenses/lgpl.html
* @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
* @link http://php.net/function.bcpowmod
* @author Sara Golemon <pollita@php.net>
* @version $Revision: 1.1 $
* @since PHP 5.0.0
* @require PHP 4.0.0 (user_error)
*/
function php_compat_bcpowmod($x, $y, $modulus, $scale = 0)
{
// Sanity check
if (!is_scalar($x)) {
user_error('bcpowmod() expects parameter 1 to be string, ' .
gettype($x) . ' given', E_USER_WARNING);
return false;
}
if (!is_scalar($y)) {
user_error('bcpowmod() expects parameter 2 to be string, ' .
gettype($y) . ' given', E_USER_WARNING);
return false;
}
if (!is_scalar($modulus)) {
user_error('bcpowmod() expects parameter 3 to be string, ' .
gettype($modulus) . ' given', E_USER_WARNING);
return false;
}
if (!is_scalar($scale)) {
user_error('bcpowmod() expects parameter 4 to be integer, ' .
gettype($scale) . ' given', E_USER_WARNING);
return false;
}
$t = '1';
while (bccomp($y, '0')) {
if (bccomp(bcmod($y, '2'), '0')) {
$t = bcmod(bcmul($t, $x), $modulus);
$y = bcsub($y, '1');
}
$x = bcmod(bcmul($x, $x), $modulus);
$y = bcdiv($y, '2');
}
return $t;
}
// Define
if (!function_exists('bcpowmod')) {
function bcpowmod($x, $y, $modulus, $scale = 0)
{
return php_compat_bcpowmod($x, $y, $modulus, $scale);
}
}
?>

View file

@ -0,0 +1,59 @@
<?php
/**
* Replace str_split()
*
* @category PHP
* @package PHP_Compat
* @license LGPL - http://www.gnu.org/licenses/lgpl.html
* @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
* @link http://php.net/function.str_split
* @author Aidan Lister <aidan@php.net>
* @version $Revision: 1.1 $
* @since PHP 5
* @require PHP 4.0.0 (user_error)
*/
function php_compat_str_split($string, $split_length = 1)
{
if (!is_scalar($split_length)) {
user_error('str_split() expects parameter 2 to be long, ' .
gettype($split_length) . ' given', E_USER_WARNING);
return false;
}
$split_length = (int) $split_length;
if ($split_length < 1) {
user_error('str_split() The length of each segment must be greater than zero', E_USER_WARNING);
return false;
}
// Select split method
if ($split_length < 65536) {
// Faster, but only works for less than 2^16
preg_match_all('/.{1,' . $split_length . '}/s', $string, $matches);
return $matches[0];
} else {
// Required due to preg limitations
$arr = array();
$idx = 0;
$pos = 0;
$len = strlen($string);
while ($len > 0) {
$blk = ($len < $split_length) ? $len : $split_length;
$arr[$idx++] = substr($string, $pos, $blk);
$pos += $blk;
$len -= $blk;
}
return $arr;
}
}
// Define
if (!function_exists('str_split')) {
function str_split($string, $split_length = 1)
{
return php_compat_str_split($string, $split_length);
}
}

135
library/phpsec/crypt.html Normal file
View file

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 3. Cryptography</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="math.html" title="Chapter 2. Math" /><link rel="next" href="net.html" title="Chapter 4. Networking" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 3. Cryptography</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="math.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="net.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="crypt"></a>Chapter 3. Cryptography</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="crypt.html#crypt_intro">3.1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_dependencies">3.1.1. Dependencies</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_set">3.1.2. setKey() and setIV()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_setmcrypt">3.1.3. setMCrypt()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_encrypt">3.1.4. encrypt() and decrypt()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_continuousbuffer">3.1.5. enableContinuousBuffer() and disableContinuousBuffer()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_padding">3.1.6. enablePadding() and disablePadding()</a></span></dt></dl></dd><dt><span class="section"><a href="crypt.html#crypt_des">3.2. Crypt_DES</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_des_constructor">3.2.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="crypt.html#crypt_tripledes">3.3. Crypt_TripleDES</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_tripledes_constructor">3.3.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="crypt.html#crypt_rc4">3.4. Crypt_RC4</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_rc4_constructor">3.4.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="crypt.html#crypt_aes">3.5. Crypt_Rijndael &amp; Crypt_AES</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_aes_constructor">3.5.1. The constructor</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_aes_vs_rijndael">3.5.2. AES vs. Rijndael</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_aes_setkeylength">3.5.3. setKeyLength()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_aes_setblocklength">3.5.4. setBlockLength()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_aes_benchmarks">3.5.5. Speed Comparisons</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_intro"></a>3.1. Introduction</h2></div></div></div><p>
All of the cryptographic libraries included in phpseclib use mcrypt, if available, and an internal implementation
if it's not. The libraries all use a common interface although some functions, for some algorithms, carry with
with them certain caveats. Those that do not have caveats attached (or have relatively few attached) are
described below. If you don't know which one to use, try <code class="code">Crypt_TripleDES</code>.
</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_dependencies"></a>3.1.1. Dependencies</h3></div></div></div><p>
The Crypt_* functions require, minimally, PHP 4.0.0. Crypt_TripleDES additionally requires Crypt/DES.php.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_set"></a>3.1.2. setKey() and setIV()</h3></div></div></div><p>
Sets the key and the initialization vector, respectively. If neither are set, each assumed to be equal to
some amount of null bytes. The initialization vector is only used in block ciphers and even then only
in CBC mode. If the key or the initialization vector are larger then the block size, they're truncated.
If they're smaller, they're padded with null bytes.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_setmcrypt"></a>3.1.3. setMCrypt()</h3></div></div></div><p>
See php.net's entry on <a class="ulink" href="http://php.net/function.mcrypt-module-open#function.mcrypt-module-open" target="_top">mcrypt_module_open</a>.
The first parameter is equal to <code class="code">$algorithm_directory</code> and the second, to <code class="code">$mode_directory</code>.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_encrypt"></a>3.1.4. encrypt() and decrypt()</h3></div></div></div><p>
Self-explanatory. Encrypts or decrypts messages. See the examples in the subsequent sections.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_continuousbuffer"></a>3.1.5. enableContinuousBuffer() and disableContinuousBuffer()</h3></div></div></div><p>
Say you have a 16-byte plaintext $plaintext and that you're using <code class="code">Crypt_DES</code>. Using the default behavior, the two following code snippets
will yield different outputs:
</p><pre class="programlisting"> echo $des-&gt;encrypt(substr($plaintext, 0, 8));
echo $des-&gt;encrypt(substr($plaintext, 8, 8));</pre><pre class="programlisting"> echo $des-&gt;encrypt($plaintext);</pre><p>
The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
another, as demonstrated with the following:
</p><pre class="programlisting"> $des-&gt;encrypt(substr($plaintext, 0, 8));
echo $des-&gt;decrypt($des-&gt;encrypt(substr($plaintext, 8, 8)));</pre><pre class="programlisting"> echo $des-&gt;decrypt($des-&gt;encrypt(substr($plaintext, 8, 8)));</pre><p>
With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
outputs. The reason is due to the fact that the initialization vector's change after every encryption /
decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
Put another way, when the continuous buffer is enabled, the state of the <code class="code">Crypt_DES()</code> object changes after each
encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
however, they are also less intuitive and more likely to cause you problems.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_padding"></a>3.1.6. enablePadding() and disablePadding()</h3></div></div></div><p>
Enables / disables PKCS padding on block ciphers. Stream ciphers (<code class="code">Crypt_RC4</code> is the only stream
cipher currently included) ignore this.
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_des"></a>3.2. Crypt_DES</h2></div></div></div><p>
Implements DES (a block cipher). Here's an example of how to use it:
</p><pre class="programlisting">&lt;?php
include('Crypt/DES.php');
$des = new Crypt_DES();
$des-&gt;setKey('abcdefgh');
$size = 10 * 1024;
$plaintext = '';
for ($i = 0; $i &lt; $size; $i++) {
$plaintext.= 'a';
}
echo $des-&gt;decrypt($des-&gt;encrypt($plaintext));
?&gt;</pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_des_constructor"></a>3.2.1. The constructor</h3></div></div></div><p>
The constructor takes one optional parameter - $mode. $mode can be equal to <code class="code">CRYPT_DES_MODE_ECB</code>
or <code class="code">CRYPT_DES_MODE_CBC</code>. <code class="code">CRYPT_DES_MODE_CBC</code> is generally considered more secure
and is what <code class="code">Crypt_DES</code> uses by default. If you don't know the difference between ECB or CBC,
just use the default settings.
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_tripledes"></a>3.3. Crypt_TripleDES</h2></div></div></div><p>
Implements TripleDES (a block cipher). Here's an example of how to use it:
</p><pre class="programlisting">&lt;?php
include('Crypt/TripleDES.php');
$des = new Crypt_TripleDES();
$des-&gt;setKey('abcdefghijklmnopqrstuvwx');
$size = 10 * 1024;
$plaintext = '';
for ($i = 0; $i &lt; $size; $i++) {
$plaintext.= 'a';
}
echo $des-&gt;decrypt($des-&gt;encrypt($plaintext));
?&gt;</pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_tripledes_constructor"></a>3.3.1. The constructor</h3></div></div></div><p>
The constructor takes one optional parameter - $mode. $mode can be equal to <code class="code">CRYPT_DES_MODE_ECB</code>,
<code class="code">CRYPT_DES_MODE_CBC</code>, <code class="code">CRYPT_DES_MODE_3CBC</code>, or <code class="code">CRYPT_DES_MODE_CBC3</code>.
<code class="code">CRYPT_DES_MODE_CBC3</code> is an alias <code class="code">CRYPT_DES_MODE_CBC</code>. It's defined to distinguish
it from <code class="code">CRYPT_DES_MODE_3CBC</code>, which uses inner chaining to propogate the initialization vector.
SSH-1 uses this and it is generally considered to be less secure then <code class="code">CRYPT_DES_MODE_CBC3</code>,
which uses outer chaining (and is what SSH-2 uses).
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_rc4"></a>3.4. Crypt_RC4</h2></div></div></div><p>
Implements RC4 (a stream cipher). Here's an example of how to use it:
</p><pre class="programlisting">&lt;?php
include('Crypt/RC4.php');
$rc4 = new Crypt_RC4();
$rc4-&gt;setKey('abcdefghijklmnopqrstuvwx');
$size = 10 * 1024;
$plaintext = '';
for ($i = 0; $i &lt; $size; $i++) {
$plaintext.= 'a';
}
echo $rc4-&gt;decrypt($rc4-&gt;encrypt($plaintext));
?&gt;</pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_rc4_constructor"></a>3.4.1. The constructor</h3></div></div></div><p>
Not much to say about this constructor. Since it's a stream cipher, you don't need to worry about which
mode of operation to use.
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_aes"></a>3.5. Crypt_Rijndael &amp; Crypt_AES</h2></div></div></div><p>
Implements Rijndael / AES. Here's an example of how to use Crypt_AES:
</p><pre class="programlisting">&lt;?php
include('Crypt/AES.php');
$aes = new Crypt_AES();
$aes-&gt;setKey('abcdefghijklmnop');
$size = 10 * 1024;
$plaintext = '';
for ($i = 0; $i &lt; $size; $i++) {
$plaintext.= 'a';
}
echo $aes-&gt;decrypt($aes-&gt;encrypt($plaintext));
?&gt;</pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_constructor"></a>3.5.1. The constructor</h3></div></div></div><p>
<code class="code">Crypt_AES</code>'s constructor takes <code class="code">CRYPT_AES_MODE_ECB</code> and <code class="code">CRYPT_AES_MODE_CBC</code> as parameters. <code class="code">Crypt_Rijndael</code>, <code class="code">CRYPT_RIJNDAEL_MODE_ECB</code> and <code class="code">CRYPT_RIJNDAEL_MODE_CBC</code>. In both cases, if no valid mode is defined, CBC will be used.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_vs_rijndael"></a>3.5.2. AES vs. Rijndael</h3></div></div></div><p>
AES is a subset of Rijndael. Both have variable key sizes, however, AES's block size is fixed at 128 bytes, whereas Rijndael's is variable. Also, Rijndael supports, by means of an extension to the specification, two key sizes that AES does not - 160 bits and 224 bits.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_setkeylength"></a>3.5.3. setKeyLength()</h3></div></div></div><p>
Valid key lengths for AES are 128 bits, 192 bits, and 256 bits. If the key that is assigned is invalid and less than 256 bits, they key length is rounded up to the next closest valid size and the key will be null padded to that amount. If the key length is greater than 256 bits, it will be truncated to 256 bits.
</p><p>
As an example, if the key is 136 bits, it will be null padded to 192 bits (or 160 bits if Rijndael is being used).
</p><p>
If <code class="code">setKeyLength()</code> has been called, this behavior changes somewhat. Say you've set the key length, via this function, to 256 bits. Then, instead of an invalid key being null padded to 192 or 160 bits, it will be null padded to 256 bits.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_setblocklength"></a>3.5.4. setBlockLength()</h3></div></div></div><p>
<code class="code">setBlockLength()</code> operates in a manner similar to <code class="code">setKeyLength()</code>, with one exception. <code class="code">setBlockLength()</code> only works on Rijndael. Although <code class="code">Crypt_AES</code> inherits <code class="code">setBlockLength()</code> as a function, the function doesn't do anything in AES.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_benchmarks"></a>3.5.5. Speed Comparisons</h3></div></div></div><p>
The following table compares the speed of five different pure-PHP implementations of AES (one of which is Crypt_Rijndael and one of which is Crypt_AES) when ran on 150KB of text on a 1.8GHz Pentium 4-M. The numbers listed are averaged from five different trials and are measured in seconds. phpseclib's two implementations are highlighted. All implementations can be viewed by clicking on their names.
</p><div class="table"><a id="crypt_aes_benchmarks_table"></a><p class="title"><b>Table 3.1. AES Speed Comparisons</b></p><div class="table-contents"><table summary="AES Speed Comparisons" border="1"><colgroup><col /><col /><col /><col /><col /></colgroup><thead><tr><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/movable-type.phps" target="_top">movable-type.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpaes.phps" target="_top">phpaes.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpclasses1.phps" target="_top">phpclasses1.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpclasses2.phps" target="_top">phpclasses2.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpseclib-aes.phps" target="_top">phpseclib-aes.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpseclib-rijndael.phps" target="_top">phpseclib-rijndael.phps</a></th></tr></thead><tbody><tr><td align="right">15.6844158172</td><td align="right">39.9537248135</td><td align="right">15.0100150108</td><td align="right">62.591713190079</td><td class="highlight" align="right">3.5728311081</td><td class="highlight" align="right">5.24388728142</td></tr></tbody></table></div></div><br class="table-break" /><p>
As can be seen, phpseclib's implementations are the fastest. phpseclib-aes.phps is faster than phpseclib-rijndael.phps because phpseclib-rijndael.phps has to contend with multiple block sizes whereas phpseclib-aes.phps does not.
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="math.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="net.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 2. Math </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 4. Networking</td></tr></table></div></body></html>

View file

@ -0,0 +1,43 @@
body {
font-family: Georgia, "Times New Roman", Times, serif
}
div.author h3 {
display: none
}
a {
text-decoration: none;
color: #369
}
a:hover {
text-decoration: underline
}
.programlisting {
font-family: Monaco, "Andale Mono","Courier New", Courier, mono;
font-size: 10pt
}
.programlisting, .code {
background: #eee;
color: #181;
font-weight: bold
}
.red {
color: #e11
}
.highlight {
background: #ee1
}
thead {
background: #ccc
}
#crypt_aes_benchmarks_table.tbody {
font-weight: bold
}

File diff suppressed because one or more lines are too long

20
library/phpsec/intro.html Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 1. Introduction</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="index.html" title="PHP Secure Communications Library" /><link rel="next" href="math.html" title="Chapter 2. Math" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 1. Introduction</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="index.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="math.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="intro.html#intro_intro">1.1. Who should use phpseclib</a></span></dt><dt><span class="section"><a href="intro.html#intro_usage">1.2. Using phpseclib</a></span></dt></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="intro_intro"></a>1.1. Who should use phpseclib</h2></div></div></div><p>
Although many of the features this library implements are implemented in PHP via optional extensions, what are
you, as a developer, going to do when a user tries to run your software on a host which, coincidentally, doesn't
happen to have that optional extension installed? You could, flat-out, tell that user to look for another
software package that does work on their server (or to get another host, or whatever), which is liable to leave
a bad impression on the user, or you could use a library like this - a library that uses those optional
extensions if they're available and falls back on an internal PHP implementation if they're not.
</p><p>
Another advantage of using this library over optional PHP extensions is that you, as a developer, may find this
libraries API easier to use then extensions API.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="intro_usage"></a>1.2. Using phpseclib</h2></div></div></div><p>
This library is written using the same conventions that libraries in the PHP Extension and Application Repository (PEAR)
have been written in. In particular, this library expects to be in your <code class="code">include_path</code>:
</p><pre class="programlisting">&lt;?php
set_include_path(get_include_path() . PATH_SEPARATOR . 'phpseclib');
include('Net/SSH2.php');
?&gt;</pre></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="index.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="math.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">PHP <span class="red">Secure</span> Communications Library </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 2. Math</td></tr></table></div></body></html>

157
library/phpsec/math.html Normal file
View file

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 2. Math</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="intro.html" title="Chapter 1. Introduction" /><link rel="next" href="sym_crypt.html" title="Chapter 3. Symmetric-key Cryptography" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 2. Math</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="intro.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="sym_crypt.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="math"></a>Chapter 2. Math</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="math.html#math_biginteger">2.1. Math_BigInteger</a></span></dt><dd><dl><dt><span class="section"><a href="math.html#math_biginteger_dependencies">2.1.1. Dependencies</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_constructor">2.1.2. The constructor</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_output">2.1.3. toString(), toBytes(), toHex() and toBits()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_fourfunctions">2.1.4. add(), subtract(), multiply() and divide()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_modulo">2.1.5. powMod() and modInverse()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_gcd">2.1.6. gcd() and extendedGCD()</a></span></dt><dt><span class="section"><a href="math.html#math_abs">2.1.7. abs()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_compare">2.1.8. equals() and compare()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_precision">2.1.9. setPrecision()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_bitwise">2.1.10. bitwise_and(), bitwise_or(), bitwise_xor() and bitwise_not()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_shifts">2.1.11. bitwise_rightShift() and bitwise_leftShift()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_rotates">2.1.12. bitwise_rightRotate() and bitwise_leftRotate()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_setrandom">2.1.13. setRandomGenerator()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_prime">2.1.14. isPrime()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_random">2.1.15. random() and randomPrime()</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="math_biginteger"></a>2.1. Math_BigInteger</h2></div></div></div><p>
Implements an arbitrary precision integer arithmetic library. Uses gmp or bcmath, if available, and an
internal implementation, otherwise. Here's an example:
</p><pre class="programlisting">&lt;?php
include('Math/BigInteger.php');
$a = new Math_BigInteger(2);
$b = new Math_BigInteger(3);
$c = $a-&gt;add($b);
echo $c-&gt;toString(); // outputs 5
?&gt;</pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_dependencies"></a>2.1.1. Dependencies</h3></div></div></div><p>
If you're running PHP 5, Math_BigInteger's only dependancy is the PCRE extension (which is enabled by default). Math_BigInteger also works on PHP 4 if PHP/Compat/Function/array_fill.php and PHP/Compat/Function/bcpowmod.php are included.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_constructor"></a>2.1.2. The constructor</h3></div></div></div><p>
The constructor takes two parameters. The first is the number and the second represents the base. Both
are optional (if they're not provided, the Math_BigInteger object will assume a value of 0).
</p><p>
The supported bases are base-2, base-10 (default), base-16, and base-256. To set $a, in the
above example, to 2, using base-2, we'd do <code class="code">new Math_BigInteger('10', 2)</code>. To do it using
base-16, you could do <code class="code">new Math_BigInteger('2', 16)</code> or <code class="code">new Math_BigInteger('0x2', 16)</code>.
To set it to 2 using base-256, you'd do <code class="code">new Math_BigInteger(chr(2), 256)</code>.
</p><p>
If the base is negative (eg. -256), two's compliment will be used. Thus, <code class="code">new Math_BigInteger(chr(0xFF), -256)</code>
is equal to -1, as is <code class="code">new Math_BigInteger('0xFFFFFFFF', -16)</code> and <code class="code">new Math_BigInteger('11', -2)</code>.
Basically, if the leading bit is 1, the number is assumed to be negative.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_output"></a>2.1.3. toString(), toBytes(), toHex() and toBits()</h3></div></div></div><p>
<code class="code">toString()</code> returns the base-10 form of a number. <code class="code">toBytes()</code> returns the base-256
form of a number, <code class="code">toHex()</code> returns the base-16 form, and <code class="code">toBits()</code> the base-2 form.
<code class="code">toBytes()</code>, <code class="code">toHex()</code>, and <code class="code">toBits()</code> also take an optional parameter which,
if set, will return the two's compliment of a number. So if, for example, $a is equal to -1,
<code class="code">toBytes(true)</code> will return <code class="code">chr(0xFF)</code>.
</p><p>
On PHP 5, <code class="code">toString()</code> is called automatically when used in a string context via the
<a class="ulink" href="http://php.net/language.oop5.magic#language.oop5.magic.tostring" target="_top">__toString() magic method</a>.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_fourfunctions"></a>2.1.4. add(), subtract(), multiply() and divide()</h3></div></div></div><p>
<code class="code">subtract()</code> and <code class="code">multiply()</code> operate similarly to <code class="code">add()</code>. <code class="code">divide()</code>,
however, does not. Namely, it returns an array whose first element contains the quotient and whose
second element contains the "common residue". If the remainder would be positive, the "common residue"
and the remainder are the same. If the remainder would be negative, the "common residue" is equal to
the sum of the remainder and the divisor (basically, the "common residue" is the first positive modulo).
Here's an example:
</p><pre class="programlisting">&lt;?php
include('Math/BigInteger.php');
$a = new Math_BigInteger('10');
$b = new Math_BigInteger('20');
list($quotient, $remainder) = $a-&gt;divide($b);
echo $quotient-&gt;toString(); // outputs 0
echo "\r\n";
echo $remainder-&gt;toString(); // outputs 10
?&gt;</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_modulo"></a>2.1.5. powMod() and modInverse()</h3></div></div></div><p>
Examples of each follow:
</p><pre class="programlisting">&lt;?php
include('Math/BigInteger.php');
$a = new Math_BigInteger('10');
$b = new Math_BigInteger('20');
$c = new Math_BigInteger('30');
$c = $a-&gt;powMod($b, $c);
echo $c-&gt;toString(); // outputs 10
?&gt;</pre><pre class="programlisting">&lt;?php
include('Math/BigInteger.php');
$a = new Math_BigInteger(30);
$b = new Math_BigInteger(17);
$c = $a-&gt;modInverse($b);
echo $c-&gt;toString(); // outputs 4
?&gt;</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_gcd"></a>2.1.6. gcd() and extendedGCD()</h3></div></div></div><p>
<code class="code">extendedGCD()</code> returns an array containing three Math_BigInteger values indexed with x, y,
and gcd. x and y represent Bézout's identity. <code class="code">gcd()</code> returns a Math_BigInteger value
equal to the gcd. An example of each follows:
</p><pre class="programlisting">&lt;?php
include('Math/BigInteger.php');
$a = new Math_BigInteger(693);
$b = new Math_BigInteger(609);
extract($a-&gt;extendedGCD($b));
$c = $a-&gt;gcd($b);
echo $gcd-&gt;toString() . "\r\n"; // outputs 21
echo $c-&gt;toString() . "\r\n"; // outputs 21
echo $a-&gt;toString() * $x-&gt;toString() + $b-&gt;toString() * $y-&gt;toString(); // outputs 21
?&gt;</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_abs"></a>2.1.7. abs()</h3></div></div></div><p>
<code class="code">$x-&gt;abs()</code> returns the absolute value of <code class="code">$x</code>.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_compare"></a>2.1.8. equals() and compare()</h3></div></div></div><p>
<code class="code">$x-&gt;equals($y)</code> returns true or false depending on whether or not <code class="code">$x</code> and
<code class="code">$y</code> are equal.
</p><p>
<code class="code">$x-&gt;compare($y)</code> returns 1 if $x &gt; $y, 0 if $x == $y, and -1 if $x &lt; $y. The reason for this
is demonstrated thusly:
</p><pre class="programlisting">$x &gt; $y: $x-&gt;compare($y) &gt; 0
$x &lt; $y: $x-&gt;compare($y) &lt; 0
$x == $y: $x-&gt;compare($y) == 0
$x &gt;= $y: $x-&gt;compare($y) &gt;= 0
$x &lt;= $y: $x-&gt;compare($y) &lt;= 0</pre><p>
As a consequence of this, <code class="code">!$x-&gt;compare($y)</code> does not mean <code class="code">$x != $y</code> but rather
<code class="code">$x == $y</code>.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_precision"></a>2.1.9. setPrecision()</h3></div></div></div><p>
Some bitwise operations give different results depending on the precision being used. Examples include
left shift, not, and rotates, as discussed for <a class="link" href="math.html#math_biginteger_bitwise" title="2.1.10. bitwise_and(), bitwise_or(), bitwise_xor() and bitwise_not()">bitwise_not()</a>.
This function lets you control the precision.
</p><p>
Whenever a new Math_BigInteger object is created it's precision is set to the same precision as the
calling object. In other words, if you do <code class="code">$b = $a-&gt;bitwise_not()</code> then <code class="code">$b</code> will
have the same precision as <code class="code">$a</code>.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_bitwise"></a>2.1.10. bitwise_and(), bitwise_or(), bitwise_xor() and bitwise_not()</h3></div></div></div><p>
<code class="code">bitwise_and()</code>, <code class="code">bitwise_or()</code> and <code class="code">bitwise_xor()</code> operate similar to
<code class="code">add()</code>. <code class="code">bitwise_not()</code> is a bit more complicated. To elaborate, if the
precision (see <a class="link" href="math.html#math_biginteger_precision" title="2.1.9. setPrecision()">setPrecision</a>) is arbitrary,
<code class="code">$x-&gt;bitwise_not()</code> will always yield a smaller value since the most significant bit is
assumed to have a value of one. With fixed precision, however, the leading bit can be anything.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_shifts"></a>2.1.11. bitwise_rightShift() and bitwise_leftShift()</h3></div></div></div><p>
<code class="code">$a-&gt;bitwise_rightShift($shift)</code> shifts $a by $shift bits, effectively dividing by 2**$shift.
<code class="code">$a-&gt;bitwise_leftShift($shift)</code> shifts $a by $shift bits, effectively multiplying by 2**$shift.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_rotates"></a>2.1.12. bitwise_rightRotate() and bitwise_leftRotate()</h3></div></div></div><p>
<code class="code">$a-&gt;bitwise_rightRotate($shift)</code> and <code class="code">$a-&gt;bitwise_leftRotate($shift)</code> are
demonstrated thusly:
</p><pre class="programlisting">&lt;?php
include('Math/BigInteger.php');
$a = new Math_BigInteger('00111000', 2);
$a-&gt;setPrecision(8);
$b = $a-&gt;bitwise_leftRotate(2);
echo $b-&gt;toBits(); // returns 11100000
echo "\r\n";
$a = new Math_BigInteger('00111000', 2);
$b = $a-&gt;bitwise_leftRotate(2);
echo $b-&gt;toBits(); // returns 100011
?&gt;</pre><p>
Just as with <a class="link" href="math.html#math_biginteger_bitwise" title="2.1.10. bitwise_and(), bitwise_or(), bitwise_xor() and bitwise_not()">bitwise_not()</a>, these operations are
precision dependant.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_setrandom"></a>2.1.13. setRandomGenerator()</h3></div></div></div><p>
Sets the random generator. To set it to <code class="code">mt_rand()</code> (which is what it is by default), call
<code class="code">$x-&gt;setRandomGenerator('mt_rand')</code>.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_prime"></a>2.1.14. isPrime()</h3></div></div></div><p>
Returns true if a number is prime and false if it isn't.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_random"></a>2.1.15. random() and randomPrime()</h3></div></div></div><p>
<code class="code">random($min, $max)</code> generates a random number between <code class="code">$min</code> and <code class="code">$max</code>.
<code class="code">randomPrime($min, $max)</code> generates a random prime number between <code class="code">$min</code> and <code class="code">$max</code>.
If no prime number exists between <code class="code">$min</code> and <code class="code">$max</code> false is returned.
</p><p>
<code class="code">randomPrime()</code> has an optional third parameter, as well - $timeout. Generating prime numbers
is a particurarly expensive operation and although in certain environments even 512-bit primes can be
generated in a less than a second it can take other environments upwards of around a minute if not more.
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="intro.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="sym_crypt.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 1. Introduction </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 3. Symmetric-key Cryptography</td></tr></table></div></body></html>

View file

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 4. Miscellaneous Cryptography</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="sym_crypt.html" title="Chapter 3. Symmetric-key Cryptography" /><link rel="next" href="net.html" title="Chapter 5. Networking" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 4. Miscellaneous Cryptography</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="sym_crypt.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="net.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="misc_crypt"></a>Chapter 4. Miscellaneous Cryptography</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="misc_crypt.html#misc_crypt_hash">4.1. Crypt_Hash</a></span></dt><dd><dl><dt><span class="section"><a href="misc_crypt.html#misc_crypt_hash_supported">4.1.1. Supported Algorithms and Dependencies</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_hash_example">4.1.2. Example</a></span></dt></dl></dd><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa">4.2. Crypt_RSA</a></span></dt><dd><dl><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_dependencies">4.2.1. Dependencies</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_examples">4.2.2. Examples</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_createkey">4.2.3. createKey()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_format">4.2.4. setPrivateKeyFormat(), setPublicKeyFormat(), loadKey() and setPassword()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_getpublickey">4.2.5. setPublicKey() and getPublicKey()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_encrypt">4.2.6. encrypt(), decrypt() and setEncryptionMode()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_sign">4.2.7. sign(), verify(), and setSignatureMode()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_params">4.2.8. setHash(), setMGFHash() and setSaltLength()</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="misc_crypt_hash"></a>4.1. Crypt_Hash</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_hash_supported"></a>4.1.1. Supported Algorithms and Dependencies</h3></div></div></div><p>The following algorithms are supported:</p><p>md2, md5, md5-96, sha1, sha1-96, sha256, sha384, and sha512</p><p>
Crypt_Hash requires, minimally, PHP 4.3.0 (due to its use of
<a class="ulink" href="http://php.net/function.sha1" target="_top">sha1()</a>). If sha384 or sha512 are being used and
you're not running PHP 5.1.2 or greater then Math/BigInteger.php is also required.
</p><p>
Crypt_Hash uses the hash extension if it's available (&gt; 5.1.2), mhash if it's not, and it's own
internal implementation if not even mhash is available.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_hash_example"></a>4.1.2. Example</h3></div></div></div><pre class="programlisting">&lt;?php
include('Crypt/Hash.php');
$hash = new Crypt_Hash('sha1');
//$hash-&gt;setKey('abcdefg');
echo bin2hex($hash-&gt;hash('abcdefg'));
?&gt;</pre><p>If <code class="code">$hash-&gt;setKey()</code> had been called <code class="code">$hash-&gt;hash()</code> would have returned an HMAC.</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="misc_crypt_rsa"></a>4.2. Crypt_RSA</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_dependencies"></a>4.2.1. Dependencies</h3></div></div></div>
If you're running PHP 5, Crypt_RSA requires Math/BigInteger.php and Crypt/Hash.php. If you're running
PHP 4, Crypt_RSA also requires PHP/Compat/Function/array_fill.php, PHP/Compat/Function/bcpowmod.php, and
PHP/Compat/Function/str_split.php
</div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_examples"></a>4.2.2. Examples</h3></div></div></div><p>Here's an example of how to encrypt / decrypt with Crypt_RSA:</p><pre class="programlisting">&lt;?php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
extract($rsa-&gt;createKey());
$plaintext = 'terrafrost';
$rsa-&gt;loadKey($privatekey);
$ciphertext = $rsa-&gt;encrypt($plaintext);
$rsa-&gt;loadKey($publickey);
echo $rsa-&gt;decrypt($ciphertext);
?&gt;</pre><p>Here's an example of how to create / verify a signature with Crypt_RSA:</p><pre class="programlisting">&lt;?php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
extract($rsa-&gt;createKey());
$plaintext = 'terrafrost';
$rsa-&gt;loadKey($privatekey);
$signature = $rsa-&gt;sign($plaintext);
$rsa-&gt;loadKey($publickey);
echo $rsa-&gt;verify($plaintext, $signature) ? 'verified' : 'unverified';
&gt;</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_createkey"></a>4.2.3. createKey()</h3></div></div></div><p>
<code class="code">createKey()</code> takes three parameters - <code class="code">$bits</code>, <code class="code">$timeout</code>,
and <code class="code">$primes</code>. <code class="code">$timeout</code> is present since creating a key has the potential to be
fairly time consuming and will guarantee that <code class="code">createKey()</code> does not run for more than
<code class="code">$timeout</code> seconds. <code class="code">$primes</code> lets provide pre-computed prime numbers to speed
things up.
</p><p>
<code class="code">extract($rsa-&gt;createKey())</code> creates three variables - <code class="code">$publickey</code>,
<code class="code">$privatekey</code>, and <code class="code">$partialkey</code>. If <code class="code">createKey</code> hit the timeout then
it'll return all the primes that it had managed to compute so that you might pass them back to
<code class="code">createKey()</code> on a subsequent call.
</p><p>
The exponent can be set by defining <code class="code">CRYPT_RSA_EXPONENT</code> and multi-prime RSA can be utilized
by adjusting <code class="code">CRYPT_RSA_SMALLEST_PRIME</code>. Note that these must be done before a Crypt_RSA()
object is initialized.
</p><p>
Smaller values for <code class="code">CRYPT_RSA_SMALLEST_PRIME</code> result in increased speed at the cost of security.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_format"></a>4.2.4. setPrivateKeyFormat(), setPublicKeyFormat(), loadKey() and setPassword()</h3></div></div></div><p>Crypt_RSA supports the following formats:</p><p>CRYPT_RSA_PRIVATE_FORMAT_PKCS1:</p><pre class="programlisting">-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgHx5XHa3LjiugtNq2xkd0oFf2SdsJ04hQYLoeRR3bqAei3Gc+PSy
AvynCIh/03JCvBsUHaCe8BwjwaTYrpq5QunGo/wvIzvx2d3G9dlrpOIFLiatZYOf
h07+CkSfaRXhBUKkul/gU87WPhKEcbnPDJS10uD1HqLsHfSKLNitGOf7AgElAoGA
ENIhQHmedlzFkjEI2eFveURNxw6dhxlANEjtxH7XmRjiaUyQWGsVKQ+nNQpa2Bbb
JkD9FbSc/OI8wz/gPmwP9eJN29CriebhaV3ebM1L1gbb5r7Vf/D/6rxB0BG/h2lA
jyZWEZrV/Gi9ZCaw/J+IUu1pAskKid84yHphvszywCUCQQDigrtr+cVkwkUsxOGd
B378yQCroXmybAD7FQHwVslafuFfTHkaMQSU/ZZLVY1ioMs1VVzzq/vOu0RstZOY
AfHFAkEAjK3mIWdG4JOM44/SrDkACNatsMtXKOi4K3SlXu9ie6ikXPD+GSZ+bWCX
GstFaXr9cHRvZPF3qYtK+j2N9UXOvwJBALeoRO/DmSFDkgifoixLRF5CHDgiD6Vs
U9J/vGIBLaNSHoSe3rtKVr3+CyhTNF3Oe0AABi1bA4UGioGn+yFNr0UCQBbQF3sJ
1CRq9ECT3PlVWfOYbzFtFQ2NhaYul1uAw9yzkEZsROF73SZ+XbFRZTOzFFds08su
E2eaDCiUXDWcnhECQQCRUQn2huHlssj8kt35NAVwiHCNfaeSQ5tiDcwfOywA4YXl
Q+kpuWq5U3V8j/9/n7pE/DL0nXEG/3QpKHJEYV5T
-----END RSA PRIVATE KEY-----</pre><p>CRYPT_RSA_PRIVATE_FORMAT_PKCS1 (with password):</p><pre class="programlisting">-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,0AE1DB47E71463BE
pI2Kk5ceURbMYNo1xQqqA5rm2/QP4hgj/HuvrACtPSz/aesbG+h4lYXGpQ9os6Ha
AyFW+iX2UWS6BRwJj1ztO20sKT6ckg7eINSfiSSAeOOiG5aHLxOYayO9aQ5UrrJX
r0QmwRJRiHTW/82PLBNzfFHYskslNI9EWA5L/Gg4NAXDWwDooGvGkDq3ex7WkWLr
k7DN2JoZuWsUZxwpgTDouRQMsygrsdSjwRDSgbnTn6luEBrL9fc5/oAWf0xoTk5h
XMiOOHPBNPiZ1883ayq91HL/6895g8U9oIR1wQmdl0USViYYp5jI19ueowCyblzP
xD3Bfpb6RPaZ/yqECOysPk6PDz257SGDMNk/QrQJ/eZkeniNXHJ8d+nJGuajZeBu
6A/bglvKGNNNWe8UJMb5P2OAliD7y7F9wXrkV5FnQ/Q49tGxdBl7WXNuGp4x2d9s
ZEnv3mOtrr1lM+2QE0Zg8mjqSem5b6Dp0LxOj5j45j5IbBrrd3MKu87jJVzp8yHy
sBC6NMYYtO03qxV/j1kJR+MmAcCF1+4GGRWdFcoc0sXGVqmEOmK4QfYx3T0Vb6Hk
oLdlh6ofZogezzJ8A1BvV382sTsJ90eqbgz3E+fDl8iR86+EV9bUujFE4IaBgZJP
gxikVItdTcq1frNKTCSH/RPeRwk+oKWTpCYGgNA+bl641onW1DCLYcd14N6TDKmY
77cOTf2ZDGOYNPycAF/FnNJJyLO3IYpU63aKBshB4dYeVrfH0FvG6g5Xt0geIkiD
5W9El4ks7/3r97x443SagDRt6Mceo5TtzzFfAo7cZeA=
-----END RSA PRIVATE KEY-----</pre><p>CRYPT_RSA_PUBLIC_FORMAT_PKCS1:</p><pre class="programlisting">-----BEGIN PUBLIC KEY-----
MIGGAoGAfHlcdrcuOK6C02rbGR3SgV/ZJ2wnTiFBguh5FHduoB6LcZz49LIC/KcIiH/TckK8GxQd
oJ7wHCPBpNiumrlC6caj/C8jO/HZ3cb12Wuk4gUuJq1lg5+HTv4KRJ9pFeEFQqS6X+BTztY+EoRx
uc8MlLXS4PUeouwd9Ios2K0Y5/sCASU=
-----END PUBLIC KEY-----</pre><p>CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:</p><pre class="programlisting">ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIB8eVx2ty44roLTatsZHdKBX9knbCdOIUGC6HkUd26gHotx
nPj0sgL8pwiIf9NyQrwbFB2gnvAcI8Gk2K6auULpxqP8LyM78dndxvXZa6TiBS4mrWWDn4dO/gpEn2kV
4QVCpLpf4FPO1j4ShHG5zwyUtdLg9R6i7B30iizYrRjn+w== phpseclib-generated-key</pre><p>
Passwords can be set via <code class="code">setPassword()</code> and are only supported on private keys.
CRYPT_RSA_PUBLIC_FORMAT_OPENSSH generates keys that are intended to go in $HOME/.ssh/authorized_keys
for use with OpenSSH. Another format - CRYPT_RSA_PUBLIC_FORMAT_RAW - is stored as an array with two
indexes - one for the modulus and one for the exponent. Indexes accepted by <code class="code">loadkey()</code>
are as follows:
</p><p>
e, exponent, publicExponent, modulus, modulo, n
</p><p>
<code class="code">loadKey()</code> has two parameters - <code class="code">$key</code> and the optional <code class="code">$type</code>.
The default type, if <code class="code">$type</code> is not explicitely set, is CRYPT_RSA_PRIVATE_FORMAT_PKCS1.
It should, at this point, be noted that Crypt_RSA treats public and private keys largelly identically.
A key can be formatted as a CRYPT_RSA_PUBLIC_FORMAT_PKCS1 and still conform to the
CRYPT_RSA_PRIVATE_FORMAT_PKCS1 format and vice versa. The only real difference between private keys and
public keys is that private keys *can* contain their public key counterparts whereas public keys cannot.
That said, this distinction is, for the most part, irrelevant and academic. For a more thorough
discussion of this see <a class="link" href="misc_crypt.html#misc_crypt_rsa_getpublickey" title="4.2.5. setPublicKey() and getPublicKey()">setPublicKey() and getPublicKey()</a>.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_getpublickey"></a>4.2.5. setPublicKey() and getPublicKey()</h3></div></div></div><p>
As noted in <a class="link" href="misc_crypt.html#misc_crypt_rsa_format" title="4.2.4. setPrivateKeyFormat(), setPublicKeyFormat(), loadKey() and setPassword()">setPrivateKeyFormat(), setPublicKeyFormat(), loadKey() and setPassword()</a>,
Crypt_RSA treats public and private keys largely identically. The only real difference is that some
private key formats contain the public key within them whereas no public key format does. The reason
you'd want to do this is for indexing purposes. For example, in SSH-2, RSA authentication works by
sending your public key along with a signature created by your private key. The SSH-2 server then looks
the public key up in an index of public keys to see if it's an allowed key and then verifies the signature.
To that end, <code class="code">setPublicKey()</code> defines the public key if it hasn't already been defined and
<code class="code">getPublicKey()</code> returns it. <code class="code">getPublicKey()</code> has an optional parameter - $type -
that sets the format.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_encrypt"></a>4.2.6. encrypt(), decrypt() and setEncryptionMode()</h3></div></div></div><p>
Crypt_RSA supports two encryption modes - <code class="code">CRYPT_RSA_ENCRYPTION_OAEP</code> and
<code class="code">CRYPT_RSA_ENCRYPTION_PKCS1</code>. <code class="code">CRYPT_RSA_ENCRYPTION_OAEP</code> uses
<a class="ulink" href="http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding" target="_top">Optimal Asymmetric Encryption Padding</a>
and provides more security than <code class="code">CRYPT_RSA_ENCRYPTION_PKCS1</code>.
</p><p>
Both <code class="code">CRYPT_RSA_ENCRYPTION_OAEP</code> and <code class="code">CRYPT_RSA_ENCRYPTION_PKCS1</code> impose limits
on how large the plaintext can be. If the plaintext exceeds these limits the plaintext will be split
up such that each block falls within those limits.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_sign"></a>4.2.7. sign(), verify(), and setSignatureMode()</h3></div></div></div><p>
Crypt_RSA supports two signature modes - <code class="code">CRYPT_RSA_SIGNATURE_PSS</code> and
<code class="code">CRYPT_RSA_SIGNATURE_PKCS1</code>. The former is assumed to provide more security than the latter.
See <a class="link" href="misc_crypt.html#misc_crypt_rsa_examples" title="4.2.2. Examples">Examples</a> for examples.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_params"></a>4.2.8. setHash(), setMGFHash() and setSaltLength()</h3></div></div></div><p>
In all likelihood, calling these functions will be unnecessary as the default values should be sufficient.
None-the-less a discussion of them follows.
</p><p>
<code class="code">setHash()</code> is used with signature production / verification and (if the encryption mode is
CRYPT_RSA_ENCRYPTION_OAEP) encryption and decryption. If the specified hash isn't supported sha1 will
be used.
</p><p>
<code class="code">setMGFHash()</code> determines which hashing function should be used for the mask generation
function as utilized in CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS. PKCS#1 recommends
but does not require that the MGFHash and the Hash be set to the same thing.
</p><p>
<code class="code">setSaltLength()</code> is only utilized with CRYPT_RSA_SIGNATURE_PSS. PKCS#1 recommends this
value either be 0 (which is what it is by default) or the length of the output of the hash function as
set via <code class="code">setHash()</code>
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="sym_crypt.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="net.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 3. Symmetric-key Cryptography </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 5. Networking</td></tr></table></div></body></html>

153
library/phpsec/net.html Normal file
View file

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 5. Networking</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="misc_crypt.html" title="Chapter 4. Miscellaneous Cryptography" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 5. Networking</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="misc_crypt.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> </td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="net"></a>Chapter 5. Networking</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="net.html#net_ssh">5.1. Net_SSH</a></span></dt><dd><dl><dt><span class="section"><a href="net.html#net_ssh_dependencies">5.1.1. Dependencies</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_ssh1">5.1.2. Net_SSH1 Examples</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_ssh2">5.1.3. Net_SSH2 Examples</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_host_key_verify">5.1.4. Host Key Verification</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_interactive">5.1.5. interactiveRead() / interactiveWrite() vs. exec()</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_exec">5.1.6. SSH-1's exec() vs. SSH-2's exec()</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_successive">5.1.7. Successive calls to SSH-2's exec()</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_debug">5.1.8. Debugging SSH-2</a></span></dt></dl></dd><dt><span class="section"><a href="net.html#net_sftp">5.2. Net_SFTP</a></span></dt><dd><dl><dt><span class="section"><a href="net.html#net_sftp_intro">5.2.1. Introduction</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_dependencies">5.2.2. Dependencies</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_example">5.2.3. Net_SFTP Example</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_put">5.2.4. put($remote_file, $data [, $mode])</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_get">5.2.5. get($remote_file [, $local_file])</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_pwd">5.2.6. pwd(), chdir(), mkdir() and rmdir()</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_chmod">5.2.7. chmod() and size()</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_nlist">5.2.8. nlist() and rawlist()</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_delete">5.2.9. delete() and rename()</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_debug">5.2.10. Debugging SFTP</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="net_ssh"></a>5.1. Net_SSH</h2></div></div></div><p>
The Net_SSH1 and Net_SSH2 libraries have, for the most part, an identical API. Some functions, however, do behave differently.
</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_dependencies"></a>5.1.1. Dependencies</h3></div></div></div><p>
Net_SSH1/2 require, minimally, Math/BigInteger.php, Crypt/*.php, and PHP/Compat/Function/*.php. Net_SSH1 requires PHP 4.0.0 unless you're using the interactive functions, which require PHP 4.3.0. Net_SSH2 requires PHP 4.3.0 due to it's use of <a class="ulink" href="http://php.net/function.sha1" target="_top">sha1()</a>.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_ssh1"></a>5.1.2. Net_SSH1 Examples</h3></div></div></div><pre class="programlisting">&lt;?php
include('Net/SSH1.php');
$ssh = new Net_SSH1('www.domain.tld');
if (!$ssh-&gt;login('username', 'password')) {
exit('Login Failed');
}
while (true) {
echo $ssh-&gt;interactiveRead();
$read = array(STDIN);
$write = $except = NULL;
if (stream_select($read, $write, $except, 0)) {
$ssh-&gt;interactiveWrite(fread(STDIN, 1));
}
}
?&gt;</pre><pre class="programlisting">&lt;?php
include('Net/SSH1.php');
$ssh = new Net_SSH1('www.domain.tld');
if (!$ssh-&gt;login('username', 'password')) {
exit('Login Failed');
}
echo $ssh-&gt;exec('ls -la');
?&gt;</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_ssh2"></a>5.1.3. Net_SSH2 Examples</h3></div></div></div><pre class="programlisting">&lt;?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh-&gt;login('username', 'password')) {
exit('Login Failed');
}
echo $ssh-&gt;exec('pwd');
echo $ssh-&gt;exec('ls -la');
?&gt;</pre><pre class="programlisting">&lt;?php
include('Crypt/RSA.php');
include('Net/SSH2.php');
$key = new Crypt_RSA();
//$key-&gt;setPassword('whatever');
$key-&gt;loadKey(file_get_contents('privatekey'));
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh-&gt;login('username', $key)) {
exit('Login Failed');
}
echo $ssh-&gt;exec('pwd');
echo $ssh-&gt;exec('ls -la');
?&lt;</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_host_key_verify"></a>5.1.4. Host Key Verification</h3></div></div></div><p>
SSH protects itself against active eavesdroppers by providing a host key. The first time you connect the host key is supposed to be cached in some manner. On subsequent connections, the host key being used for this connection should be checked against the cached host key. If they match, it's the same server. If not, it's a different one.
</p><p>
In SSH-1, <code class="code">getHostKeyPublicModulus()</code> and <code class="code">getHostKeyPublicExponent()</code> will provide you with the host key. In SSH-2, <code class="code">getServerPublicHostKey()</code> gets you the key.
</p><p>
The Net_SSH1 and Net_SSH2 examples omit the key verification stage for brevity. Also, depending on the context in which this library is used, it may even be unnecessary. For example, if you're connecting to www.example.com:22 from www.example.com:80, eavesdroppers are not something you need to worry about.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_interactive"></a>5.1.5. interactiveRead() / interactiveWrite() vs. exec()</h3></div></div></div><p>
Say you wanted to use SSH to get the contents of a directory. If you used <code class="code">interactiveWrite('ls')</code> to do this, and then <code class="code">interactiveRead()</code> to get the output, you, in all likelihood, wouldn't get the whole output. You'd have to call <code class="code">interactiveRead()</code> multiple times - you'd have to call it as many times as it took for you to get the complete output, which, in turn, begs the question... how do you know when you have the complete output?
You could assume that whenever the prompt (eg. <code class="code">root@desktop:/root#</code>) showed up, that that'd mean you had the complete output, but that's not fool proof. And what about messages of the day? The first <code class="code">interactiveRead()</code> you do is liable to include a part of that rather than the directory listing. So, not only do you have to make some sort of guestimate as to when the output ends - you often may have to guestimate as to when it begins.
</p><p>
To top it all off, you may also get <a class="ulink" href="http://en.wikipedia.org/wiki/ANSI_escape_code" target="_top">ANSI escape codes</a> interspersed amongst the output, which would need to be removed.
</p><p>
Using <code class="code">exec('ls')</code> resolves all of these issues. If you're implementing an interactive client, the interactive functions are the ones you'll want to use. Otherwise, <code class="code">exec()</code> is likely what you'll want to use.
</p><p>
interactiveRead() / interactiveWrite() are not implemented in Net_SSH2. The SSH-2 protocol supports them, however, phpseclib does not. The reasons are discussed in the <a class="link" href="net.html#net_ssh_exec" title="5.1.6. SSH-1's exec() vs. SSH-2's exec()">SSH-1's exec() vs. SSH-2's exec()</a> section.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_exec"></a>5.1.6. SSH-1's exec() vs. SSH-2's exec()</h3></div></div></div><p>
<code class="code">exec()</code> works by creating a channel, issuing a command, and then subsequently destroying that channel. Since SSH-1 <a class="ulink" href="http://www.snailbook.com/faq/ssh-1-vs-2.auto.html" target="_top">only allows one channel</a>, exec() can only be called once. SSH-2, in contrast, allows an unlimited number of channels, and as such, you can perform as many <code class="code">exec()</code>'s as you see fit.
</p><p>
As a consequence of this difference, Net_SSH2 does not implement <a class="link" href="net.html#net_ssh_interactive" title="5.1.5. interactiveRead() / interactiveWrite() vs. exec()">interactiveRead() / interactiveWrite()</a>, even though the SSH-2 specifications provide for those functions. Simply put, in SSH-1, those functions are necessary to do multiple commands. In SSH-2, they're not, and so they're not implemented.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_successive"></a>5.1.7. Successive calls to SSH-2's exec()</h3></div></div></div><p>
Successive calls to SSH-2's exec() may not work as expected. Consider the following:
</p><pre class="programlisting">&lt;?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh-&gt;login('username', 'password')) {
exit('Login Failed');
}
echo $ssh-&gt;exec('pwd');
echo $ssh-&gt;exec('cd /');
echo $ssh-&gt;exec('pwd');
?&gt;</pre><p>
If done on an interactive shell, the output you'd receive for the first <code class="code">pwd</code> would (depending on how your system is setup) be different than the output of the second <code class="code">pwd</code>. The above code snippet, however, will yield two identical lines. The reason for this is that any "state changes" you make to the one-time shell are gone once the <code class="code">exec()</code> has been ran and the channel has been deleted.
As such, if you want to support <code class="code">cd</code> in your program, it'd be best to just handle that internally and rewrite all commands, before they're passed to <code class="code">exec()</code> such that the relative paths are expanded to the absolute paths.
Alternatively, one could always run a shell script, however, that may not always be an option.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_debug"></a>5.1.8. Debugging SSH-2</h3></div></div></div><p>
To log output, the NET_SSH2_LOGGING constant will need to be defined. If you want full logs, you'll need to do <code class="code">define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX)</code>. <code class="code">$ssh-&gt;getLog()</code> will then return a string containing the unencrypted packets in hex and ASCII. If you want to just record the packet types that are being sent to and fro, you'll need to do <code class="code">define('NET_SSH2_LOGGING', NET_SSH2_LOG_SIMPLE)</code>. <code class="code">$ssh-&gt;getLog()</code> will then return an array. Both log types include the amount of time it took to send the packet in question. The former is useful for general diagnostics and the latter is more useful for profiling. An example follows:
</p><pre class="programlisting">&lt;?php
include('Net/SSH2.php');
define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX);
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh-&gt;login('username', 'password')) {
exit('Login Failed');
}
echo $ssh-&gt;exec('pwd');
echo $ssh-&gt;getLog();
?&gt;</pre><p>
Depending on the problem, it may be more effective to just look at the output of <code class="code">$ssh-&gt;getLastError()</code> (which returns a string) and <code class="code">$ssh-&gt;getErrors()</code> (which returns an array) than to sift through the logs.
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="net_sftp"></a>5.2. Net_SFTP</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_intro"></a>5.2.1. Introduction</h3></div></div></div><p>
Net_SFTP currently only supports SFTPv3, which, according to wikipedia.org, "is the most widely used
version, implemented by the popular OpenSSH SFTP server".
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_dependencies"></a>5.2.2. Dependencies</h3></div></div></div><p>
Net_SFTP requires, minimally, PHP 4.3.0 and Net/SSH2.php, Math/BigInteger.php, Crypt/*.php, and PHP/Compat/Function/*.php.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_example"></a>5.2.3. Net_SFTP Example</h3></div></div></div><pre class="programlisting">&lt;?php
include('Net/SFTP.php');
$sftp = new Net_SFTP('www.domain.tld');
if (!$sftp-&gt;login('username', 'password')) {
exit('Login Failed');
}
echo $sftp-&gt;pwd() . "\r\n";
$sftp-&gt;put('filename.ext', 'hello, world!');
print_r($sftp-&gt;nlist());
?&gt;</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_put"></a>5.2.4. put($remote_file, $data [, $mode])</h3></div></div></div><p>
By default, put() does not read from the local filesystem. $data is dumped directly into $remote_file.
So, for example, if you set $data to 'filename.ext' and then do get(), you will get a file, twelve bytes
long, containing 'filename.ext' as its contents.
</p><p>
Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior. With NET_SFTP_LOCAL_FILE, $remote_file will
contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
large $remote_file will be, as well.
</p><p>
Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take
care of that, yourself.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_get"></a>5.2.5. get($remote_file [, $local_file])</h3></div></div></div><p>
Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the
operation
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_pwd"></a>5.2.6. pwd(), chdir(), mkdir() and rmdir()</h3></div></div></div><p>
pwd() returns the current directory, chdir() changes directories, mkdir() creates direcotires, and rmdir() removes directories.
In the event of failure, they all return false. chdir(), mkdir(), and rmdir() return true on successful completion of the operation.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_chmod"></a>5.2.7. chmod() and size()</h3></div></div></div><p>
chmod() sets the permissions on a file and returns the new file permissions on success or false on error. Permissions are expected to be in octal so to set a file to 777 do <code class="code">$sftp-&gt;chmod(0777, $filename)</code>
</p><p>
size() returns the size, in bytes, of an arbitrary file.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_nlist"></a>5.2.8. nlist() and rawlist()</h3></div></div></div><p>
nlist($dir = '.') returns the contents of the current directory as a numerically indexed array and rawlist() returns an associate array where the filenames are the array keys and the array values are, themselves, arrays containing the file attributes. The directory can be changed with the first parameter.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_delete"></a>5.2.9. delete() and rename()</h3></div></div></div><p>
The purpose of both functions should be easy enough to glean - delete() deletes files or directories and rename() renames them. Both return true on success and false on failure.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_debug"></a>5.2.10. Debugging SFTP</h3></div></div></div><p>
Debbuging SFTP connections in phpseclib works in a manner similar to <a class="link" href="net.html#net_ssh_debug" title="5.1.8. Debugging SSH-2">debugging SSH-2</a> Instead of the constant being NET_SSH2_LOGGING, however, it's <code class="code">NET_SFTP_LOGGING</code>. And instead of NET_SSH2_LOG_COMPLEX or NET_SSH2_LOG_SIMPLE it's NET_SFTP_LOG_COMPLEX or NET_SFTP_LOG_SIMPLE respectively. And instead of calling $sftp-&gt;getLog() you call <code class="code">$sftp-&gt;getSFTPLog()</code> or <code class="code">$sftp-&gt;getLastSFTPError()</code> or whatever.
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="misc_crypt.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> </td></tr><tr><td width="40%" align="left" valign="top">Chapter 4. Miscellaneous Cryptography </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> </td></tr></table></div></body></html>

View file

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 3. Symmetric-key Cryptography</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="math.html" title="Chapter 2. Math" /><link rel="next" href="misc_crypt.html" title="Chapter 4. Miscellaneous Cryptography" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 3. Symmetric-key Cryptography</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="math.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="misc_crypt.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="sym_crypt"></a>Chapter 3. Symmetric-key Cryptography</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_intro">3.1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_dependencies">3.1.1. Dependencies</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_set">3.1.2. setKey() and setIV()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_encrypt">3.1.3. encrypt() and decrypt()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_continuousbuffer">3.1.4. enableContinuousBuffer() and disableContinuousBuffer()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_padding">3.1.5. enablePadding() and disablePadding()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_caution">3.1.6. A word of caution about stream ciphers and CTR mode</a></span></dt></dl></dd><dt><span class="section"><a href="sym_crypt.html#sym_crypt_des">3.2. Crypt_DES</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_des_constructor">3.2.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="sym_crypt.html#sym_crypt_tripledes">3.3. Crypt_TripleDES</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_tripledes_constructor">3.3.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="sym_crypt.html#sym_crypt_rc4">3.4. Crypt_RC4</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_rc4_constructor">3.4.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes">3.5. Crypt_Rijndael &amp; Crypt_AES</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_constructor">3.5.1. The constructor</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_vs_rijndael">3.5.2. AES vs. Rijndael</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_setkeylength">3.5.3. setKeyLength()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_setblocklength">3.5.4. setBlockLength()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_benchmarks">3.5.5. Speed Comparisons</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_intro"></a>3.1. Introduction</h2></div></div></div><p>
All of the cryptographic libraries included in phpseclib use mcrypt, if available, and an internal implementation
if it's not. The libraries all use a common interface although some functions, for some algorithms, carry with
with them certain caveats. Those that do not have caveats attached (or have relatively few attached) are
described below. If you don't know which one to use, try <code class="code">Crypt_TripleDES</code>.
</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_dependencies"></a>3.1.1. Dependencies</h3></div></div></div><p>
The Crypt_* functions require, minimally, PHP 4.0.0. Crypt_TripleDES additionally requires Crypt/DES.php.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_set"></a>3.1.2. setKey() and setIV()</h3></div></div></div><p>
Sets the key and the initialization vector, respectively. If neither are set, each assumed to be equal to
some amount of null bytes. The initialization vector is only used in block ciphers and even then only
in CBC mode. If the key or the initialization vector are larger then the block size, they're truncated.
If they're smaller, they're padded with null bytes.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_encrypt"></a>3.1.3. encrypt() and decrypt()</h3></div></div></div><p>
Self-explanatory. Encrypts or decrypts messages. See the examples in the subsequent sections.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_continuousbuffer"></a>3.1.4. enableContinuousBuffer() and disableContinuousBuffer()</h3></div></div></div><p>
If the continuous buffer is enabled and you're using a stream cipher or a block cipher mode other than ECB then encrypting the same string twice will yield different ciphertexts.
The reason being that the IV doesn't reset after each encryption / decryption round when the continuous buffer is used.
This provides better security but it may also make for less intuitive behavior.
For this reason, the continuous buffer is disabled by default.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_padding"></a>3.1.5. enablePadding() and disablePadding()</h3></div></div></div><p>
Enables / disables PKCS padding on block ciphers. Stream ciphers (<code class="code">Crypt_RC4</code> is the only stream
cipher currently included) ignore this.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_caution"></a>3.1.6. A word of caution about stream ciphers and CTR mode</h3></div></div></div><p>
Most stream ciphers (and block ciphers operating in a mode - like CTR - that turns them into stream ciphers) work by generating a stream of pseudorandom characters called a <a class="ulink" href="http://en.wikipedia.org/wiki/Keystream" target="_top">keystream</a> and then XOR'ing that with the plaintext.
This *effectively* makes them <a class="ulink" href="http://en.wikipedia.org/wiki/One-time_pad" target="_top">one-time pads</a> which, in theory, can provide perfect secrecy. The problem with one-time pads is that they're not as versatile as one might desire.
Among other things, a keystream must never be reset, lest it be possible for an attacker to recover the keystream via a <a class="ulink" href="http://en.wikipedia.org/wiki/Known-plaintext_attack" target="_top">known-plaintext attack</a>. ie. <code class="code">$ciphertext ^ $plaintext = $key</code>. If <code class="code">$key</code> is constant (because the keystream's being reset or something) than an attacker can recover any <code class="code">$plaintext</code>, but if not - if it's dynamic - then the only key that an attacker could recover is their own.
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_des"></a>3.2. Crypt_DES</h2></div></div></div><p>
Implements DES (a block cipher). Here's an example of how to use it:
</p><pre class="programlisting">&lt;?php
include('Crypt/DES.php');
$des = new Crypt_DES();
$des-&gt;setKey('abcdefgh');
$size = 10 * 1024;
$plaintext = '';
for ($i = 0; $i &lt; $size; $i++) {
$plaintext.= 'a';
}
echo $des-&gt;decrypt($des-&gt;encrypt($plaintext));
?&gt;</pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_des_constructor"></a>3.2.1. The constructor</h3></div></div></div><p>
The constructor takes one optional parameter - $mode. Valid values for $mode are as follows:
</p><div class="itemizedlist"><ul type="disc"><li><code class="code">CRYPT_DES_MODE_ECB</code></li><li><code class="code">CRYPT_DES_MODE_CBC</code>: The default value.</li><li><code class="code">CRYPT_DES_MODE_CTR</code></li></ul></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_tripledes"></a>3.3. Crypt_TripleDES</h2></div></div></div><p>
Implements TripleDES (a block cipher). Here's an example of how to use it:
</p><pre class="programlisting">&lt;?php
include('Crypt/TripleDES.php');
$des = new Crypt_TripleDES();
$des-&gt;setKey('abcdefghijklmnopqrstuvwx');
$size = 10 * 1024;
$plaintext = '';
for ($i = 0; $i &lt; $size; $i++) {
$plaintext.= 'a';
}
echo $des-&gt;decrypt($des-&gt;encrypt($plaintext));
?&gt;</pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_tripledes_constructor"></a>3.3.1. The constructor</h3></div></div></div><p>
The constructor takes one optional parameter - $mode. Valid values for $mode are as follows:
</p><div class="itemizedlist"><ul type="disc"><li><code class="code">CRYPT_DES_MODE_ECB</code></li><li><code class="code">CRYPT_DES_MODE_CBC3</code>: Employs outer chaining to propogate the initialization vector. Used by SSH-2 and generally considered more secure than inner chaining.</li><li><code class="code">CRYPT_DES_MODE_3CBC</code>: Employs inner chaining to propogate the initialization vector. Used by SSH-1.</li><li><code class="code">CRYPT_DES_MODE_CBC</code>: The default value. An alias for <code class="code">CRYPT_DES_MODE_CBC3</code>.</li><li><code class="code">CRYPT_DES_MODE_CTR</code></li></ul></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_rc4"></a>3.4. Crypt_RC4</h2></div></div></div><p>
Implements RC4 (a stream cipher). Here's an example of how to use it:
</p><pre class="programlisting">&lt;?php
include('Crypt/RC4.php');
$rc4 = new Crypt_RC4();
$rc4-&gt;setKey('abcdefghijklmnopqrstuvwx');
$size = 10 * 1024;
$plaintext = '';
for ($i = 0; $i &lt; $size; $i++) {
$plaintext.= 'a';
}
echo $rc4-&gt;decrypt($rc4-&gt;encrypt($plaintext));
?&gt;</pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_rc4_constructor"></a>3.4.1. The constructor</h3></div></div></div><p>
Not much to say about this constructor. Since it's a stream cipher, you don't need to worry about which
mode of operation to use.
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_aes"></a>3.5. Crypt_Rijndael &amp; Crypt_AES</h2></div></div></div><p>
Implements Rijndael / AES. Here's an example of how to use Crypt_AES:
</p><pre class="programlisting">&lt;?php
include('Crypt/AES.php');
$aes = new Crypt_AES();
$aes-&gt;setKey('abcdefghijklmnop');
$size = 10 * 1024;
$plaintext = '';
for ($i = 0; $i &lt; $size; $i++) {
$plaintext.= 'a';
}
echo $aes-&gt;decrypt($aes-&gt;encrypt($plaintext));
?&gt;</pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_constructor"></a>3.5.1. The constructor</h3></div></div></div><p>
<code class="code">Crypt_AES</code>'s constructor's optional parameter can take the following values:
</p><div class="itemizedlist"><ul type="disc"><li><code class="code">CRYPT_AES_MODE_ECB</code></li><li><code class="code">CRYPT_AES_MODE_CBC</code>: The default value.</li><li><code class="code">CRYPT_AES_MODE_CTR</code></li></ul></div><p>
<code class="code">Crypt_Rijndael</code> takes the following:
</p><div class="itemizedlist"><ul type="disc"><li><code class="code">CRYPT_RIJNDAEL_MODE_ECB</code></li><li><code class="code">CRYPT_RIJNDAEL_MODE_CBC</code>: The default value.</li><li><code class="code">CRYPT_RIJNDAEL_MODE_CTR</code></li></ul></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_vs_rijndael"></a>3.5.2. AES vs. Rijndael</h3></div></div></div><p>
AES is a subset of Rijndael. Both have variable key sizes, however, AES's block size is fixed at 128 bits, whereas Rijndael's is variable. Also, Rijndael supports, by means of an extension to the specification, two key sizes that AES does not - 160 bits and 224 bits.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_setkeylength"></a>3.5.3. setKeyLength()</h3></div></div></div><p>
Valid key lengths for AES are 128 bits, 192 bits, and 256 bits. If the key that is assigned is invalid and less than 256 bits, they key length is rounded up to the next closest valid size and the key will be null padded to that amount. If the key length is greater than 256 bits, it will be truncated to 256 bits.
</p><p>
As an example, if the key is 136 bits, it will be null padded to 192 bits (or 160 bits if Rijndael is being used).
</p><p>
If <code class="code">setKeyLength()</code> has been called, this behavior changes somewhat. Say you've set the key length, via this function, to 256 bits. Then, instead of an invalid key being null padded to 192 or 160 bits, it will be null padded to 256 bits.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_setblocklength"></a>3.5.4. setBlockLength()</h3></div></div></div><p>
<code class="code">setBlockLength()</code> operates in a manner similar to <code class="code">setKeyLength()</code>, with one exception. <code class="code">setBlockLength()</code> only works on Rijndael. Although <code class="code">Crypt_AES</code> inherits <code class="code">setBlockLength()</code> as a function, the function doesn't do anything in AES.
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_benchmarks"></a>3.5.5. Speed Comparisons</h3></div></div></div><p>
The following table compares the speed of five different pure-PHP implementations of AES (one of which is Crypt_Rijndael and one of which is Crypt_AES) when ran on 150KB of text on a 1.8GHz Pentium 4-M. The numbers listed are averaged from five different trials and are measured in seconds. phpseclib's two implementations are highlighted. All implementations can be viewed by clicking on their names.
</p><div class="table"><a id="sym_crypt_aes_benchmarks_table"></a><p class="title"><b>Table 3.1. AES Speed Comparisons</b></p><div class="table-contents"><table summary="AES Speed Comparisons" border="1"><colgroup><col /><col /><col /><col /><col /></colgroup><thead><tr><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/movable-type.phps" target="_top">movable-type.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpaes.phps" target="_top">phpaes.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpclasses1.phps" target="_top">phpclasses1.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpclasses2.phps" target="_top">phpclasses2.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpseclib-aes.phps" target="_top">phpseclib-aes.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpseclib-rijndael.phps" target="_top">phpseclib-rijndael.phps</a></th></tr></thead><tbody><tr><td align="right">15.6844158172</td><td align="right">39.9537248135</td><td align="right">15.0100150108</td><td align="right">62.591713190079</td><td class="highlight" align="right">2.03581542968752</td><td class="highlight" align="right">2.62501101493836</td></tr></tbody></table></div></div><br class="table-break" /><p>
As can be seen, phpseclib's implementations are the fastest. phpseclib-aes.phps is faster than phpseclib-rijndael.phps because phpseclib-rijndael.phps has to contend with multiple block sizes whereas phpseclib-aes.phps does not. Note that if mcrypt weren't explicitily disabled phpseclib would have been even faster.
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="math.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="misc_crypt.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 2. Math </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 4. Miscellaneous Cryptography</td></tr></table></div></body></html>

View file

@ -0,0 +1,26 @@
Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of the SimplePie Team nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,53 @@
# SimplePie
## Authors and contributors
* [Ryan Parman](http://ryanparman.com)
* [Geoffrey Sneddon](http://gsnedders.com)
* [Ryan McCue](http://ryanmccue.info)
* [Michael Shipley](http://michaelpshipley.com)
* [Steve Minutillo](http://minutillo.com/steve/)
## License
[New BSD license](http://www.opensource.org/licenses/bsd-license.php)
## Project status
SimplePie is currently maintained by Ryan McCue.
At the moment, there isn't a lot of active development happening. If the community decides that SimplePie is a valuable tool, then the community will come together to maintain it into the future.
If you're interested in getting involved with SimplePie, please get in touch with Ryan McCue.
## What comes in the package?
1. `simplepie.inc` - The SimplePie library. This is all that's required for your pages.
2. `README.markdown` - This document.
3. `LICENSE.txt` - A copy of the BSD license.
4. `compatibility_test/` - The SimplePie compatibility test that checks your server for required settings.
5. `demo/` - A basic feed reader demo that shows off some of SimplePie's more noticable features.
6. `idn/` - A third-party library that SimplePie can optionally use to understand Internationalized Domain Names (IDNs).
7. `test/` - SimplePie's unit test suite.
## To start the demo
1. Upload this package to your webserver.
2. Make sure that the cache folder inside of the demo folder is server-writable.
3. Navigate your browser to the demo folder.
## Need support?
For further setup and install documentation, function references, etc., visit:
[http://simplepie.org/wiki/](http://simplepie.org/wiki/)
For bug reports and feature requests, visit:
[http://github.com/rmccue/simplepie/issues](http://github.com/rmccue/simplepie/issues)
Support mailing list -- powered by users, for users.
[http://tech.groups.yahoo.com/group/simplepie-support/](http://tech.groups.yahoo.com/group/simplepie-support/)

View file

@ -0,0 +1,7 @@
SIMPLEPIE COMPATIBILITY TEST
1) Upload sp_compatibility_test.php to the web-accessible root of your website.
For example, if your website is www.example.com, upload it so that you can get
to it at www.example.com/sp_compatibility_test.php
2) Open your web browser and go to the page you just uploaded.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,178 @@
<?php
require_once 'simplepie.inc';
function normalize_character_set($charset)
{
return strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset));
}
function build_character_set_list()
{
$file = new SimplePie_File('http://www.iana.org/assignments/character-sets');
if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
{
return false;
}
else
{
$data = explode("\n", $file->body);
unset($file);
foreach ($data as $line)
{
// New character set
if (substr($line, 0, 5) === 'Name:')
{
// If we already have one, push it on to the array
if (isset($aliases))
{
for ($i = 0, $count = count($aliases); $i < $count; $i++)
{
$aliases[$i] = normalize_character_set($aliases[$i]);
}
$charsets[$preferred] = array_unique($aliases);
natsort($charsets[$preferred]);
}
$start = 5 + strspn($line, "\x09\x0A\x0B\xC\x0D\x20", 5);
$chars = strcspn($line, "\x09\x0A\x0B\xC\x0D\x20", $start);
$aliases = array(substr($line, $start, $chars));
$preferred = end($aliases);
}
// Another alias
elseif(substr($line, 0, 6) === 'Alias:')
{
$start = 7 + strspn($line, "\x09\x0A\x0B\xC\x0D\x20", 7);
$chars = strcspn($line, "\x09\x0A\x0B\xC\x0D\x20", $start);
$aliases[] = substr($line, $start, $chars);
if (end($aliases) === 'None')
{
array_pop($aliases);
}
elseif (substr($line, 7 + $chars + 1, 21) === '(preferred MIME name)')
{
$preferred = end($aliases);
}
}
}
// Compatibility replacements
$compat = array(
'EUC-KR' => 'windows-949',
'GB2312' => 'GBK',
'GB_2312-80' => 'GBK',
'ISO-8859-1' => 'windows-1252',
'ISO-8859-9' => 'windows-1254',
'ISO-8859-11' => 'windows-874',
'KS_C_5601-1987' => 'windows-949',
'TIS-620' => 'windows-874',
//'US-ASCII' => 'windows-1252',
'x-x-big5' => 'Big5',
);
foreach ($compat as $real => $replace)
{
if (isset($charsets[$real]) && isset($charsets[$replace]))
{
$charsets[$replace] = array_merge($charsets[$replace], $charsets[$real]);
unset($charsets[$real]);
}
elseif (isset($charsets[$real]))
{
$charsets[$replace] = $charsets[$real];
$charsets[$replace][] = normalize_character_set($replace);
unset($charsets[$real]);
}
else
{
$charsets[$replace][] = normalize_character_set($real);
}
$charsets[$replace] = array_unique($charsets[$replace]);
natsort($charsets[$replace]);
}
// Sort it
uksort($charsets, 'strnatcasecmp');
// Check that nothing matches more than one
$all = call_user_func_array('array_merge', $charsets);
$all_count = array_count_values($all);
if (max($all_count) > 1)
{
echo "Duplicated charsets:\n";
foreach ($all_count as $charset => $count)
{
if ($count > 1)
{
echo "$charset\n";
}
}
}
// And we're done!
return $charsets;
}
}
function charset($charset)
{
$normalized_charset = normalize_character_set($charset);
if ($charsets = build_character_set_list())
{
foreach ($charsets as $preferred => $aliases)
{
if (in_array($normalized_charset, $aliases))
{
return $preferred;
}
}
return $charset;
}
else
{
return false;
}
}
function build_function()
{
if ($charsets = build_character_set_list())
{
$return = <<<EOF
function charset(\$charset)
{
// Normalization from UTS #22
switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\\1', \$charset)))
{
EOF;
foreach ($charsets as $preferred => $aliases)
{
foreach ($aliases as $alias)
{
$return .= "\t\tcase " . var_export($alias, true) . ":\n";
}
$return .= "\t\t\treturn " . var_export($preferred, true) . ";\n\n";
}
$return .= <<<EOF
default:
return \$charset;
}
}
EOF;
return $return;
}
else
{
return false;
}
}
if (php_sapi_name() === 'cli' && realpath($_SERVER['argv'][0]) === __FILE__)
{
echo build_function();
}
?>

38
library/simplepie/db.sql Normal file
View file

@ -0,0 +1,38 @@
/* SQLite */
CREATE TABLE cache_data (
id TEXT NOT NULL,
items SMALLINT NOT NULL DEFAULT 0,
data BLOB NOT NULL,
mtime INTEGER UNSIGNED NOT NULL
);
CREATE UNIQUE INDEX id ON cache_data(id);
CREATE TABLE items (
feed_id TEXT NOT NULL,
id TEXT NOT NULL,
data TEXT NOT NULL,
posted INTEGER UNSIGNED NOT NULL
);
CREATE INDEX feed_id ON items(feed_id);
/* MySQL */
CREATE TABLE `cache_data` (
`id` TEXT CHARACTER SET utf8 NOT NULL,
`items` SMALLINT NOT NULL DEFAULT 0,
`data` BLOB NOT NULL,
`mtime` INT UNSIGNED NOT NULL,
UNIQUE (
`id`(125)
)
);
CREATE TABLE `items` (
`feed_id` TEXT CHARACTER SET utf8 NOT NULL,
`id` TEXT CHARACTER SET utf8 NOT NULL,
`data` TEXT CHARACTER SET utf8 NOT NULL,
`posted` INT UNSIGNED NOT NULL,
INDEX `feed_id` (
`feed_id`(125)
)
);

View file

@ -0,0 +1,23 @@
#!/usr/bin/php
<?php
include_once('../simplepie.inc');
// Parse it
$feed = new SimplePie();
if (isset($argv[1]) && $argv[1] !== '')
{
$feed->set_feed_url($argv[1]);
$feed->enable_cache(false);
$feed->init();
}
$items = $feed->get_items();
foreach ($items as $item)
{
echo $item->get_title() . "\n";
}
var_dump($feed->get_item_quantity());
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

View file

@ -0,0 +1,5 @@
<html>
<head>
<meta http-equiv="refresh" content="0;url=http://www.jeroenwijering.com/extras/readme.html">
</head>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -0,0 +1,35 @@
/*=:project
scalable Inman Flash Replacement (sIFR) version 3.
=:file
Copyright: 2006 Mark Wubben.
Author: Mark Wubben, <http://novemberborn.net/>
=:history
* IFR: Shaun Inman
* sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin
* sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben
=:license
This software is licensed and provided under the CC-GNU LGPL.
See <http://creativecommons.org/licenses/LGPL/2.1/>
*/
/* This is the print stylesheet to hide the Flash headlines from the browser... regular browser text headlines will now print as normal */
.sIFR-flash {
display: none !important;
height: 0;
width: 0;
position: absolute;
overflow: hidden;
}
.sIFR-alternate {
visibility: visible !important;
display: block !important;
position: static !important;
left: auto !important;
top: auto !important;
}

View file

@ -0,0 +1,39 @@
/*=:project
scalable Inman Flash Replacement (sIFR) version 3.
=:file
Copyright: 2006 Mark Wubben.
Author: Mark Wubben, <http://novemberborn.net/>
=:history
* IFR: Shaun Inman
* sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin
* sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben
=:license
This software is licensed and provided under the CC-GNU LGPL.
See <http://creativecommons.org/licenses/LGPL/2.1/>
*/
/*---- sIFR ---*/
.sIFR-flash {
visibility: visible !important;
margin: 0;
padding: 0;
}
.sIFR-replaced {
visibility: visible !important;
}
.sIFR-alternate {
position: absolute;
left: 0;
top: 0;
width: 0;
height: 0;
display: block;
overflow: hidden;
}
/*---- Header styling ---*/

View file

@ -0,0 +1,40 @@
var yanone_kaffeesatz = {
src: './for_the_demo/yanone-kaffeesatz-bold.swf'
};
var lucida_grande = {
src: './for_the_demo/lucida-grande-bold.swf'
};
sIFR.activate(yanone_kaffeesatz);
//sIFR.activate(lucida_grande);
sIFR.replace(yanone_kaffeesatz, {
//sIFR.replace(lucida_grande, {
selector: 'h3.header',
wmode: 'transparent',
css: {
'.sIFR-root': {
'text-align': 'center',
'color': '#000000',
'font-weight': 'bold',
'background-color': '#EEFFEE',
'font-size': '50px', // For Yanone Kaffeesatz
//'font-size': '40px', // For Lucida Grande
'letter-spacing': '0' // For Yanone Kaffeesatz
//'letter-spacing': '-4' // For Lucida Grande
},
'a': {
'text-decoration': 'none',
'color': '#000000'
},
'a:hover': {
'text-decoration': 'none',
'color': '#666666'
}
}
});

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,397 @@
/*
Theme Name: SimplePie
Theme URI: http://simplepie.org
Description: A simple, yet beautiful theme inspired by several cleanly designed websites.
Version: 1.4
Author: Ryan Parman
Author URI: http://skyzyx.com
Updated: 21 June 2007
*/
/*********************************************
HYPERLINK STYLES
*********************************************/
a {
color:#369;
text-decoration:underline;
padding:0 1px;
}
a:hover {
color:#fff !important;
background-color:#333;
text-decoration:none;
padding:0 1px;
}
a.nohover {
text-decoration:none;
border:none;
}
a.nohover:hover {
background-color:transparent;
border:none;
}
a.namelink {
padding:0;
margin:0;
overflow:hidden;
height:1px;
}
h4 a,
.sample_feeds a {
color:#000;
}
/*********************************************
GENERAL STYLES
*********************************************/
body {
/*font:12px/18px Verdana, sans-serif;*/
font:14px/1.5em "Lucida Grande", Tahoma, sans-serif;
letter-spacing:0px;
color:#333;
background-color:#fff;
margin:0;
padding:0;
}
div#site {
width:600px;
margin:50px auto 0 auto;
}
h1#logo {
margin:0;
padding:0;
text-align:center;
}
h1#logo a,
h1#logo a:hover {
background-color:transparent;
text-decoration:none;
padding:0;
}
h2.image {
margin:0;
padding:0;
text-align:center;
}
h3 {
margin:20px 0 0 0;
padding:0;
font-size:1.5em;
}
h4 {
margin:20px 0 0 0;
padding:0;
font-size:1.2em;
letter-spacing:-1px;
}
h5 {
margin:10px 0 0 0;
padding:0;
font-size:1em;
font-weight:bold;
}
em {
font-style:normal;
background-color:#ffc;
}
p {
margin:0;
padding:5px 0;
}
ul, ol {
margin:10px 0 10px 20px;
padding:0 0 0 15px;
}
ul li, ol li {
margin:0 0 7px 0;
padding:0 0 0 3px;
}
form {
margin:0;
padding:0;
}
code {
font-size:1em;
background-color:#f3f3ff;
color:#000;
}
div#site pre {
background-color:#f3f3ff;
color:#000080;
border:1px dotted #000080;
overflow:auto;
padding:3px 5px;
}
blockquote {
font-size:1em;
color:#666;
border-left:4px solid #666;
margin:10px 0 10px 30px;
padding:0 5px 0 10px;
background:#f3f3f3 url(background_blockquote.png) repeat top left;
}
input, select, textarea {
font-size:12px;
line-height:1.2em;
padding:2px;
}
input[type=text], select, textarea {
background-color:#e9f5ff;
border:1px solid #333;
}
input[type=text]:focus, select:focus, textarea:focus {
background-color:#ffe;
}
.clearLeft {clear:left;}
.clearRight {clear:right;}
.clearBoth {clear:both;}
.hide {display:none;}
/*********************************************
NAVIGATION STYLES
*********************************************/
div#header {
background:#fff url(top_gradient.gif) repeat-x top left;
margin:0;
padding:0;
}
div#header form {
margin:0;
padding:0;
}
div#header div#headerInner {
margin:0;
padding:0;
}
div#header div#headerInner div#logoContainer {}
div#header div#headerInner div#logoContainerInner {
width:550px;
margin:0 auto;
padding:20px;
}
div#header div#headerInner div#logoContainer div#logo {
float:left;
width:200px;
}
div#header div#headerInner div#logoContainer div#logo a,
div#header div#headerInner div#logoContainer div#logo a:hover {
border:none;
background:none;
}
div#header div#headerInner div#logoContainer div#feed {
float:right;
width:300px;
text-align:right;
padding:10px 0 0 0;
}
div#header div#headerInner div#logoContainer div#feed input.text {
width:60%;
}
div#header div#headerInner div#menu {
background:#eee url(background_menuitem_shadow.gif) repeat-x top left;
border-top:2px solid #ccc;
border-bottom:1px solid #ddd;
text-align:center;
}
div#header div#headerInner div#menu table {
width:auto;
margin:0 auto;
}
div#header div#headerInner div#menu ul {
display:block;
width:100%;
margin:0 auto;
padding:0;
font-size:12px;
}
div#header div#headerInner div#menu ul li {
display:block;
float:left;
}
div#header div#headerInner div#menu ul li a {
display:block;
margin:-2px 0 0 0;
padding:5px 7px 8px 7px;
text-decoration:none;
color:#666 !important;
background-color:transparent;
}
div#header div#headerInner div#menu ul li a:hover {
display:block;
margin:-2px 0 0 0;
padding:5px 7px 8px 7px;
text-decoration:none;
color:#666;
background:#fff url(background_menuitem_off.gif) no-repeat bottom right;
}
body#bodydemo div#header div#headerInner div#menu ul li#demo a {
display:block;
margin:-2px 0 0 0;
padding:5px 7px 8px 7px;
text-decoration:none;
color:#333;
font-weight:bold;
background:#fff url(background_menuitem.gif) no-repeat bottom right;
}
/*********************************************
CONTENT STYLES
*********************************************/
div.chunk {
margin:20px 0 0 0;
padding:0 0 10px 0;
border-bottom:1px solid #ccc;
}
div.topchunk {
margin:0 !important;
}
.footnote,
.footnote a {
font-size:12px;
line-height:1.3em;
color:#aaa;
}
.footnote em {
background-color:transparent;
font-style:italic;
}
.footnote code {
background-color:transparent;
font:11px/14px monospace;
color:#aaa;
}
p.subscribe {
background-color:#f3f3f3;
font-size:12px;
text-align:center;
}
p.highlight {
background-color:#ffc;
font-size:12px;
text-align:center;
}
p.sample_feeds {
font-size:12px;
line-height:1.2em;
}
div.sp_errors {
background-color:#eee;
padding:5px;
text-align:center;
font-size:12px;
}
.noborder {
border:none !important;
}
img.favicon {
margin:0 4px -2px 0;
width:16px;
height:16px;
}
p.favicons a,
p.favicons a:hover {
border:none;
background-color:transparent;
}
p.favicons img {
border:none;
}
/*********************************************
DEMO STYLES
*********************************************/
div#sp_input {
background-color:#ffc;
border:2px solid #f90;
padding:5px;
text-align:center;
}
div#sp_input input.text {
border:1px solid #999;
background:#e9f5ff url(feed.png) no-repeat 4px 50%;
width:75%;
padding:2px 2px 2px 28px;
font:18px/22px "Lucida Grande", Verdana, sans-serif;
font-weight:bold;
letter-spacing:-1px;
}
form#sp_form {
margin:15px 0;
}
div.focus {
margin:0;
padding:10px 20px;
background-color:#efe;
}
p.sample_feeds {
text-align:justify;
}
/*********************************************
SIFR STYLES
*********************************************/
.sIFR-active h3.header {
visibility:hidden;
line-height:1em;
}

View file

@ -0,0 +1,31 @@
/**********************************************************
Sleight
(c) 2001, Aaron Boodman
http://www.youngpup.net
**********************************************************/
if (navigator.platform == "Win32" && navigator.appName == "Microsoft Internet Explorer" && window.attachEvent)
{
document.writeln('<style type="text/css">img { visibility:hidden; } </style>');
window.attachEvent("onload", fnLoadPngs);
}
function fnLoadPngs()
{
var rslt = navigator.appVersion.match(/MSIE (\d+\.\d+)/, '');
var itsAllGood = (rslt != null && Number(rslt[1]) >= 5.5);
for (var i = document.images.length - 1, img = null; (img = document.images[i]); i--)
{
if (itsAllGood && img.src.match(/\.png$/i) != null)
{
var src = img.src;
var div = document.createElement("DIV");
div.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "', sizing='scale')"
div.style.width = img.width + "px";
div.style.height = img.height + "px";
img.replaceNode(div);
}
img.style.visibility = "visible";
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View file

@ -0,0 +1,71 @@
/*=:project
scalable Inman Flash Replacement (sIFR) version 3.
=:file
Copyright: 2006 Mark Wubben.
Author: Mark Wubben, <http://novemberborn.net/>
=:history
* IFR: Shaun Inman
* sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin
* sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben
=:license
This software is licensed and provided under the CC-GNU LGPL.
See <http://creativecommons.org/licenses/LGPL/2.1/>
*/
import TextField.StyleSheet;
class SifrStyleSheet extends TextField.StyleSheet {
public var fontSize;
public var latestLeading = 0;
public function parseCSS(cssText:String) {
var native = new TextField.StyleSheet();
var parsed = native.parseCSS(cssText);
if(!parsed) return false;
var selectors = native.getStyleNames();
for(var i = selectors.length - 1; i >= 0; i--) {
var selector = selectors[i];
var nativeStyle = native.getStyle(selector);
var style = this.getStyle(selector) || nativeStyle;
if(style != nativeStyle) {
for(var property in nativeStyle) style[property] = nativeStyle[property];
}
this.setStyle(selector, style);
}
return true;
}
// Apply leading to the textFormat. Much thanks to <http://www.blog.lessrain.com/?p=98>.
private function applyLeading(format, leading) {
this.latestLeading = leading;
if(leading >= 0) {
format.leading = leading;
return format;
}
// Workaround for negative leading, which is ignored otherwise.
var newFormat = new TextFormat(null, null, null, null, null, null, null, null, null, null, null, null, leading);
for(var property in format) if(property != 'leading') newFormat[property] = format[property];
return newFormat;
}
public function transform(style) {
var format = super.transform(style);
if(style.leading) format = applyLeading(format, style.leading);
if(style.letterSpacing) format.letterSpacing = style.letterSpacing;
// Support font sizes relative to the size of .sIFR-root.
if(this.fontSize && style.fontSize && style.fontSize.indexOf('%')) {
format.size = this.fontSize * parseInt(style.fontSize) / 100;
}
format.kerning = _root.kerning == 'true' || !(_root.kerning == 'false') || sIFR.defaultKerning;
return format;
}
}

View file

@ -0,0 +1,12 @@
This is a pre-release nightly of sIFR 3 (r245 to be exact). We (the SimplePie team) will be updating the
sIFR code and font files from time to time as new releases of sIFR 3 are made available.
In this folder you'll find a few Flash 8 files. The only one of you might want to mess with is sifr.fla.
* Open it up
* Double-click the rectangle in the middle
* Select all
* Change the font
More information about sIFR 3 can be found here:
* http://dev.novemberborn.net/sifr3/
* http://wiki.novemberborn.net/sifr3/

View file

@ -0,0 +1,12 @@
// MTASC only parses as-files with class definitions, so here goes...
class Options {
public static function apply() {
sIFR.fromLocal = true;
sIFR.domains = ['*'];
// Parsing `p.foo` might not work, see: <http://livedocs.macromedia.com/flash/mx2004/main_7_2/wwhelp/wwhimpl/common/html/wwhelp.htm?context=Flash_MX_2004&file=00001766.html>
// Appearantly you have to use hex color codes as well, names are not supported!
sIFR.styles.parseCSS('.foo { text-decoration: underline; }');
}
}

View file

@ -0,0 +1,359 @@
/*=:project
scalable Inman Flash Replacement (sIFR) version 3.
=:file
Copyright: 2006 Mark Wubben.
Author: Mark Wubben, <http://novemberborn.net/>
=:history
* IFR: Shaun Inman
* sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin
* sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben
=:license
This software is licensed and provided under the CC-GNU LGPL.
See <http://creativecommons.org/licenses/LGPL/2.1/>
*/
import SifrStyleSheet;
class sIFR {
public static var DEFAULT_TEXT = 'Rendered with sIFR 3, revision 245';
public static var CSS_ROOT_CLASS = 'sIFR-root';
public static var DEFAULT_WIDTH = 300;
public static var DEFAULT_HEIGHT = 100;
public static var DEFAULT_ANTI_ALIAS_TYPE = 'advanced';
public static var MARGIN_LEFT = -3;
public static var PADDING_BOTTOM = 5; // Extra padding to make sure the movie is high enough in most cases.
public static var LEADING_REMAINDER = 2; // Flash uses the specified leading minus 2 as the applied leading.
public static var MAX_FONT_SIZE = 126;
public static var ALIASING_MAX_FONT_SIZE = 48;
//= Holds CSS properties and other rendering properties for the Flash movie.
// *Don't overwrite!*
public static var styles:SifrStyleSheet = new SifrStyleSheet();
//= Allow sIFR to be run from localhost
public static var fromLocal:Boolean = true;
//= Array containing domains for which sIFR may render text. Used to prevent
// hotlinking. Use `*` to allow all domains.
public static var domains:Array = [];
//= Whether kerning is enabled by default. This can be overriden from the client side.
// See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002811.html>.
public static var defaultKerning:Boolean = true;
//= Default value which can be overriden from the client side.
// See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002788.html>.
public static var defaultSharpness:Number = 0;
//= Default value which can be overriden from the client side.
// See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002787.html>.
public static var defaultThickness:Number = 0;
//= Default value which can be overriden from the client side.
// See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002732.html>.
public static var defaultOpacity:Number = -1; // Use client settings
//= Default value which can be overriden from the client side.
// See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002788.html>.
public static var defaultBlendMode:Number = -1; // Use cliest settings
//= Overrides the grid fit type as defined on the client side.
// See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002444.html>.
public static var enforcedGridFitType:String = null;
//= If `true` sIFR won't override the anti aliasing set in the Flash IDE when exporting.
// Thickness and sharpness won't be affected either.
public static var preserveAntiAlias:Boolean = false;
//= If `true` sIFR will disable anti-aliasing if the font size is larger than `ALIASING_MAX_FONT_SIZE`.
// This setting is *independent* from `preserveAntiAlias`.
public static var conditionalAntiAlias:Boolean = true;
//= Sets the anti alias type. By default it's `DEFAULT_ANTI_ALIAS_TYPE`.
// See also <http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002733.html>.
public static var antiAliasType:String = null;
//= Flash filters can be added to this array and will be applied to the text field.
public static var filters:Array = [];
//= A mapping from the names of the filters to their actual objecs, used when transforming
// filters defined on the client. You can add additional filters here so they'll be supported
// when defined on the client.
public static var filterMap:Object = {
DisplacementMapFilter : flash.filters.DisplacementMapFilter,
ColorMatrixFilter : flash.filters.ColorMatrixFilter,
ConvolutionFilter : flash.filters.ConvolutionFilter,
GradientBevelFilter : flash.filters.GradientBevelFilter,
GradientGlowFilter : flash.filters.GradientGlowFilter,
BevelFilter : flash.filters.BevelFilter,
GlowFilter : flash.filters.GlowFilter,
BlurFilter : flash.filters.BlurFilter,
DropShadowFilter : flash.filters.DropShadowFilter
};
private static var instance;
private var textField;
private var content;
private var realHeight;
private var originalHeight;
private var currentHeight;
private var fontSize;
private var tuneWidth;
private var tuneHeight;
//= Sets the default styles for `sIFR.styles`. This method is called
// directly in `sifr.fla`, before options are applied.
public static function setDefaultStyles() {
sIFR.styles.parseCSS([
'.', CSS_ROOT_CLASS, ' { color: #000000; }',
'strong { display: inline; font-weight: bold; } ',
'em { display: inline; font-style: italic; }',
'a { color: #0000FF; text-decoration: underline; }',
'a:hover { color: #0000FF; text-decoration: none; }'
].join(''));
}
//= Validates the domain sIFR is being used on.
// Returns `true` if the domain is valid, `false` otherwise.
public static function checkDomain():Boolean {
if(sIFR.domains.length == 0) return true;
var domain = (new LocalConnection()).domain();
if(sIFR.fromLocal) sIFR.domains.push('localhost');
for(var i = 0; i < sIFR.domains.length; i++) {
var match = sIFR.domains[i];
if(match == '*' || match == domain) return true;
var wildcard = match.lastIndexOf('*');
if(wildcard > -1) {
match = match.substr(wildcard + 1);
var matchPosition = domain.lastIndexOf(match);
if(matchPosition > -1 && (matchPosition + match.length) == domain.length) return true;
}
}
return false;
}
//= Runs sIFR. Called automatically.
public static function run() {
var holder = _root.holder;
var content = checkDomain() ? unescape(_root.content) : DEFAULT_TEXT
if(content == 'undefined' || content == '') {
content = DEFAULT_TEXT;
fscommand('resetmovie', '');
} else fscommand('ping', '');
// Sets stage parameters
Stage.scaleMode = 'noscale';
Stage.align = 'TL';
Stage.showMenu = false;
// Other parameters
var opacity = parseInt(_root.opacity);
if(!isNaN(opacity)) holder._alpha = sIFR.defaultOpacity == -1 ? opacity : sIFR.defaultOpacity;
else holder._alpha = 100;
_root.blendMode = sIFR.defaultBlendMode == -1 ? _root.blendmode : sIFR.defaultBlendMode;
sIFR.instance = new sIFR(holder.txtF, content);
// This should ignore resizes from the callback. Disabled for now.
/* if(_root.zoomsupport == 'true') Stage.addListener({onResize: function() { sIFR.instance.scale() }});*/
// Setup callbacks
_root.watch('callbackTrigger', function() {
sIFR.callback();
return false;
});
}
private static function eval(str) {
var as;
if(str.charAt(0) == '{') { // Ah, we need to create an object
as = {};
str = str.substring(1, str.length - 1);
var $ = str.split(',');
for(var i = 0; i < $.length; i++) {
var $1 = $[i].split(':');
as[$1[0]] = sIFR.eval($1[1]);
}
} else if(str.charAt(0) == '"') { // String
as = str.substring(1, str.length - 1);
} else if(str == 'true' || str == 'false') { // Boolean
as = str == 'true';
} else { // Float
as = parseFloat(str);
}
return as;
}
private function applyFilters() {
var $filters = this.textField.filters;
$filters = $filters.concat(sIFR.filters);
var $ = _root.flashfilters.split(';'); // name,prop:value,...;
for(var i = 0; i < $.length; i++) {
var $1 = $[i].split(',');
var newFilter = new sIFR.filterMap[$1[0]]();
for(var j = 1; j < $1.length; j++) {
var $2 = $1[j].split(':');
newFilter[$2[0]] = sIFR.eval(unescape($2[1]));
}
$filters.push(newFilter);
}
this.textField.filters = $filters;
}
private function sIFR(textField, content) {
this.textField = textField;
this.content = content;
var offsetLeft = parseInt(_root.offsetleft);
textField._x = MARGIN_LEFT + (isNaN(offsetLeft) ? 0 : offsetLeft);
var offsetTop = parseInt(_root.offsettop);
if(!isNaN(offsetTop)) textField._y += offsetTop;
tuneWidth = parseInt(_root.tunewidth);
if(isNaN(tuneWidth)) tuneWidth = 0;
tuneHeight = parseInt(_root.tuneheight);
if(isNaN(tuneHeight)) tuneHeight = 0;
textField._width = tuneWidth + (isNaN(parseInt(_root.width)) ? DEFAULT_WIDTH : parseInt(_root.width));
textField._height = tuneHeight + (isNaN(parseInt(_root.height)) ? DEFAULT_HEIGHT : parseInt(_root.height));
textField.wordWrap = true;
textField.selectable = _root.selectable == 'true';
textField.gridFitType = sIFR.enforcedGridFitType || _root.gridfittype;
this.applyFilters();
// Determine font-size and the number of lines
this.fontSize = parseInt(_root.size);
if(isNaN(this.fontSize)) this.fontSize = 26;
styles.fontSize = this.fontSize;
if(!sIFR.preserveAntiAlias && (sIFR.conditionalAntiAlias && this.fontSize < ALIASING_MAX_FONT_SIZE
|| !sIFR.conditionalAntiAlias)) {
textField.antiAliasType = sIFR.antiAliasType || DEFAULT_ANTI_ALIAS_TYPE;
}
if(!sIFR.preserveAntiAlias || !isNaN(parseInt(_root.sharpness))) {
textField.sharpness = parseInt(_root.sharpness);
}
if(isNaN(textField.sharpness)) textField.sharpness = sIFR.defaultSharpness;
if(!sIFR.preserveAntiAlias || !isNaN(parseInt(_root.thickness))) {
textField.thickness = parseInt(_root.thickness);
}
if(isNaN(textField.thickness)) textField.thickness = sIFR.defaultThickness;
// Set font-size and other styles
sIFR.styles.parseCSS(unescape(_root.css));
var rootStyle = styles.getStyle('.sIFR-root') || {};
rootStyle.fontSize = this.fontSize; // won't go higher than 126!
styles.setStyle('.sIFR-root', rootStyle);
textField.styleSheet = styles;
this.write(content);
this.repaint();
}
private function repaint() {
var leadingFix = this.isSingleLine() ? sIFR.styles.latestLeading : 0;
if(leadingFix > 0) leadingFix -= LEADING_REMAINDER;
// Flash wants to scroll the movie by one line, by adding the fontSize to the
// textField height this is no longer happens. We also add the absolute tuneHeight,
// to prevent a negative value from triggering the bug. We won't send the fake
// value to the JavaScript side, though.
textField._height = textField.textHeight + PADDING_BOTTOM + this.fontSize + Math.abs(tuneHeight) + tuneHeight - leadingFix;
this.realHeight = textField._height - this.fontSize - Math.abs(tuneHeight);
var arg = 'height:' + this.realHeight;
if(_root.fitexactly == 'true') arg += ',width:' + (textField.textWidth + tuneWidth);
fscommand('resize', arg);
this.originalHeight = textField._height;
this.currentHeight = Stage.height;
textField._xscale = textField._yscale = parseInt(_root.zoom);
}
private function write(content) {
this.textField.htmlText = ['<p class="', CSS_ROOT_CLASS, '">',
content, '</p>'
].join('');
}
private function isSingleLine() {
return Math.round((this.textField.textHeight - sIFR.styles.latestLeading) / this.fontSize) == 1;
}
//= Scales the text field to the new scale of the Flash movie itself.
public function scale() {
this.currentHeight = Stage.height;
var scale = 100 * Math.round(this.currentHeight / this.originalHeight);
textField._xscale = textField._yscale = scale;
}
private function calculateRatios() {
var strings = ['X', 'X<br>X', 'X<br>X<br>X', 'X<br>X<br>X<br>X'];
var results = {};
for(var i = 1; i <= strings.length; i++) {
var size = 6;
this.write(strings[i - 1]);
while(size < MAX_FONT_SIZE) {
var rootStyle = sIFR.styles.getStyle('.sIFR-root') || {};
rootStyle.fontSize = size;
sIFR.styles.setStyle('.sIFR-root', rootStyle);
this.textField.styleSheet = sIFR.styles;
this.repaint();
var ratio = (this.realHeight - PADDING_BOTTOM) / i / size;
if(!results[size]) results[size] = ratio;
else results[size] = ((i - 1) * results[size] + ratio) / i;
size++;
}
}
var sizes = [], ratios = [];
var ratiosToSizes = {}, sizesToRatios = {};
for(var size in results) {
if(results[size] == Object.prototype[size]) continue;
var ratio = results[size];
ratiosToSizes[ratio] = Math.max(ratio, parseInt(size));
}
for(var ratio in ratiosToSizes) {
if(ratiosToSizes[ratio] == Object.prototype[ratio]) continue;
sizesToRatios[ratiosToSizes[ratio]] = roundDecimals(ratio, 2);
sizes.push(ratiosToSizes[ratio]);
}
sizes.sort(function(a, b) { return a - b; });
for(var j = 0; j < sizes.length - 1; j++) ratios.push(sizes[j], sizesToRatios[sizes[j]]);
ratios.push(sizesToRatios[sizes[sizes.length - 1]]);
fscommand('debug:ratios', '[' + ratios.join(',') + ']');
}
private function roundDecimals(value, decimals) {
return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
}
public static function callback() {
switch(_root.callbackType) {
case 'replacetext':
sIFR.instance.content = _root.callbackValue;
sIFR.instance.write(_root.callbackValue);
sIFR.instance.repaint();
break;
case 'resettext':
sIFR.instance.write('');
sIFR.instance.write(sIFR.instance.content);
break;
case 'ratios':
sIFR.instance.calculateRatios();
break;
}
}
}

Some files were not shown because too many files have changed in this diff Show more