diff --git a/lib/Pi/File/Transfer/Upload.php b/lib/Pi/File/Transfer/Upload.php index 0c7e97a966..1c3722f9b4 100644 --- a/lib/Pi/File/Transfer/Upload.php +++ b/lib/Pi/File/Transfer/Upload.php @@ -202,4 +202,51 @@ public function getUploaded($name = null, $path = false) } return $result; } -} + + /** + * Get file(s) info + * + * @return array + */ + public function getInfo() + { + $ret = array(); + $files = $this->getAdapter()->getFileList(); + foreach ($files as $key => $data) { + $ret['name'] = $data['name']; + $ret['size'] = $data['size']; + $ret['type'] = $data['type']; + } + return $ret; + } + + /** + * Get file size + * + * @return size number + */ + public function getSize() + { + return $this->getInfo()['size']; + } + + /** + * Get file name + * + * @return file name + */ + public function getName() + { + return $this->getInfo()['name']; + } + + /** + * Get file Type + * + * @return file Type + */ + public function getType() + { + return $this->getInfo()['type']; + } +} \ No newline at end of file diff --git a/lib/Pi/Mvc/Controller/Plugin/Alias.php b/lib/Pi/Mvc/Controller/Plugin/Alias.php new file mode 100644 index 0000000000..32c7912117 --- /dev/null +++ b/lib/Pi/Mvc/Controller/Plugin/Alias.php @@ -0,0 +1,92 @@ + + * @since 3.0 + * @package Pi\Mvc + * @version $Id$ + */ + +namespace Pi\Mvc\Controller\Plugin; + +use Pi; +use Zend\Mvc\Controller\Plugin\AbstractPlugin; +use Zend\Mvc\MvcEvent; + +class Alias extends AbstractPlugin +{ + public $_search = array(" ","\t","\r\n","\r","\n",",",".","'",";",":",")", + "(",'"','?','!','{','}','[',']','<','>','/','+','-','_', + '\\','*','=','@','#','$','%','^','&'); + public $_replace = array(' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + ' ',' ',' ',' ',' ',''); + + /* + * $this->alias($alias); + */ + public function __invoke($alias, $id, $model) + { + $alias = $this->setAlias($alias); + $alias = $this->checkAlias($alias, $id, $model); + return $alias; + } + + /** + * Returns the alias + * + * @return boolean + */ + public function setAlias($alias) + { + $alias = strip_tags($alias); + $alias = strtolower($alias); + $alias = htmlentities($alias, ENT_COMPAT, 'utf-8'); + $alias = preg_replace('`\[.*\]`U', ' ', $alias); + $alias = preg_replace('`&(amp;)?#?[a-z0-9]+;`i', ' ', $alias); + $alias = preg_replace('`&([a-z])(acute|uml|circ|grave|ring|cedil|slash|tilde|caron|lig);`i', '\\1', $alias); + $alias = str_replace($this->_search, $this->_replace, $alias); + $alias = explode(' ',$alias); + foreach($alias as $word) { + if(!empty($word)) { + $key[] = $word; + } + } + $alias = implode('-',$key); + return $alias; + } + + /** + * Check alias exit ot not + * + * @return boolean + */ + public function checkAlias($alias, $id, $model) + { + if (empty($id)) { + $select = $model->select()->columns(array('id', 'alias'))->where(array('alias' => $alias)); + } else { + $select = $model->select()->columns(array('id', 'alias'))->where(array('alias' => $alias, 'id != ?' => $id)); + } + $rowset = $model->selectWith($select); + if($rowset->count()) { + /* + * This part need improvment + * replace rand function whit method for add number + */ + $alias = $this->setAlias($alias . ' ' . rand(1, 9999)); + $alias = $this->checkAlias($alias, $id, $model); + } + return $alias; + } +} \ No newline at end of file diff --git a/lib/Pi/Mvc/Controller/Plugin/Meta.php b/lib/Pi/Mvc/Controller/Plugin/Meta.php new file mode 100644 index 0000000000..01536637d8 --- /dev/null +++ b/lib/Pi/Mvc/Controller/Plugin/Meta.php @@ -0,0 +1,73 @@ + + * @since 3.0 + * @package Pi\Mvc + * @version $Id$ + */ + +namespace Pi\Mvc\Controller\Plugin; + +use Zend\Mvc\Controller\Plugin\AbstractPlugin; + +class Meta extends AbstractPlugin +{ + public $_search = array(" ","\t","\r\n","\r","\n",",",".","'",";",":",")", + "(",'"','?','!','{','}','[',']','<','>','/','+','-','_', + '\\','*','=','@','#','$','%','^','&'); + public $_replace = array(' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', + ' ',' ',' ',' ',' ',''); + + /* + * $this->meta()->keywords($keywords); + * $this->meta()->keywords($keywords, $number, $limit); + */ + public function keywords($keywords, $number = '6', $limit = '3') + { + $keywords = strip_tags($keywords); + $keywords = strtolower($keywords); + $keywords = htmlentities($keywords, ENT_COMPAT, 'utf-8'); + $keywords = preg_replace('`\[.*\]`U', '', $keywords); + $keywords = preg_replace('`&(amp;)?#?[a-z0-9]+;`i', '', $keywords); + $keywords = preg_replace('`&([a-z])(acute|uml|circ|grave|ring|cedil|slash|tilde|caron|lig);`i', '\\1', $keywords); + $keywords = str_replace($this->_search, $this->_replace, $keywords); + $keywords = explode(' ',$keywords); + $keywords = array_unique($keywords); + foreach($keywords as $keyword) { + if(mb_strlen($keyword) >= $limit && !empty($keyword) && !is_numeric($keyword)) { + $key[] = $keyword; + } + } + $key = array_slice($key, 0, $number); + $keywords = implode(',',$key); + $keywords = trim($keywords, ','); + return $keywords; + } + + /* + * $this->meta()->description($description); + */ + public function description($description) + { + $description = strip_tags($description); + $description = strtolower($description); + $description = htmlentities($description, ENT_COMPAT, 'utf-8'); + $description = preg_replace('`\[.*\]`U', '', $description); + $description = preg_replace('`&(amp;)?#?[a-z0-9]+;`i', '-', $description); + $description = preg_replace('`&([a-z])(acute|uml|circ|grave|ring|cedil|slash|tilde|caron|lig);`i', '\\1', $description); + $description = str_replace($this->_search, $this->_replace, $description); + return $description; + } +} \ No newline at end of file diff --git a/lib/Pi/Mvc/Controller/Plugin/Resize.php b/lib/Pi/Mvc/Controller/Plugin/Resize.php new file mode 100644 index 0000000000..8d0d700384 --- /dev/null +++ b/lib/Pi/Mvc/Controller/Plugin/Resize.php @@ -0,0 +1,277 @@ + + * @author Herve Thouzard + * @author Jarrod Oberto + * @since 3.0 + * @package Pi\Mvc + * @version $Id$ + */ + +namespace Pi\Mvc\Controller\Plugin; + +use Pi; +use Zend\Mvc\Controller\Plugin\AbstractPlugin; + +class Resize extends AbstractPlugin +{ + private $image; + private $imageResized; + private $width; + private $height; + private $mime; + + /** + * Resize a Picture to some given dimensions (using the wideImage library) + * @param string $image Picture's name + * @param string $src_path Picture's source pach + * @param string $dst_path Picture's destination + * @param integer $width Maximum picture's width + * @param integer $height Maximum picture's height + * @param boolean $keep_original Do we have to keep the original picture ? + * @param string $option is crop, exact, portrait, landscape, auto + * @return true / false + */ + public function __invoke($file, $src_path, $dst_path, $param_width, $param_height, $keep_original = true, $option = 'auto') + { + // Set main paths + $src_path = Pi::path('upload') . '/' . $src_path; + $dst_path = Pi::path('upload') . '/' . $dst_path; + // Make dir + if (!is_dir($dst_path)) { + Pi::service('file')->mkdir($dst_path); + } + // Set image path + $src_path = $src_path . '/' . $file; + $dst_path = $dst_path . '/' . $file; + // start resize + $doresize = true; + $pictureDimensions = getimagesize($src_path); + if (is_array($pictureDimensions)) { + // Set output array + $this->width = $pictureDimensions[0]; + $this->height = $pictureDimensions[1]; + $this->mime = $pictureDimensions['mime']; + // Check image dimensions for resize + if ($this->width < $param_width && $this->height < $param_height) { + $doresize = false; + } + // Do resize or copy image + if ($doresize) { + // Will add resize codes + $this->openImage($src_path); + $this->resizeImage($param_width, $param_height); + $this->saveImage($dst_path); + } else { + Pi::service('file')->copy($src_path, $dst_path); + } + // Keep original image or not + if (!$keep_original) { + Pi::service('file')->remove($src_path); + } + // return info + return true; + } + return false; + } + + /** + * All subsequent functions written by Jarrod Oberto + * For more information please check his website : http://www.jarrodoberto.com + * source : http://net.tutsplus.com/tutorials/php/image-resizing-made-easy-with-php/ + */ + private function openImage($file) + { + // *** Get extension + $extension = strtolower(strrchr($file, '.')); + + switch ($extension) { + case '.jpg': + case '.jpeg': + $img = @imagecreatefromjpeg($file); + break; + case '.gif': + $img = @imagecreatefromgif($file); + break; + case '.png': + $img = @imagecreatefrompng($file); + break; + default: + $img = false; + break; + } + $this->image = $img; + } + + private function resizeImage($newWidth, $newHeight, $option = "auto") + { + // *** Get optimal width and height - based on $option + $optionArray = $this->getDimensions($newWidth, $newHeight, $option); + $optimalWidth = $optionArray['optimalWidth']; + $optimalHeight = $optionArray['optimalHeight']; + + // *** Resample - create image canvas of x, y size + $this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight); + imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height); + + // *** if option is 'crop', then crop too + if ($option == 'crop') { + $this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight); + } + } + + private function getDimensions($newWidth, $newHeight, $option) + { + switch ($option) { + case 'exact': + $optimalWidth = $newWidth; + $optimalHeight = $newHeight; + break; + case 'portrait': + $optimalWidth = $this->getSizeByFixedHeight($newHeight); + $optimalHeight = $newHeight; + break; + case 'landscape': + $optimalWidth = $newWidth; + $optimalHeight = $this->getSizeByFixedWidth($newWidth); + break; + case 'auto': + $optionArray = $this->getSizeByAuto($newWidth, $newHeight); + $optimalWidth = $optionArray['optimalWidth']; + $optimalHeight = $optionArray['optimalHeight']; + break; + case 'crop': + $optionArray = $this->getOptimalCrop($newWidth, $newHeight); + $optimalWidth = $optionArray['optimalWidth']; + $optimalHeight = $optionArray['optimalHeight']; + break; + } + return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight); + } + + private function getSizeByFixedHeight($newHeight) + { + $ratio = $this->width / $this->height; + $newWidth = $newHeight * $ratio; + return $newWidth; + } + + private function getSizeByFixedWidth($newWidth) + { + $ratio = $this->height / $this->width; + $newHeight = $newWidth * $ratio; + return $newHeight; + } + + private function getSizeByAuto($newWidth, $newHeight) + { + if ($this->height < $this->width) + // *** Image to be resized is wider (landscape) + { + $optimalWidth = $newWidth; + $optimalHeight = $this->getSizeByFixedWidth($newWidth); + } elseif ($this->height > $this->width) + // *** Image to be resized is taller (portrait) + { + $optimalWidth = $this->getSizeByFixedHeight($newHeight); + $optimalHeight = $newHeight; + } else + // *** Image to be resizerd is a square + { + if ($newHeight < $newWidth) { + $optimalWidth = $newWidth; + $optimalHeight = $this->getSizeByFixedWidth($newWidth); + } else if ($newHeight > $newWidth) { + $optimalWidth = $this->getSizeByFixedHeight($newHeight); + $optimalHeight = $newHeight; + } else { + // *** Sqaure being resized to a square + $optimalWidth = $newWidth; + $optimalHeight = $newHeight; + } + } + return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight); + } + + private function getOptimalCrop($newWidth, $newHeight) + { + $heightRatio = $this->height / $newHeight; + $widthRatio = $this->width / $newWidth; + + if ($heightRatio < $widthRatio) { + $optimalRatio = $heightRatio; + } else { + $optimalRatio = $widthRatio; + } + + $optimalHeight = $this->height / $optimalRatio; + $optimalWidth = $this->width / $optimalRatio; + + return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight); + } + + private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight) + { + // *** Find center - this will be used for the crop + $cropStartX = ($optimalWidth / 2) - ($newWidth / 2); + $cropStartY = ($optimalHeight / 2) - ($newHeight / 2); + + $crop = $this->imageResized; + //imagedestroy($this->imageResized); + + // *** Now crop from center to exact requested size + $this->imageResized = imagecreatetruecolor($newWidth, $newHeight); + imagecopyresampled($this->imageResized, $crop, 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight, $newWidth, $newHeight); + } + + + public function saveImage($savePath, $imageQuality = "100") + { + // *** Get extension + $extension = strrchr($savePath, '.'); + $extension = strtolower($extension); + + switch ($extension) { + case '.jpg': + case '.jpeg': + if (imagetypes() & IMG_JPG) { + imagejpeg($this->imageResized, $savePath, $imageQuality); + } + break; + + case '.gif': + if (imagetypes() & IMG_GIF) { + imagegif($this->imageResized, $savePath); + } + break; + + case '.png': + // *** Scale quality from 0-100 to 0-9 + $scaleQuality = round(($imageQuality / 100) * 9); + // *** Invert quality setting as 0 is best, not 9 + $invertScaleQuality = 9 - $scaleQuality; + + if (imagetypes() & IMG_PNG) { + imagepng($this->imageResized, $savePath, $invertScaleQuality); + } + break; + + default: + // *** No extension - No save. + break; + } + + imagedestroy($this->imageResized); + } +} \ No newline at end of file diff --git a/lib/Pi/View/Helper/Alert.php b/lib/Pi/View/Helper/Alert.php new file mode 100644 index 0000000000..167dd0796b --- /dev/null +++ b/lib/Pi/View/Helper/Alert.php @@ -0,0 +1,61 @@ + + * @since 3.0 + * @package Pi\View + * @subpackage Helper + * @version $Id$ + */ + +namespace Pi\View\Helper; + +use Pi; +use Zend\View\Helper\AbstractHtmlElement; + +/** + * Helper for loading Alert + * + * Usage inside a phtml template: + * + * $this->alert('title', 'body'); + * $this->alert('title', 'body', 'class'); + * + */ +class Alert extends AbstractHtmlElement +{ + /** + * Make Button + * + * @param string + * @param string + * @param string + * @return Alert + */ + public function __invoke($title, $body, $class = null) + { + + if(!in_array($class, array('alert-success','alert-error','alert-info'))) { + $class = ''; + } + + return '
' . self::EOL + . '×' . self::EOL + . '

' . $title . '

' . self::EOL + . $body . self::EOL + . '
' . self::EOL + . '' . self::EOL; + } +} \ No newline at end of file diff --git a/lib/Pi/View/Helper/Facebook.php b/lib/Pi/View/Helper/Facebook.php new file mode 100644 index 0000000000..3b49d30a32 --- /dev/null +++ b/lib/Pi/View/Helper/Facebook.php @@ -0,0 +1,57 @@ + + * @since 3.0 + * @package Pi\View + * @subpackage Helper + * @version $Id$ + */ + +namespace Pi\View\Helper; + +use Pi; +use Zend\View\Helper\AbstractHtmlElement; + +/** + * Helper for loading facebook + * + * Usage inside a phtml template: + * + * $this->facebook(); + * + */ +class Facebook extends AbstractHtmlElement +{ + /** + * Make Plusone + * + * @param array + * @return Button + */ + public function __invoke() + { + $facebook = '
' . self::EOL + . '' . self::EOL + . '
' . self::EOL; + return $facebook; + } +} diff --git a/lib/Pi/View/Helper/Pager.php b/lib/Pi/View/Helper/Pager.php new file mode 100644 index 0000000000..61831bed70 --- /dev/null +++ b/lib/Pi/View/Helper/Pager.php @@ -0,0 +1,71 @@ + + * @since 3.0 + * @package Pi\View + * @subpackage Helper + * @version $Id$ + */ + +namespace Pi\View\Helper; + +use Pi; +use Zend\View\Helper\AbstractHtmlElement; + +/** + * Helper for loading Pager + * + * Usage inside a phtml template: + * + * $this->pager(array, false); + * + */ +class Pager extends AbstractHtmlElement +{ + /** + * Make Button + * + * @param string + * @param string + * @param string + * @return Button + */ + public function __invoke($link, $align = false) + { + + if($align) { + $larr = '←'; + $rarr = '→'; + } else { + $larr = ''; + $rarr = ''; + } + + if(!empty($link['previous'])) { + $previous = '' . $larr . ' ' . __('Older') . ''; + $previous = '' . self::EOL; + } else { + $previous = ''; + } + + if(!empty($link['next'])) { + $next = '' . __('Newer') . ' ' . $rarr . ''; + $next = '' . self::EOL; + } else { + $next = ''; + } + + return ''; + } +} \ No newline at end of file diff --git a/lib/Pi/View/Helper/Plusone.php b/lib/Pi/View/Helper/Plusone.php new file mode 100644 index 0000000000..f680f75f35 --- /dev/null +++ b/lib/Pi/View/Helper/Plusone.php @@ -0,0 +1,67 @@ + + * @since 3.0 + * @package Pi\View + * @subpackage Helper + * @version $Id$ + */ + +namespace Pi\View\Helper; + +use Pi; +use Zend\View\Helper\AbstractHtmlElement; + +/** + * Helper for loading google pliusone + * + * Usage inside a phtml template: + * + * $this->plusone(); + * $this->plusone(array()); + * + */ +class Plusone extends AbstractHtmlElement +{ + /** + * Make Plusone + * + * @param array + * @return Button + */ + public function __invoke($setting = '') + { + $attribs = array(); + if(isset($setting['data-size']) && in_array($setting['data-size'], array('small', 'medium', 'tall'))) { + $attribs['data-size'] = $setting['data-size']; + } + + if(isset($setting['data-annotation']) && in_array($setting['data-annotation'], array('inline', 'none'))) { + $attribs['data-annotation'] = $setting['data-annotation']; + } + + if(isset($setting['data-annotation'], $setting['data-width']) && $setting['data-annotation'] == 'inline' && is_numeric($setting['data-width']) ) { + $attribs['data-width'] = $setting['data-width']; + } + + return '
htmlAttribs($attribs) . '>
' . self::EOL + . ''; + } +} \ No newline at end of file diff --git a/lib/Pi/View/Helper/Twitter.php b/lib/Pi/View/Helper/Twitter.php new file mode 100644 index 0000000000..1dd6d6dfa3 --- /dev/null +++ b/lib/Pi/View/Helper/Twitter.php @@ -0,0 +1,55 @@ + + * @since 3.0 + * @package Pi\View + * @subpackage Helper + * @version $Id$ + */ + +namespace Pi\View\Helper; + +use Pi; +use Zend\View\Helper\AbstractHtmlElement; + +/** + * Helper for loading twitter + * + * Usage inside a phtml template: + * + * $this->twitter(); + * + */ +class Twitter extends AbstractHtmlElement +{ + /** + * Make Plusone + * + * @param array + * @return Button + */ + public function __invoke() + { + $twitter = 'Tweet' . self::EOL + . '' . self::EOL; + return $twitter; + } +} \ No newline at end of file diff --git a/www/static/vendor/backbone/backbone-min.js b/www/static/vendor/backbone/backbone-min.js index 90bc0f776e..3541019c58 100644 --- a/www/static/vendor/backbone/backbone-min.js +++ b/www/static/vendor/backbone/backbone-min.js @@ -1,56 +1,4 @@ -// Backbone.js 0.9.2 - -// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://backbonejs.org -(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks= -{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g= -z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent= -{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null== -b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent: -b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)}; -a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error, -h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t(); -return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending= -{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length|| -!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator); -this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c=b))this.iframe=i('