").replace(/\s/g," ")},void 0===b&&(b=this.getPos()),j=c.val().slice(0,b),f=c.val().slice(b),h=""+g(j)+"",h+="|",h+=""+g(f)+"",i=new d(c),e=i.create(h).rect()},b.prototype.getIEPosition=function(a){var b,c,d,e,f;return d=this.getIEOffset(a),c=this.$inputor.offset(),e=d.left-c.left,f=d.top-c.top,b=d.height,{left:e,top:f,height:b}},b}(),d=function(){function b(a){this.$inputor=a}return b.prototype.css_attr=["borderBottomWidth","borderLeftWidth","borderRightWidth","borderTopStyle","borderRightStyle","borderBottomStyle","borderLeftStyle","borderTopWidth","boxSizing","fontFamily","fontSize","fontWeight","height","letterSpacing","lineHeight","marginBottom","marginLeft","marginRight","marginTop","outlineWidth","overflow","overflowX","overflowY","paddingBottom","paddingLeft","paddingRight","paddingTop","textAlign","textOverflow","textTransform","wordBreak","wordWrap"],b.prototype.mirrorCss=function(){var b,c=this;return b={position:"absolute",left:-9999,top:0,zIndex:-2e4,whiteSpace:"pre-wrap"},"TEXTAREA"===this.$inputor.prop("tagName")&&this.css_attr.push("width"),a.each(this.css_attr,function(a,d){return b[d]=c.$inputor.css(d)}),b},b.prototype.create=function(b){return this.$mirror=a(""),this.$mirror.css(this.mirrorCss()),this.$mirror.html(b),this.$inputor.after(this.$mirror),this},b.prototype.rect=function(){var a,b,c;return a=this.$mirror.find("#caret"),b=a.position(),c={left:b.left,top:b.top,height:a.height()},this.$mirror.remove(),c},b}(),e={contentEditable:function(a){return!(!a[0].contentEditable||"true"!==a[0].contentEditable)}},g={pos:function(a){return a||0===a?this.setPos(a):this.getPos()},position:function(a){return h.selection?this.getIEPosition(a):this.getPosition(a)},offset:function(a){var b;return b=this.getOffset(a)}},h=null,j=null,i=null,l=function(a){var b;return(b=null!=a?a.iframe:void 0)?(i=b,j=b.contentWindow,h=b.contentDocument||j.document):(i=void 0,j=window,h=document)},f=function(a){var b;h=a[0].ownerDocument,j=h.defaultView||h.parentWindow;try{return i=j.frameElement}catch(c){b=c}},a.fn.caret=function(d,f,h){var i;return g[d]?(a.isPlainObject(f)?(l(f),f=void 0):l(h),i=e.contentEditable(this)?new b(this):new c(this),g[d].apply(i,[f])):a.error("Method "+d+" does not exist on jQuery.caret")},a.fn.caret.EditableCaret=b,a.fn.caret.InputCaret=c,a.fn.caret.Utils=e,a.fn.caret.apis=g}); -------------------------------------------------------------------------------- /application/models/userModel.php: -------------------------------------------------------------------------------- 1 | ssdb->hget($this->prefix.$uid,'username'); 15 | if($username!=null){ 16 | return $username; 17 | }else{ 18 | return false; 19 | } 20 | } 21 | /* 22 | * get uid by username 23 | * @param char $username 24 | */ 25 | public function getUidByUsername($username){ 26 | $uid=$this->mysql->table('user')->where(array('username'=>$username))->field('uid')->find(); 27 | if(isint($uid['uid'])){ 28 | return $uid['uid']; 29 | }else{ 30 | return false; 31 | } 32 | } 33 | /* 34 | * get username attibution by uid, e.g. username email reg_ip ctime 35 | * @param int $uid 36 | * @param char or array $attrname 37 | */ 38 | public function getAttrByUid($uid,$attrname){ 39 | if(is_array($attrname)){ 40 | $attr=$this->ssdb->multi_hget($this->prefix.$uid,$attrname); 41 | return $attr; 42 | }else{ 43 | $attr=$this->ssdb->hget($this->prefix.$uid,$attrname); 44 | return $attr; 45 | } 46 | } 47 | /* 48 | * get username attibution by attrname, e.g. email reg_ip ctime, but not including uid 49 | * @param char $username 50 | * @param char or array $attrname 51 | */ 52 | public function getAttrByUsername($username,$attrname){ 53 | $uid=$this->getUidByUsername($username); 54 | if(isint($uid)){ 55 | $attr=$this->getAttrByUid($uid,$attrname); 56 | if($attr!=null){ 57 | return $attr; 58 | }else{ 59 | return false; 60 | } 61 | }else{ 62 | return false; 63 | } 64 | } 65 | /* 66 | * get user info by uid 67 | * @param int $uid 68 | */ 69 | public function getUserInfoByUid($uid){ 70 | $userinfo=$this->ssdb->hgetall($this->prefix.$uid); 71 | if($userinfo!=null){ 72 | return $userinfo; 73 | }else{ 74 | return false; 75 | } 76 | } 77 | /* 78 | * get user info by username 79 | * @param char $username 80 | */ 81 | public function getUserInfoByUsername($username){ 82 | $uid=$this->getUidByUsername($username); 83 | if(isint($uid)){ 84 | $userinfo=$this->getUserInfoByUid($uid); 85 | return $userinfo; 86 | }else{ 87 | return false; 88 | } 89 | } 90 | /* 91 | * check user has the attrbution e.g. notice msg 92 | * @param int $uid 93 | * @param char $attr 94 | */ 95 | public function hasAttr($uid,$attr){ 96 | return $this->ssdb->hexists($this->prefix.$uid,$attr); 97 | } 98 | /* 99 | * set user attribution 100 | * @param int $int 101 | * @param char $attr 102 | * @param any $value 103 | */ 104 | public function setUserAttr($uid,$attr,$value){ 105 | $result=$this->ssdb->hset($this->prefix.$uid,$attr,$value); 106 | return $result; 107 | } 108 | /* 109 | * @param int $uid 110 | * @param array $attr 111 | */ 112 | public function setUserAttrs($uid,$attr){ 113 | $result=$this->ssdb->multi_hset($this->prefix.$uid,$attr); 114 | if($result==null){ 115 | return false; 116 | }else{ 117 | return true; 118 | } 119 | } 120 | /* 121 | * @param int $uid 122 | */ 123 | public function freezeUser($uid){ 124 | return $this->ssdb->multi_hdel($this->prefix.$uid,array('new_msg','new_notice')); 125 | } 126 | /* 127 | * increase user attibution value e.g. notice msg 128 | * @param int $int 129 | * @param char $attr 130 | * @param int $incr_num 131 | */ 132 | public function incrUserAttr($uid,$attr,$incr_num=1){ 133 | return $this->ssdb->hincr($this->prefix.$uid,$attr,$incr_num); 134 | } 135 | /* 136 | * @param char $words 137 | */ 138 | public function searchUser($words){ 139 | $where['username'] = array('like','%'.$words.'%'); 140 | return $this->mysql->table('user')->where($where)->field('uid,username')->limit(5)->select(); 141 | } 142 | /* 143 | * @param int $my_uid 144 | * @param int $hid_uid 145 | */ 146 | public function getUserVoteInteraction($my_uid,$his_uid){ 147 | return $this->ssdb->get($this->vote_interaction_prefix.$my_uid.'_'.$his_uid); 148 | } 149 | /* 150 | * @param int $my_uid 151 | * @param int $hid_uid 152 | */ 153 | public function getUserReplyInteraction($my_uid,$his_uid){ 154 | return $this->ssdb->get($this->reply_interaction_prefix.$my_uid.'_'.$his_uid); 155 | } 156 | /* 157 | * @param int $my_uid 158 | * @param int $hid_uid 159 | */ 160 | public function incrUserVoteInteraction($my_uid,$his_uid){ 161 | $i=$this->vote_interaction_prefix.$my_uid.'_'.$his_uid; 162 | $this->ssdb->exists($i)?$this->ssdb->incr($i):$this->ssdb->set($i,1); 163 | } 164 | /* 165 | * @param int $my_uid 166 | * @param int $hid_uid 167 | */ 168 | public function incrUserReplyInteraction($my_uid,$his_uid){ 169 | $i=$this->reply_interaction_prefix.$my_uid.'_'.$his_uid; 170 | $this->ssdb->exists($i)?$this->ssdb->incr($i):$this->ssdb->set($i,1); 171 | } 172 | } 173 | ?> -------------------------------------------------------------------------------- /system/controller.php: -------------------------------------------------------------------------------- 1 | config=$config; 11 | $classname=str_replace('Controller','',get_class($this)); 12 | if(file_exists(APP_DIR .'models'.DIRECTORY_SEPARATOR. strtolower($classname) .'Model.php')){ 13 | $this->model=$this->model($classname); 14 | } 15 | $this->set('siteCounter',$this->model('main')->getSiteCounter()); 16 | $cookie=decrypt($_COOKIE[$this->config['cookie_prefix'].'Auth']); 17 | if(isint($cookie)&&$_COOKIE[$this->config['cookie_prefix'].'username']!=''){ 18 | $this->uid=$cookie; 19 | $this->username=urldecode($_COOKIE[$this->config['cookie_prefix'].'username']); 20 | if($_COOKIE[$this->config['cookie_prefix'].'avatar_small']==''){ 21 | $avatar=$this->model('user')->getAttrByUid($cookie,array('small_avatar','big_avatar')); 22 | if($avatar['small_avatar']==''||$avatar['big_avatar']==''){ 23 | setcookie($this->config['cookie_prefix'].'avatar_big',$this->config['default_big_avatar'],0,'/'); 24 | setcookie($this->config['cookie_prefix'].'avatar_small',$this->config['default_small_avatar'],0,'/'); 25 | }else{ 26 | setcookie($this->config['cookie_prefix'].'avatar_small',$avatar['small_avatar'],0,'/'); 27 | setcookie($this->config['cookie_prefix'].'avatar_big',$avatar['big_avatar'],0,'/'); 28 | } 29 | } 30 | $new_inform=$this->model('user')->getAttrByUid($cookie,array('new_notice','new_msg')); 31 | if(count($new_inform)==0&&$_COOKIE[$this->config['cookie_prefix'].'Auth']!=''){//account has been deleted 32 | setcookie($this->config['cookie_prefix'].'Auth','',time()-3600,'/'); 33 | setcookie($this->config['cookie_prefix'].'avatar_big','',time()-3600,'/'); 34 | setcookie($this->config['cookie_prefix'].'avatar_small','',time()-3600,'/'); 35 | setcookie($this->config['cookie_prefix'].'user_info','',time()-3600,'/'); 36 | header('location:'.$this->config['site']); 37 | } 38 | $new_inform['new_msg']=json_decode($new_inform['new_msg'],true); 39 | $this->set('new_inform',$new_inform); 40 | if($_COOKIE[$this->config['cookie_prefix'].'user_info']==''){ 41 | $user_attrs=$this->model('user')->getAttrByUid($cookie,array('thread_num','reply_num','collect_num')); 42 | setcookie($this->config['cookie_prefix'].'user_info',$user_attrs['thread_num'].'_'.$user_attrs['reply_num'].'_'.$user_attrs['collect_num'],0,'/'); 43 | } 44 | } 45 | } 46 | public function model($name){ 47 | require_once(APP_DIR .'models'.DIRECTORY_SEPARATOR. strtolower($name) .'Model.php'); 48 | $name.='Model'; 49 | $model = new $name; 50 | return $model; 51 | } 52 | public function set($var, $val){ 53 | $this->pageVars[$var] = $val; 54 | } 55 | public function render($template){ 56 | $tpl_cache=ROOT_DIR.'runtime'.DIRECTORY_SEPARATOR.'tpl'.DIRECTORY_SEPARATOR.$this->config['theme'].DIRECTORY_SEPARATOR.$template.'.php'; 57 | $tpl=APP_DIR.'views'.DIRECTORY_SEPARATOR . $this->config['theme'].DIRECTORY_SEPARATOR . strtolower($template).'.php'; 58 | if(!file_exists($tpl_cache)){ 59 | //start to create template cache dir 60 | if(!is_dir(ROOT_DIR.'runtime'.DIRECTORY_SEPARATOR.'tpl'.DIRECTORY_SEPARATOR.$this->config['theme'])){ 61 | mkdir(ROOT_DIR.'runtime'.DIRECTORY_SEPARATOR.'tpl',0755); 62 | mkdir(ROOT_DIR.'runtime'.DIRECTORY_SEPARATOR.'tpl'.DIRECTORY_SEPARATOR.$this->config['theme'],0755); 63 | } 64 | copy($tpl,$tpl_cache); 65 | $match=true; 66 | while($match){ 67 | $tpl_content=file_get_contents($tpl_cache); 68 | preg_match_all('/(include|require|include_once|require_once)\((\"|\')(.*?)(\"|\')\)(\s*)\;/',$tpl_content,$matches); 69 | $pattern=array(); 70 | $replace=array(); 71 | if(count($matches[0])>0){ 72 | foreach($matches[0] as $k=>$v){ 73 | $pattern[]='/\<\?php(\s*)'.preg_quote($v).'(\s*)\?\>/'; 74 | $replace[]=file_get_contents(APP_DIR .'views'.DIRECTORY_SEPARATOR.$this->config['theme'].DIRECTORY_SEPARATOR.$matches[3][$k]); 75 | } 76 | $tpl_temp=preg_replace($pattern,$replace,$tpl_content); 77 | file_put_contents($tpl_cache,$tpl_temp); 78 | }else{ 79 | $match=false; 80 | } 81 | } 82 | } 83 | extract($this->pageVars); 84 | ob_start(); 85 | if(CACHE_ON){ 86 | include($tpl_cache); 87 | }else{ 88 | include($tpl); 89 | } 90 | echo ob_get_clean(); 91 | } 92 | function ajaxNeedLogin(){ 93 | if(!$this->uid){ 94 | $callback=array( 95 | 'status'=>false, 96 | 'msg'=>'登录后才能执行此操作哟~' 97 | ); 98 | echo json_encode($callback); 99 | exit; 100 | } 101 | } 102 | function needLogin(){ 103 | if(!$this->uid){ 104 | header('location:'.$this->config['site']); 105 | exit; 106 | } 107 | } 108 | function err404(){ 109 | header("HTTP/1.1 404 Not Found"); 110 | require_once(APP_DIR . 'views/default/err404.php'); 111 | exit; 112 | } 113 | } 114 | ?> -------------------------------------------------------------------------------- /application/controllers/mainController.php: -------------------------------------------------------------------------------- 1 | model->getThreads('hot_thread',0,1); 9 | $thread=pagination($thread_list,1,0,$this->config['thread_per_page']); 10 | $this->set('thread_list',$thread['arr']); 11 | $next=$thread['next']!=''?url('main/threads',array('type'=>'hot','last_id'=>$thread['next'],'direction'=>1)):''; 12 | $this->set('prev',''); 13 | $this->set('next',$next); 14 | $this->set('title','热门帖子 | '.$this->config['shortname']); 15 | $this->set('navbar_cur','hot'); 16 | $this->render('index'); 17 | } 18 | public function threads($type,$last_id,$direction){ 19 | /*$thread_model=$this->model('thread'); 20 | $reply_model=$this->model('reply'); 21 | for($i=0;$i<3;$i++){ 22 | $rand_uid=rand(2,4); 23 | $u_name=array('abc','def','ghi'); 24 | $t=$thread_model->addThread(genstr(rand(15,30)),rand(1,7),genstr(rand(20,50)),$rand_uid,$u_name[$rand_uid]); 25 | if($t['status']){ 26 | $thread_id=$t['msg']; 27 | $reply_model->postReply($thread_id,genstr(rand(10,30)),rand(2,4)); 28 | } 29 | }*/ 30 | $type=x($type); 31 | $keys=array('hot'=>'热门','latest'=>'最新'); 32 | !array_key_exists($type,$keys)&&exit; 33 | $last_id=intval($last_id); 34 | $direction=intval($direction); 35 | $convert_keys=array('hot'=>'hot_thread','latest'=>'thread'); 36 | $thread_list=$this->model->getThreads($convert_keys[$type],$last_id,$direction); 37 | $thread=pagination($thread_list,$direction,$last_id,$this->config['thread_per_page']); 38 | $prev=$thread['prev']!=''?url('main/threads',array('type'=>$type,'last_id'=>$thread['prev'],'direction'=>0)):''; 39 | $next=$thread['next']!=''?url('main/threads',array('type'=>$type,'last_id'=>$thread['next'],'direction'=>1)):''; 40 | $this->set('thread_list',$thread['arr']); 41 | $this->set('prev',$prev); 42 | $this->set('next',$next); 43 | $this->set('breadcrumb',' ›
'.$keys[$type].'帖子
46 | 47 | 48 |'.$category.'
72 | 73 | 74 |$pt = PhpThumb::getInstance();
38 | *
39 | * It's that simple! Outside of that, there's no need to modify anything within this class, unless you're doing
40 | * some crazy customization... then knock yourself out! :)
41 | *
42 | * @package PhpThumb
43 | * @subpackage Core
44 | */
45 | class PhpThumb
46 | {
47 | /**
48 | * Instance of self
49 | *
50 | * @var object PhpThumb
51 | */
52 | protected static $_instance;
53 | /**
54 | * The plugin registry
55 | *
56 | * This is where all plugins to be loaded are stored. Data about the plugin is
57 | * provided, and currently consists of:
58 | * - loaded: true/false
59 | * - implementation: gd/imagick/both
60 | *
61 | * @var array
62 | */
63 | protected $_registry;
64 | /**
65 | * What implementations are available
66 | *
67 | * This stores what implementations are available based on the loaded
68 | * extensions in PHP, NOT whether or not the class files are present.
69 | *
70 | * @var array
71 | */
72 | protected $_implementations;
73 |
74 | /**
75 | * Returns an instance of self
76 | *
77 | * This is the usual singleton function that returns / instantiates the object
78 | *
79 | * @return PhpThumb
80 | */
81 | public static function getInstance ()
82 | {
83 | if(!(self::$_instance instanceof self))
84 | {
85 | self::$_instance = new self();
86 | }
87 |
88 | return self::$_instance;
89 | }
90 |
91 | /**
92 | * Class constructor
93 | *
94 | * Initializes all the variables, and does some preliminary validation / checking of stuff
95 | *
96 | */
97 | private function __construct ()
98 | {
99 | $this->_registry = array();
100 | $this->_implementations = array('gd' => false, 'imagick' => false);
101 |
102 | $this->getImplementations();
103 | }
104 |
105 | /**
106 | * Finds out what implementations are available
107 | *
108 | * This function loops over $this->_implementations and validates that the required extensions are loaded.
109 | *
110 | * I had planned on attempting to load them dynamically via dl(), but that would provide more overhead than I
111 | * was comfortable with (and would probably fail 99% of the time anyway)
112 | *
113 | */
114 | private function getImplementations ()
115 | {
116 | foreach($this->_implementations as $extension => $loaded)
117 | {
118 | if($loaded)
119 | {
120 | continue;
121 | }
122 |
123 | if(extension_loaded($extension))
124 | {
125 | $this->_implementations[$extension] = true;
126 | }
127 | }
128 | }
129 |
130 | /**
131 | * Returns whether or not $implementation is valid (available)
132 | *
133 | * If 'all' is passed, true is only returned if ALL implementations are available.
134 | *
135 | * You can also pass 'n/a', which always returns true
136 | *
137 | * @return bool
138 | * @param string $implementation
139 | */
140 | public function isValidImplementation ($implementation)
141 | {
142 | if ($implementation == 'n/a')
143 | {
144 | return true;
145 | }
146 |
147 | if ($implementation == 'all')
148 | {
149 | foreach ($this->_implementations as $imp => $value)
150 | {
151 | if ($value == false)
152 | {
153 | return false;
154 | }
155 | }
156 |
157 | return true;
158 | }
159 |
160 | if (array_key_exists($implementation, $this->_implementations))
161 | {
162 | return $this->_implementations[$implementation];
163 | }
164 |
165 | return false;
166 | }
167 |
168 | /**
169 | * Registers a plugin in the registry
170 | *
171 | * Adds a plugin to the registry if it isn't already loaded, and if the provided
172 | * implementation is valid. Note that you can pass the following special keywords
173 | * for implementation:
174 | * - all - Requires that all implementations be available
175 | * - n/a - Doesn't require any implementation
176 | *
177 | * When a plugin is added to the registry, it's added as a key on $this->_registry with the value
178 | * being an array containing the following keys:
179 | * - loaded - whether or not the plugin has been "loaded" into the core class
180 | * - implementation - what implementation this plugin is valid for
181 | *
182 | * @return bool
183 | * @param string $pluginName
184 | * @param string $implementation
185 | */
186 | public function registerPlugin ($pluginName, $implementation)
187 | {
188 | if (!array_key_exists($pluginName, $this->_registry) && $this->isValidImplementation($implementation))
189 | {
190 | $this->_registry[$pluginName] = array('loaded' => false, 'implementation' => $implementation);
191 | return true;
192 | }
193 |
194 | return false;
195 | }
196 |
197 | /**
198 | * Loads all the plugins in $pluginPath
199 | *
200 | * All this function does is include all files inside the $pluginPath directory. The plugins themselves
201 | * will not be added to the registry unless you've properly added the code to do so inside your plugin file.
202 | *
203 | * @param string $pluginPath
204 | */
205 | public function loadPlugins ($pluginPath)
206 | {
207 | // strip the trailing slash if present
208 | if (substr($pluginPath, strlen($pluginPath) - 1, 1) == '/')
209 | {
210 | $pluginPath = substr($pluginPath, 0, strlen($pluginPath) - 1);
211 | }
212 |
213 | if ($handle = opendir($pluginPath))
214 | {
215 | while (false !== ($file = readdir($handle)))
216 | {
217 | if ($file == '.' || $file == '..' || $file == '.svn')
218 | {
219 | continue;
220 | }
221 |
222 | include_once($pluginPath . '/' . $file);
223 | }
224 | }
225 | }
226 |
227 | /**
228 | * Returns the plugin registry for the supplied implementation
229 | *
230 | * @return array
231 | * @param string $implementation
232 | */
233 | public function getPluginRegistry ($implementation)
234 | {
235 | $returnArray = array();
236 |
237 | foreach ($this->_registry as $plugin => $meta)
238 | {
239 | if ($meta['implementation'] == 'n/a' || $meta['implementation'] == $implementation)
240 | {
241 | $returnArray[$plugin] = $meta;
242 | }
243 | }
244 |
245 | return $returnArray;
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/public/zepto.timeago.js:
--------------------------------------------------------------------------------
1 | (function (factory) {
2 | if (typeof define === 'function' && define.amd) {
3 | // AMD. Register as an anonymous module.
4 | define(['zepto'], factory);
5 | } else if (typeof module === 'object' && typeof module.exports === 'object') {
6 | factory(require('zepto'));
7 | } else {
8 | // Browser globals
9 | factory(Zepto);
10 | }
11 | }(function ($) {
12 | $.timeago = function(timestamp) {
13 | if (timestamp instanceof Date) {
14 | return inWords(timestamp);
15 | } else if (typeof timestamp === "string") {
16 | return inWords($.timeago.parse(timestamp));
17 | } else if (typeof timestamp === "number") {
18 | return inWords(new Date(timestamp));
19 | } else {
20 | return inWords($.timeago.datetime(timestamp));
21 | }
22 | };
23 | var $t = $.timeago;
24 |
25 | $.extend($.timeago, {
26 | settings: {
27 | refreshMillis: 60000,
28 | allowPast: true,
29 | allowFuture: false,
30 | localeTitle: false,
31 | cutoff: 0,
32 | strings: {
33 | prefixAgo: null,
34 | prefixFromNow: null,
35 | suffixAgo: "前",
36 | suffixFromNow: "当前",
37 | inPast: '过去',
38 | seconds: "%d秒",
39 | minute: "1分钟",
40 | minutes: "%d分钟",
41 | hour: "1小时前",
42 | hours: "%d小时",
43 | day: "1天",
44 | days: "%d天",
45 | month: "1个月",
46 | months: "%d个月",
47 | year: "1年",
48 | years: "%d年",
49 | wordSeparator: "",
50 | numbers: []
51 | }
52 | },
53 |
54 | inWords: function(distanceMillis) {
55 | if(!this.settings.allowPast && ! this.settings.allowFuture) {
56 | throw 'timeago allowPast and allowFuture settings can not both be set to false.';
57 | }
58 |
59 | var $l = this.settings.strings;
60 | var prefix = $l.prefixAgo;
61 | var suffix = $l.suffixAgo;
62 | if (this.settings.allowFuture) {
63 | if (distanceMillis < 0) {
64 | prefix = $l.prefixFromNow;
65 | suffix = $l.suffixFromNow;
66 | }
67 | }
68 |
69 | if(!this.settings.allowPast && distanceMillis >= 0) {
70 | return this.settings.strings.inPast;
71 | }
72 |
73 | var seconds = Math.abs(distanceMillis) / 1000;
74 | var minutes = seconds / 60;
75 | var hours = minutes / 60;
76 | var days = hours / 24;
77 | var years = days / 365;
78 |
79 | function substitute(stringOrFunction, number) {
80 | var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
81 | var value = ($l.numbers && $l.numbers[number]) || number;
82 | return string.replace(/%d/i, value);
83 | }
84 |
85 | var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
86 | seconds < 90 && substitute($l.minute, 1) ||
87 | minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
88 | minutes < 90 && substitute($l.hour, 1) ||
89 | hours < 24 && substitute($l.hours, Math.round(hours)) ||
90 | hours < 42 && substitute($l.day, 1) ||
91 | days < 30 && substitute($l.days, Math.round(days)) ||
92 | days < 45 && substitute($l.month, 1) ||
93 | days < 365 && substitute($l.months, Math.round(days / 30)) ||
94 | years < 1.5 && substitute($l.year, 1) ||
95 | substitute($l.years, Math.round(years));
96 |
97 | var separator = $l.wordSeparator || "";
98 | if ($l.wordSeparator === undefined) { separator = " "; }
99 | return $.trim([prefix, words, suffix].join(separator));
100 | },
101 |
102 | parse: function(iso8601) {
103 | var s = $.trim(iso8601);
104 | s = s.replace(/\.\d+/,""); // remove milliseconds
105 | s = s.replace(/-/,"/").replace(/-/,"/");
106 | s = s.replace(/T/," ").replace(/Z/," UTC");
107 | s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
108 | s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
109 | return new Date(s);
110 | },
111 | datetime: function(elem) {
112 | var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
113 | return $t.parse(iso8601);
114 | },
115 | isTime: function(elem) {
116 | // jQuery's `is()` doesn't play well with HTML5 in IE
117 | return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
118 | }
119 | });
120 |
121 | // functions that can be called via $(el).timeago('action')
122 | // init is default when no action is given
123 | // functions are called with context of a single element
124 | var functions = {
125 | init: function(){
126 | var refresh_el = $.proxy(refresh, this);
127 | refresh_el();
128 | var $s = $t.settings;
129 | if ($s.refreshMillis > 0) {
130 | this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
131 | }
132 | },
133 | update: function(time){
134 | var parsedTime = $t.parse(time);
135 | $(this).data('timeago', { datetime: parsedTime });
136 | if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleString());
137 | refresh.apply(this);
138 | },
139 | updateFromDOM: function(){
140 | $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
141 | refresh.apply(this);
142 | },
143 | dispose: function () {
144 | if (this._timeagoInterval) {
145 | window.clearInterval(this._timeagoInterval);
146 | this._timeagoInterval = null;
147 | }
148 | }
149 | };
150 |
151 | $.fn.timeago = function(action, options) {
152 | var fn = action ? functions[action] : functions.init;
153 | if(!fn){
154 | throw new Error("Unknown function name '"+ action +"' for timeago");
155 | }
156 | // each over objects here and call the requested function
157 | this.each(function(){
158 | fn.call(this, options);
159 | });
160 | return this;
161 | };
162 |
163 | function refresh() {
164 | //check if it's still visible
165 | if(!$.contains(document.documentElement,this)){
166 | //stop if it has been removed
167 | $(this).timeago("dispose");
168 | return this;
169 | }
170 |
171 | var data = prepareData(this);
172 | var $s = $t.settings;
173 |
174 | if (!isNaN(data.datetime)) {
175 | if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
176 | $(this).text(inWords(data.datetime));
177 | }
178 | }
179 | return this;
180 | }
181 |
182 | function prepareData(element) {
183 | element = $(element);
184 | if (!element.data("timeago")) {
185 | element.data("timeago", { datetime: $t.datetime(element) });
186 | var text = $.trim(element.text());
187 | if ($t.settings.localeTitle) {
188 | element.attr("title", element.data('timeago').datetime.toLocaleString());
189 | } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
190 | element.attr("title", text);
191 | }
192 | }
193 | return element.data("timeago");
194 | }
195 |
196 | function inWords(date) {
197 | return $t.inWords(distance(date));
198 | }
199 |
200 | function distance(date) {
201 | return (new Date().getTime() - date.getTime());
202 | }
203 |
204 | // fix for IE6 suckage
205 | document.createElement("abbr");
206 | document.createElement("time");
207 | }));
--------------------------------------------------------------------------------
/public/cropbox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by ezgoing on 14/9/2014.
3 | */
4 |
5 | "use strict";
6 | (function (factory) {
7 | if (typeof define === 'function' && define.amd) {
8 | define(['jquery'], factory);
9 | } else {
10 | factory(jQuery);
11 | }
12 | }(function ($) {
13 | var cropbox = function(options, el){
14 | var el = el || $(options.imageBox),
15 | obj =
16 | {
17 | state : {},
18 | ratio : 1,
19 | options : options,
20 | imageBox : el,
21 | thumbBox : el.find(options.thumbBox),
22 | spinner : el.find(options.spinner),
23 | image : new Image(),
24 | getDataURL: function ()
25 | {
26 | var width = this.thumbBox.width(),
27 | height = this.thumbBox.height(),
28 | canvas = document.createElement("canvas"),
29 | dim = el.css('background-position').split(' '),
30 | size = el.css('background-size').split(' '),
31 | dx = parseInt(dim[0]) - el.width()/2 + width/2,
32 | dy = parseInt(dim[1]) - el.height()/2 + height/2,
33 | dw = parseInt(size[0]),
34 | dh = parseInt(size[1]),
35 | sh = parseInt(this.image.height),
36 | sw = parseInt(this.image.width);
37 |
38 | canvas.width = width;
39 | canvas.height = height;
40 | var context = canvas.getContext("2d");
41 | context.drawImage(this.image, 0, 0, sw, sh, dx, dy, dw, dh);
42 | var imageData = canvas.toDataURL('image/png');
43 | return imageData;
44 | },
45 | getBlob: function()
46 | {
47 | var imageData = this.getDataURL();
48 | var b64 = imageData.replace('data:image/png;base64,','');
49 | var binary = atob(b64);
50 | var array = [];
51 | for (var i = 0; i < binary.length; i++) {
52 | array.push(binary.charCodeAt(i));
53 | }
54 | return new Blob([new Uint8Array(array)], {type: 'image/png'});
55 | },
56 | zoomIn: function ()
57 | {
58 | this.ratio*=1.1;
59 | setBackground();
60 | },
61 | zoomOut: function ()
62 | {
63 | this.ratio*=0.9;
64 | setBackground();
65 | }
66 | },
67 | setBackground = function()
68 | {
69 | var w = parseInt(obj.image.width)*obj.ratio;
70 | var h = parseInt(obj.image.height)*obj.ratio;
71 |
72 | var pw = (el.width() - w) / 2;
73 | var ph = (el.height() - h) / 2;
74 |
75 | el.css({
76 | 'background-image': 'url(' + obj.image.src + ')',
77 | 'background-size': w +'px ' + h + 'px',
78 | 'background-position': pw + 'px ' + ph + 'px',
79 | 'background-repeat': 'no-repeat'});
80 | },
81 | imgMouseDown = function(e)
82 | {
83 | e.stopImmediatePropagation();
84 |
85 | obj.state.dragable = true;
86 | obj.state.mouseX = e.clientX;
87 | obj.state.mouseY = e.clientY;
88 | },
89 | imgMouseMove = function(e)
90 | {
91 | e.stopImmediatePropagation();
92 |
93 | if (obj.state.dragable)
94 | {
95 | var x = e.clientX - obj.state.mouseX;
96 | var y = e.clientY - obj.state.mouseY;
97 |
98 | var bg = el.css('background-position').split(' ');
99 |
100 | var bgX = x + parseInt(bg[0]);
101 | var bgY = y + parseInt(bg[1]);
102 |
103 | el.css('background-position', bgX +'px ' + bgY + 'px');
104 |
105 | obj.state.mouseX = e.clientX;
106 | obj.state.mouseY = e.clientY;
107 | }
108 | },
109 | imgMouseUp = function(e)
110 | {
111 | e.stopImmediatePropagation();
112 | obj.state.dragable = false;
113 | },
114 | zoomImage = function(e)
115 | {
116 | e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0 ? obj.ratio*=1.1 : obj.ratio*=0.9;
117 | setBackground();
118 | }
119 |
120 | obj.spinner.show();
121 | obj.image.onload = function() {
122 | obj.spinner.hide();
123 | setBackground();
124 |
125 | el.bind('mousedown', imgMouseDown);
126 | el.bind('mousemove', imgMouseMove);
127 | $(window).bind('mouseup', imgMouseUp);
128 | el.bind('mousewheel DOMMouseScroll', zoomImage);
129 | };
130 | obj.image.src = options.imgSrc;
131 | el.on('remove', function(){$(window).unbind('mouseup', imgMouseUp)});
132 |
133 | return obj;
134 | };
135 |
136 | jQuery.fn.cropbox = function(options){
137 | return new cropbox(options, this);
138 | };
139 | }));
140 | $(window).load(function() {
141 | var options =
142 | {
143 | thumbBox: '.thumbBox',
144 | spinner: '.spinner',
145 | imgSrc: ''
146 | }
147 | var cropper;
148 | $('#html5_upload').on('change', function(){
149 | var reader = new FileReader();
150 | $('#avatar_pre').hide();
151 | $('#avatar_select').removeClass('none');
152 | reader.onload = function(e) {
153 | options.imgSrc = e.target.result;
154 | cropper = $('.imageBox').cropbox(options);
155 | }
156 | reader.readAsDataURL(this.files[0]);
157 | this.files = [];
158 | })
159 | $('#crop_btn').click(function(){
160 | var _t=$(this);
161 | if(_t.hasClass('disabled')){
162 | return false;
163 | }
164 | _t.addClass('disabled');
165 | ZENG.msgbox.show('正在上传头像,请等待...', 6, 4800000);
166 | $.post(U('profile/doAvatar'),{content:cropper.getDataURL()},function(d){
167 | if(d.status){
168 | ZENG.msgbox.show('图片上传成功', 4, 2000);
169 | delCookie(cookie_prefix+'avatar_big','/');
170 | delCookie(cookie_prefix+'avatar_small','/');
171 | setTimeout("location.reload()",2000);
172 | }else{
173 | ZENG.msgbox.show(d.msg,3, 2000);
174 | }
175 | _t.removeClass('disabled');
176 | },'json');
177 | });
178 | //detectFlash()?$('#avatar_preupload').remove():$('#flash_container').remove();
179 | });
180 | function detectFlash() {
181 | if(navigator.mimeTypes.length>0){
182 | var flashAct = navigator.mimeTypes["application/x-shockwave-flash"];
183 | return flashAct != null ? flashAct.enabledPlugin!=null : false;
184 | } else if(self.ActiveXObject) {
185 | try {
186 | new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
187 | return true;
188 | } catch (oError) {
189 | return false;
190 | }
191 | }
192 | }
--------------------------------------------------------------------------------
/public/css/icon.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'icomoon';
3 | src:url('../fonts/icomoon.eot?npzd73');
4 | src:url('../fonts/icomoon.eot?#iefixnpzd73') format('embedded-opentype'),
5 | url('../fonts/icomoon.woff?npzd73') format('woff'),
6 | url('../fonts/icomoon.ttf?npzd73') format('truetype'),
7 | url('../fonts/icomoon.svg?npzd73#icomoon') format('svg');
8 | font-weight: normal;
9 | font-style: normal;
10 | }
11 |
12 | [class^="icon-"], [class*=" icon-"] {
13 | font-family: 'icomoon';
14 | speak: none;
15 | font-style: normal;
16 | font-weight: normal;
17 | font-variant: normal;
18 | text-transform: none;
19 | line-height: 1;
20 |
21 | /* Better Font Rendering =========== */
22 | -webkit-font-smoothing: antialiased;
23 | -moz-osx-font-smoothing: grayscale;
24 | }
25 |
26 | .icon-activity:before {
27 | content: "\e66b";
28 | }
29 |
30 | .icon-wegene:before {
31 | content: "\e667";
32 | }
33 |
34 | .icon-contact:before {
35 | content: "\e668";
36 | }
37 |
38 | .icon-about:before {
39 | content: "\e669";
40 | }
41 |
42 | .icon-protect:before {
43 | content: "\e66a";
44 | }
45 |
46 | .icon-drug:before {
47 | content: "\e666";
48 | }
49 |
50 | .icon-hear:before {
51 | content: "\e663";
52 | }
53 |
54 | .icon-ear:before {
55 | content: "\e664";
56 | }
57 |
58 | .icon-beat:before {
59 | content: "\e665";
60 | }
61 |
62 | .icon-23:before {
63 | content: "\e662";
64 | }
65 |
66 | .icon-good:before {
67 | content: "\e660";
68 | }
69 |
70 | .icon-bad:before {
71 | content: "\e661";
72 | }
73 |
74 | .icon-format:before {
75 | content: "\e65f";
76 | }
77 |
78 | .icon-strike:before {
79 | content: "\e65e";
80 | }
81 |
82 | .icon-full:before {
83 | content: "\e65d";
84 | }
85 |
86 | .icon-gene:before {
87 | content: "\e600";
88 | }
89 |
90 | .icon-count:before {
91 | content: "\e65a";
92 | }
93 |
94 | .icon-order:before {
95 | content: "\e65b";
96 | }
97 |
98 | .icon-google:before {
99 | content: "\e657";
100 | }
101 |
102 | .icon-facebook:before {
103 | content: "\e658";
104 | }
105 |
106 | .icon-twitter:before {
107 | content: "\e659";
108 | }
109 |
110 | .icon-cart:before {
111 | content: "\e656";
112 | }
113 |
114 | .icon-bulb:before {
115 | content: "\e655";
116 | }
117 |
118 | .icon-download:before {
119 | content: "\e654";
120 | }
121 |
122 | .icon-home:before {
123 | content: "\e601";
124 | }
125 |
126 | .icon-bar:before {
127 | content: "\e653";
128 | }
129 |
130 | .icon-right:before {
131 | content: "\e651";
132 | }
133 |
134 | .icon-left:before {
135 | content: "\e652";
136 | }
137 |
138 | .icon-unlock:before {
139 | content: "\e650";
140 | }
141 |
142 | .icon-verify:before {
143 | content: "\e64a";
144 | }
145 |
146 | .icon-date:before {
147 | content: "\e64b";
148 | }
149 |
150 | .icon-log:before {
151 | content: "\e64c";
152 | }
153 |
154 | .icon-forbid:before {
155 | content: "\e64d";
156 | }
157 |
158 | .icon-transfer:before {
159 | content: "\e64e";
160 | }
161 |
162 | .icon-reader:before {
163 | content: "\e64f";
164 | }
165 |
166 | .icon-phone:before {
167 | content: "\e648";
168 | }
169 |
170 | .icon-file:before {
171 | content: "\e649";
172 | }
173 |
174 | .icon-ol:before {
175 | content: "\e63e";
176 | }
177 |
178 | .icon-undo:before {
179 | content: "\e646";
180 | }
181 |
182 | .icon-redo:before {
183 | content: "\e647";
184 | }
185 |
186 | .icon-bold:before {
187 | content: "\e63a";
188 | }
189 |
190 | .icon-italic:before {
191 | content: "\e63b";
192 | }
193 |
194 | .icon-underline:before {
195 | content: "\e63c";
196 | }
197 |
198 | .icon-ul:before {
199 | content: "\e63d";
200 | }
201 |
202 | .icon-image:before {
203 | content: "\e63f";
204 | }
205 |
206 | .icon-video:before {
207 | content: "\e640";
208 | }
209 |
210 | .icon-quote:before {
211 | content: "\e641";
212 | }
213 |
214 | .icon-code:before {
215 | content: "\e642";
216 | }
217 |
218 | .icon-preview:before {
219 | content: "\e643";
220 | }
221 |
222 | .icon-help:before {
223 | content: "\e644";
224 | }
225 |
226 | .icon-h:before {
227 | content: "\e645";
228 | }
229 |
230 | .icon-prestige:before {
231 | content: "\e638";
232 | }
233 |
234 | .icon-v:before {
235 | content: "\e639";
236 | }
237 |
238 | .icon-score:before {
239 | content: "\e637";
240 | }
241 |
242 | .icon-plus:before {
243 | content: "\e635";
244 | }
245 |
246 | .icon-followed:before {
247 | content: "\e636";
248 | }
249 |
250 | .icon-mytopic:before {
251 | content: "\e633";
252 | }
253 |
254 | .icon-up:before {
255 | content: "\e634";
256 | }
257 |
258 | .icon-trash:before {
259 | content: "\e632";
260 | }
261 |
262 | .icon-fold:before {
263 | content: "\e62c";
264 | }
265 |
266 | .icon-thank:before {
267 | content: "\e62d";
268 | }
269 |
270 | .icon-report:before {
271 | content: "\e62e";
272 | }
273 |
274 | .icon-qzone:before {
275 | content: "\e62f";
276 | }
277 |
278 | .icon-at:before {
279 | content: "\e630";
280 | }
281 |
282 | .icon-attach:before {
283 | content: "\e631";
284 | }
285 |
286 | .icon-bell:before {
287 | content: "\e62b";
288 | }
289 |
290 | .icon-triangle:before {
291 | content: "\e62a";
292 | }
293 |
294 | .icon-wechat:before {
295 | content: "\e628";
296 | }
297 |
298 | .icon-lock:before {
299 | content: "\e629";
300 | }
301 |
302 | .icon-i:before {
303 | content: "\e626";
304 | }
305 |
306 | .icon-bubble:before {
307 | content: "\e627";
308 | }
309 |
310 | .icon-flag:before {
311 | content: "\e625";
312 | }
313 |
314 | .icon-txweibo:before {
315 | content: "\e622";
316 | }
317 |
318 | .icon-bestbg:before {
319 | content: "\e623";
320 | }
321 |
322 | .icon-best:before {
323 | content: "\e624";
324 | }
325 |
326 | .icon-job:before {
327 | content: "\e61e";
328 | }
329 |
330 | .icon-favor:before {
331 | content: "\e620";
332 | }
333 |
334 | .icon-down:before {
335 | content: "\e621";
336 | }
337 |
338 | .icon-location:before {
339 | content: "\e61d";
340 | }
341 |
342 | .icon-bind:before {
343 | content: "\e61f";
344 | }
345 |
346 | .icon-weibo:before {
347 | content: "\e618";
348 | }
349 |
350 | .icon-qq:before {
351 | content: "\e619";
352 | }
353 |
354 | .icon-signup:before {
355 | content: "\e61a";
356 | }
357 |
358 | .icon-users:before {
359 | content: "\e61b";
360 | }
361 |
362 | .icon-topic:before {
363 | content: "\e61c";
364 | }
365 |
366 | .icon-login:before {
367 | content: "\e617";
368 | }
369 |
370 | .icon-logout:before {
371 | content: "\e616";
372 | }
373 |
374 | .icon-insert:before {
375 | content: "\e612";
376 | }
377 |
378 | .icon-setting:before {
379 | content: "\e615";
380 | }
381 |
382 | .icon-inbox:before {
383 | content: "\e614";
384 | }
385 |
386 | .icon-pic:before {
387 | content: "\e610";
388 | }
389 |
390 | .icon-user:before {
391 | content: "\e613";
392 | }
393 |
394 | .icon-delete:before {
395 | content: "\e611";
396 | }
397 |
398 | .icon-comment:before {
399 | content: "\e60f";
400 | }
401 |
402 | .icon-share:before {
403 | content: "\e60e";
404 | }
405 |
406 | .icon-loading:before {
407 | content: "\e60d";
408 | }
409 |
410 | .icon-inviteask:before {
411 | content: "\e60c";
412 | }
413 |
414 | .icon-list:before {
415 | content: "\e602";
416 | }
417 |
418 | .icon-ask:before {
419 | content: "\e603";
420 | }
421 |
422 | .icon-search:before {
423 | content: "\e604";
424 | }
425 |
426 | .icon-more:before {
427 | content: "\e605";
428 | }
429 |
430 | .icon-agree:before {
431 | content: "\e606";
432 | }
433 |
434 | .icon-disagree:before {
435 | content: "\e607";
436 | }
437 |
438 | .icon-reply:before {
439 | content: "\e608";
440 | }
441 |
442 | .icon-draft:before {
443 | content: "\e609";
444 | }
445 |
446 | .icon-check:before {
447 | content: "\e60a";
448 | }
449 |
450 | .icon-invite:before {
451 | content: "\e60b";
452 | }
453 |
454 | .icon-edit:before {
455 | content: "\e65c";
456 | }
457 |
458 |
--------------------------------------------------------------------------------
/public/jquery.timeago.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Timeago is a jQuery plugin that makes it easy to support automatically
3 | * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
4 | *
5 | * @name timeago
6 | * @version 1.4.3
7 | * @requires jQuery v1.2.3+
8 | * @author Ryan McGeary
9 | * @license MIT License - http://www.opensource.org/licenses/mit-license.php
10 | *
11 | * For usage and examples, visit:
12 | * http://timeago.yarp.com/
13 | *
14 | * Copyright (c) 2008-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
15 | */
16 |
17 | (function (factory) {
18 | if (typeof define === 'function' && define.amd) {
19 | // AMD. Register as an anonymous module.
20 | define(['jquery'], factory);
21 | } else if (typeof module === 'object' && typeof module.exports === 'object') {
22 | factory(require('jquery'));
23 | } else {
24 | // Browser globals
25 | factory(jQuery);
26 | }
27 | }(function ($) {
28 | $.timeago = function(timestamp) {
29 | if (timestamp instanceof Date) {
30 | return inWords(timestamp);
31 | } else if (typeof timestamp === "string") {
32 | return inWords($.timeago.parse(timestamp));
33 | } else if (typeof timestamp === "number") {
34 | return inWords(new Date(timestamp));
35 | } else {
36 | return inWords($.timeago.datetime(timestamp));
37 | }
38 | };
39 | var $t = $.timeago;
40 |
41 | $.extend($.timeago, {
42 | settings: {
43 | refreshMillis: 60000,
44 | allowPast: true,
45 | allowFuture: false,
46 | localeTitle: false,
47 | cutoff: 0,
48 | strings: {
49 | prefixAgo: null,
50 | prefixFromNow: null,
51 | suffixAgo: "前",
52 | suffixFromNow: "当前",
53 | inPast: '过去',
54 | seconds: "%d秒",
55 | minute: "1分钟",
56 | minutes: "%d分钟",
57 | hour: "1小时前",
58 | hours: "%d小时",
59 | day: "1天",
60 | days: "%d天",
61 | month: "1个月",
62 | months: "%d个月",
63 | year: "1年",
64 | years: "%d年",
65 | wordSeparator: "",
66 | numbers: []
67 | }
68 | },
69 |
70 | inWords: function(distanceMillis) {
71 | if(!this.settings.allowPast && ! this.settings.allowFuture) {
72 | throw 'timeago allowPast and allowFuture settings can not both be set to false.';
73 | }
74 |
75 | var $l = this.settings.strings;
76 | var prefix = $l.prefixAgo;
77 | var suffix = $l.suffixAgo;
78 | if (this.settings.allowFuture) {
79 | if (distanceMillis < 0) {
80 | prefix = $l.prefixFromNow;
81 | suffix = $l.suffixFromNow;
82 | }
83 | }
84 |
85 | if(!this.settings.allowPast && distanceMillis >= 0) {
86 | return this.settings.strings.inPast;
87 | }
88 |
89 | var seconds = Math.abs(distanceMillis) / 1000;
90 | var minutes = seconds / 60;
91 | var hours = minutes / 60;
92 | var days = hours / 24;
93 | var years = days / 365;
94 |
95 | function substitute(stringOrFunction, number) {
96 | var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
97 | var value = ($l.numbers && $l.numbers[number]) || number;
98 | return string.replace(/%d/i, value);
99 | }
100 |
101 | var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
102 | seconds < 90 && substitute($l.minute, 1) ||
103 | minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
104 | minutes < 90 && substitute($l.hour, 1) ||
105 | hours < 24 && substitute($l.hours, Math.round(hours)) ||
106 | hours < 42 && substitute($l.day, 1) ||
107 | days < 30 && substitute($l.days, Math.round(days)) ||
108 | days < 45 && substitute($l.month, 1) ||
109 | days < 365 && substitute($l.months, Math.round(days / 30)) ||
110 | years < 1.5 && substitute($l.year, 1) ||
111 | substitute($l.years, Math.round(years));
112 |
113 | var separator = $l.wordSeparator || "";
114 | if ($l.wordSeparator === undefined) { separator = " "; }
115 | return $.trim([prefix, words, suffix].join(separator));
116 | },
117 |
118 | parse: function(iso8601) {
119 | var s = $.trim(iso8601);
120 | s = s.replace(/\.\d+/,""); // remove milliseconds
121 | s = s.replace(/-/,"/").replace(/-/,"/");
122 | s = s.replace(/T/," ").replace(/Z/," UTC");
123 | s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
124 | s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
125 | return new Date(s);
126 | },
127 | datetime: function(elem) {
128 | var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
129 | return $t.parse(iso8601);
130 | },
131 | isTime: function(elem) {
132 | // jQuery's `is()` doesn't play well with HTML5 in IE
133 | return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
134 | }
135 | });
136 |
137 | // functions that can be called via $(el).timeago('action')
138 | // init is default when no action is given
139 | // functions are called with context of a single element
140 | var functions = {
141 | init: function(){
142 | var refresh_el = $.proxy(refresh, this);
143 | refresh_el();
144 | var $s = $t.settings;
145 | if ($s.refreshMillis > 0) {
146 | this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
147 | }
148 | },
149 | update: function(time){
150 | var parsedTime = $t.parse(time);
151 | $(this).data('timeago', { datetime: parsedTime });
152 | if($t.settings.localeTitle) $(this).attr("title", parsedTime.toLocaleString());
153 | refresh.apply(this);
154 | },
155 | updateFromDOM: function(){
156 | $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
157 | refresh.apply(this);
158 | },
159 | dispose: function () {
160 | if (this._timeagoInterval) {
161 | window.clearInterval(this._timeagoInterval);
162 | this._timeagoInterval = null;
163 | }
164 | }
165 | };
166 |
167 | $.fn.timeago = function(action, options) {
168 | var fn = action ? functions[action] : functions.init;
169 | if(!fn){
170 | throw new Error("Unknown function name '"+ action +"' for timeago");
171 | }
172 | // each over objects here and call the requested function
173 | this.each(function(){
174 | fn.call(this, options);
175 | });
176 | return this;
177 | };
178 |
179 | function refresh() {
180 | //check if it's still visible
181 | if(!$.contains(document.documentElement,this)){
182 | //stop if it has been removed
183 | $(this).timeago("dispose");
184 | return this;
185 | }
186 |
187 | var data = prepareData(this);
188 | var $s = $t.settings;
189 |
190 | if (!isNaN(data.datetime)) {
191 | if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
192 | $(this).text(inWords(data.datetime));
193 | }
194 | }
195 | return this;
196 | }
197 |
198 | function prepareData(element) {
199 | element = $(element);
200 | if (!element.data("timeago")) {
201 | element.data("timeago", { datetime: $t.datetime(element) });
202 | var text = $.trim(element.text());
203 | if ($t.settings.localeTitle) {
204 | element.attr("title", element.data('timeago').datetime.toLocaleString());
205 | } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
206 | element.attr("title", text);
207 | }
208 | }
209 | return element.data("timeago");
210 | }
211 |
212 | function inWords(date) {
213 | return $t.inWords(distance(date));
214 | }
215 |
216 | function distance(date) {
217 | return (new Date().getTime() - date.getTime());
218 | }
219 |
220 | // fix for IE6 suckage
221 | document.createElement("abbr");
222 | document.createElement("time");
223 | }));
--------------------------------------------------------------------------------
/public/jquery.devbridge-autocomplete.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Ajax Autocomplete for jQuery, version 1.2.7
3 | * (c) 2013 Tomas Kirda
4 | *
5 | * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
6 | * For details, see the web site: http://www.devbridge.com/projects/autocomplete/jquery/
7 | *
8 | */
9 | (function(e){"function"===typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function g(a,b){var c=function(){},c={autoSelectFirst:!1,appendTo:"body",serviceUrl:null,lookup:null,onSelect:null,width:"auto",minChars:1,maxHeight:300,deferRequestBy:0,params:{},formatResult:g.formatResult,delimiter:null,zIndex:9999,type:"GET",noCache:!1,onSearchStart:c,onSearchComplete:c,containerClass:"autocomplete-suggestions",tabDisabled:!1,dataType:"text",lookupFilter:function(a,b,c){return-1!==
10 | a.value.toLowerCase().indexOf(c)},paramName:"query",transformResult:function(a){return"string"===typeof a?e.parseJSON(a):a}};this.element=a;this.el=e(a);this.suggestions=[];this.badQueries=[];this.selectedIndex=-1;this.currentValue=this.element.value;this.intervalId=0;this.cachedResponse=[];this.onChange=this.onChangeInterval=null;this.isLocal=this.ignoreValueChange=!1;this.suggestionsContainer=null;this.options=e.extend({},c,b);this.classes={selected:"autocomplete-selected",suggestion:"autocomplete-suggestion"};
11 | this.initialize();this.setOptions(b)}var h={extend:function(a,b){return e.extend(a,b)},createNode:function(a){var b=document.createElement("div");b.innerHTML=a;return b.firstChild}};g.utils=h;e.Autocomplete=g;g.formatResult=function(a,b){var c="("+b.replace(RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\)","g"),"\\$1")+")";return a.value.replace(RegExp(c,"gi"),"$1")};g.prototype={killerFn:null,initialize:function(){var a=this,b="."+a.classes.suggestion,c=a.classes.selected,
12 | d=a.options,f;a.element.setAttribute("autocomplete","off");a.killerFn=function(b){0===e(b.target).closest("."+a.options.containerClass).length&&(a.killSuggestions(),a.disableKillerFn())};if(!d.width||"auto"===d.width)d.width=a.el.outerWidth();a.suggestionsContainer=g.utils.createNode('');f=e(a.suggestionsContainer);f.appendTo(d.appendTo).width(d.width);f.on("mouseover.autocomplete",b,function(){a.activate(e(this).data("index"))});
13 | f.on("mouseout.autocomplete",function(){a.selectedIndex=-1;f.children("."+c).removeClass(c)});f.on("click.autocomplete",b,function(){a.select(e(this).data("index"),!1)});a.fixPosition();if(window.opera)a.el.on("keypress.autocomplete",function(b){a.onKeyPress(b)});else a.el.on("keydown.autocomplete",function(b){a.onKeyPress(b)});a.el.on("keyup.autocomplete",function(b){a.onKeyUp(b)});a.el.on("blur.autocomplete",function(){a.onBlur()});a.el.on("focus.autocomplete",function(){a.fixPosition()})},onBlur:function(){this.enableKillerFn()},
14 | setOptions:function(a){var b=this.options;h.extend(b,a);if(this.isLocal=e.isArray(b.lookup))b.lookup=this.verifySuggestionsFormat(b.lookup);e(this.suggestionsContainer).css({"max-height":b.maxHeight+"px",width:b.width+"px","z-index":b.zIndex})},clearCache:function(){this.cachedResponse=[];this.badQueries=[]},clear:function(){this.clearCache();this.currentValue=null;this.suggestions=[]},disable:function(){this.disabled=!0},enable:function(){this.disabled=!1},fixPosition:function(){var a;"body"===this.options.appendTo&&
15 | (a=this.el.offset(),e(this.suggestionsContainer).css({top:a.top+this.el.outerHeight()+"px",left:a.left+"px"}))},enableKillerFn:function(){e(document).on("click.autocomplete",this.killerFn)},disableKillerFn:function(){e(document).off("click.autocomplete",this.killerFn)},killSuggestions:function(){var a=this;a.stopKillSuggestions();a.intervalId=window.setInterval(function(){a.hide();a.stopKillSuggestions()},300)},stopKillSuggestions:function(){window.clearInterval(this.intervalId)},onKeyPress:function(a){if(!this.disabled&&
16 | !this.visible&&40===a.keyCode&&this.currentValue)this.suggest();else if(!this.disabled&&this.visible){switch(a.keyCode){case 27:this.el.val(this.currentValue);this.hide();break;case 9:case 13:if(-1===this.selectedIndex){this.hide();return}this.select(this.selectedIndex,13===a.keyCode);if(9===a.keyCode&&!1===this.options.tabDisabled)return;break;case 38:this.moveUp();break;case 40:this.moveDown();break;default:return}a.stopImmediatePropagation();a.preventDefault()}},onKeyUp:function(a){var b=this;
17 | if(!b.disabled){switch(a.keyCode){case 38:case 40:return}clearInterval(b.onChangeInterval);if(b.currentValue!==b.el.val())if(0" 180 | . "Name: $name
" 181 | . "Email: $email
" 182 | . "URL: $URL
" 183 | . "Message:
" 184 | . "
$message" 185 | . "
IP Address: {$_SERVER['REMOTE_ADDR']}
" 186 | . "Time: $time
" 187 | . "Browser: {$_SERVER['HTTP_USER_AGENT']}
"; 188 | 189 | if (isset($GLOBALS['DEBUG_MODE']) && $GLOBALS['DEBUG_MODE'] == false) { 190 | // send the message with mail() 191 | mail($GLOBALS['ct_recipient'], $GLOBALS['ct_msg_subject'], $message, "From: {$GLOBALS['ct_recipient']}\r\nReply-To: {$email}\r\nContent-type: text/html; charset=ISO-8859-1\r\nMIME-Version: 1.0"); 192 | } 193 | 194 | $return = array('error' => 0, 'message' => 'OK'); 195 | die(json_encode($return)); 196 | } else { 197 | $errmsg = ''; 198 | foreach($errors as $key => $error) { 199 | // set up error messages to display with each field 200 | $errmsg .= " - {$error}\n"; 201 | } 202 | 203 | $return = array('error' => 1, 'message' => $errmsg); 204 | die(json_encode($return)); 205 | } 206 | } // POST 207 | } // function process_si_contact_form() 208 | -------------------------------------------------------------------------------- /application/securimage/README.txt: -------------------------------------------------------------------------------- 1 | NAME: 2 | 3 | Securimage - A PHP class for creating captcha images and audio with many options. 4 | 5 | VERSION: 6 | 7 | 3.5.2 8 | 9 | AUTHOR: 10 | 11 | Drew Phillips
'; 160 | } 161 | } 162 | 163 | if (sizeof($errors) == 0) { 164 | // no errors, send the form 165 | $time = date('r'); 166 | $message = "A message was submitted from the contact form. The following information was provided.
" 167 | . "Name: $name
" 168 | . "Email: $email
" 169 | . "URL: $URL
" 170 | . "Message:
" 171 | . "
$message" 172 | . "
IP Address: {$_SERVER['REMOTE_ADDR']}
" 173 | . "Time: $time
" 174 | . "Browser: {$_SERVER['HTTP_USER_AGENT']}
"; 175 | 176 | $message = wordwrap($message, 70); 177 | 178 | if (isset($GLOBALS['DEBUG_MODE']) && $GLOBALS['DEBUG_MODE'] == false) { 179 | // send the message with mail() 180 | mail($GLOBALS['ct_recipient'], $GLOBALS['ct_msg_subject'], $message, "From: {$GLOBALS['ct_recipient']}\r\nReply-To: {$email}\r\nContent-type: text/html; charset=UTF-8\r\nMIME-Version: 1.0"); 181 | } 182 | 183 | $_SESSION['ctform']['timetosolve'] = $securimage->getTimeToSolve(); 184 | $_SESSION['ctform']['error'] = false; // no error with form 185 | $_SESSION['ctform']['success'] = true; // message sent 186 | } else { 187 | // save the entries, this is to re-populate the form 188 | $_SESSION['ctform']['ct_name'] = $name; // save name from the form submission 189 | $_SESSION['ctform']['ct_email'] = $email; // save email 190 | $_SESSION['ctform']['ct_URL'] = $URL; // save URL 191 | $_SESSION['ctform']['ct_message'] = $message; // save message 192 | 193 | foreach($errors as $key => $error) { 194 | // set up error messages to display with each field 195 | $_SESSION['ctform'][$key] = "$error"; 196 | } 197 | 198 | $_SESSION['ctform']['error'] = true; // set error floag 199 | } 200 | } // POST 201 | } 202 | 203 | $_SESSION['ctform']['success'] = false; // clear success value after running 204 | -------------------------------------------------------------------------------- /application/controllers/userController.php: -------------------------------------------------------------------------------- 1 | model->searchUser($words); 10 | $callback=array(); 11 | foreach($result as $k=>$v){ 12 | $callback[$k]['value']=$v['username']; 13 | $callback[$k]['data']=$v['uid']; 14 | } 15 | echo json_encode(array('suggestions'=>$callback)); 16 | } 17 | } 18 | public function avatarHover(){ 19 | $uid=intval($_POST['uid']); 20 | $callback=array( 21 | 'status'=>false 22 | ); 23 | if($uid>0){ 24 | $user_info=$this->model->getUserInfoByUid($uid); 25 | $user['profile_url']=url('user/index',array('uid'=>$user_info['uid'])); 26 | $user['big_avatar']=get_avatar($user_info['big_avatar'],'big'); 27 | $user['username']=$user_info['username']; 28 | $user['ban_send_msg']=$user_info['ban_send_msg']; 29 | $user['thread_num']=$user_info['thread_num']; 30 | $user['reply_num']=$user_info['reply_num']; 31 | $user['collect_num']=$user_info['collect_num']; 32 | $user['intro']=$user_info['intro']==''?'该用户暂无自我介绍':$user_info['intro']; 33 | $user['thread_url']=url('user/thread',array('uid'=>$uid,'last_id'=>0,'direction'=>1)); 34 | $user['collect_url']=url('user/collect',array('uid'=>$uid,'last_id'=>0,'direction'=>1)); 35 | $user['reply_url']=url('user/reply',array('uid'=>$uid,'last_id'=>0,'direction'=>1)); 36 | if($this->uid){ 37 | $vote_interaction=$this->model->getUserVoteInteraction($this->uid,$uid); 38 | $reply_interaction=$this->model->getUserReplyInteraction($this->uid,$uid); 39 | $user['vote_interaction']=$vote_interaction==null?0:$vote_interaction; 40 | $user['reply_interaction']=$reply_interaction==null?0:$reply_interaction; 41 | $user['send_msg_url']=url('profile/msg'); 42 | } 43 | $callback=array( 44 | 'status'=>true, 45 | 'user_info'=>$user 46 | ); 47 | } 48 | echo json_encode($callback); 49 | } 50 | public function index($uid){ 51 | $uid=intval($uid); 52 | if($uid>0){ 53 | $user_info=$this->model->getUserInfoByUid($uid); 54 | $this->set('user_info',$user_info); 55 | if($this->uid&&$this->uid!=$uid){ 56 | $vote_interaction=$this->model->getUserVoteInteraction($this->uid,$uid)==null; 57 | $reply_interaction=$this->model->getUserReplyInteraction($this->uid,$uid); 58 | $this->set('vote_interaction',$vote_interaction==null?0:$vote_interaction); 59 | $this->set('reply_interaction',$reply_interaction==null?0:$vote_interaction); 60 | } 61 | $this->set('user_cur','info'); 62 | $this->set('breadcrumb',' ›
33 |