diff --git a/.gitignore b/.gitignore index 788a1bd7..67b09557 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ /cronjobs/logs/*.txt /cronjobs/logs/*.txt.*.gz /public/templates/cache/*.php +# Test configs +public/include/config/global.inc.scrypt.php +public/include/config/global.inc.sha.php diff --git a/README.md b/README.md index 51abc689..d61073dd 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ MPOS is a web based Mining Portal for various crypto currencies. A few pools usi * http://ltc.pool.grewe.ca * http://fst.pool.grewe.ca -* http://nvc.pool.grewe.ca +* http://ftc.pool.grewe.ca **NOTE**: This project is still under development and commits are happening on a daily basis. I do not recommend using this for a live setup as of yet. Wait for the later Release Candidate @@ -18,11 +18,7 @@ I was hoping to keep this out of the README but apparently people remove or chan at the bottom of the page. For those of you finding my project and are willing to appreciate the work with some hard earned coins feel free to donate: -* BTC address: `1HuYK6WPU8o3yWCrAaADDZPRpL5QiXitfv` * LTC address: `Lge95QR2frp9y1wJufjUPCycVsg5gLJPW8` -* FTC address: `6jDgGaUzMVyac5uqBhJCMiFMKCtH1LagTA` -* NVC address: `4Guct6z7NVPVALHRAVn517TTmvqQve4WYr` -* FST address: `g17CfFHqNqR5JnUjtG8RNBYh2WrhEirV67` Website Footer ============== @@ -50,8 +46,6 @@ Requirements This setup has been tested on Ubuntu 12.04, Ubuntu 13.04 and CentOS. It should also work on any related distribution (RHEL, Debian). -For support on how to get `litecoind` or `pushpoold` to work, please ask -in the appropriate forums. Be aware that `MPOS` is **only** for pooled mining. Solo Mining is not supported. They will never match an upstream share, solo miners do not create @@ -77,16 +71,16 @@ The following feature have been implemented so far: * Fully re-written GUI with [Smarty][2] templates * Mobile WebUI -* **NEW** VARDIFF Support +* Scrypt, **NEW** SHA256, VARDIFF Support * Reward Systems * Propotional * PPS * PPLNS * Statistics are cached in Memcache by Cronjob for quick data access -* **NEW** New Theme - * **NEW** Live Dashboard - * **NEW** AJAX Support - * **NEW** Overhauled API +* New Theme + * Live Dashboard + * AJAX Support + * Overhauled API * Web User accounts * Re-Captcha protected registration form * Worker accounts @@ -143,6 +137,15 @@ Other customizations are also possible but will require merging changes together on non-existing features in `MPOS`. For the vast majority, adjusting themes should be enough to highlight your pool from others. In all that, I humbly ask to keep the `MPOS` author reference and Github URL intact. + +Related Software +================ + +There are a few other projects out there that take advantage of MPOS and it's included API. Here a quick list that you can check out for yourself: + +* [MPOS IRC Bot](https://github.com/WKNiGHT-/mpos-bot) written in Python, standlone bot, using the MPOS API +* [MPOS Eggdrop Module](https://github.com/iAmShorty/mpos-eggdrop-tcl) written in TCL, adding MPOS commands to this bot, using the MPOS API + Contributing ============ diff --git a/cronjobs/archive_cleanup.php b/cronjobs/archive_cleanup.php index 9910979f..83c347c4 100755 --- a/cronjobs/archive_cleanup.php +++ b/cronjobs/archive_cleanup.php @@ -28,10 +28,7 @@ require_once('shared.inc.php'); // If we don't keep archives, delete some now to release disk space if (!$share->purgeArchive()) { $log->logError("Failed to delete archived shares, not critical but should be checked!"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Failed to delete archived shares"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $monitoring->endCronjob($cron_name, 'E0008', 1, true); } // Cron cleanup and monitoring diff --git a/cronjobs/auto_payout.php b/cronjobs/auto_payout.php index c65a18ce..239afdc1 100755 --- a/cronjobs/auto_payout.php +++ b/cronjobs/auto_payout.php @@ -27,19 +27,12 @@ require_once('shared.inc.php'); if ($setting->getValue('disable_ap') == 1) { $log->logInfo(" auto payout disabled via admin panel"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Auto-Payout disabled"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - $monitoring->setStatus($cron_name . "_endtime", "date", time()); - exit(0); + $monitoring->endCronjob($cron_name, 'E0009', 0, true); } if ($bitcoin->can_connect() !== true) { $log->logFatal(" unable to connect to RPC server, exiting"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $monitoring->endCronjob($cron_name, 'E0006', 1, true); } // Fetch all users with setup AP @@ -73,14 +66,14 @@ if (! empty($users)) { // Send balance, fees are reduced later by RPC Server try { - $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance - $config['txfee']); + $txid = $bitcoin->sendtoaddress($aUserData['coin_address'], $dBalance - $config['txfee']); } catch (BitcoinClientException $e) { $log->logError('Failed to send requested balance to coin address, please check payout process'); continue; } // Create transaction record - if ($transaction->addTransaction($aUserData['id'], $dBalance - $config['txfee'], 'Debit_AP', NULL, $aUserData['coin_address']) && $transaction->addTransaction($aUserData['id'], $config['txfee'], 'TXFee', NULL, $aUserData['coin_address'])) { + if ($transaction->addTransaction($aUserData['id'], $dBalance - $config['txfee'], 'Debit_AP', NULL, $aUserData['coin_address'], $txid) && $transaction->addTransaction($aUserData['id'], $config['txfee'], 'TXFee', NULL, $aUserData['coin_address'])) { // Mark all older transactions as archived if (!$transaction->setArchived($aUserData['id'], $transaction->insert_id)) $log->logError('Failed to mark transactions for user #' . $aUserData['id'] . ' prior to #' . $transaction->insert_id . ' as archived'); diff --git a/cronjobs/blockupdate.php b/cronjobs/blockupdate.php index 9a1f2906..f814a515 100755 --- a/cronjobs/blockupdate.php +++ b/cronjobs/blockupdate.php @@ -27,10 +27,7 @@ require_once('shared.inc.php'); if ( $bitcoin->can_connect() !== true ) { $log->logFatal("Failed to connect to RPC server\n"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $monitoring->endCronjob($cron_name, 'E0006', 1, true); } // Fetch all unconfirmed blocks @@ -54,7 +51,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { if ($aBlock['confirmations'] == $aBlockInfo['confirmations']) { $log->logDebug(' No update needed'); } else if (!$block->setConfirmations($aBlock['id'], $aBlockInfo['confirmations'])) { - $log->logError(' Failed to update block confirmations'); + $log->logError(' Failed to update block confirmations: ' . $block->getCronMessage()); } } diff --git a/cronjobs/cron_end.inc.php b/cronjobs/cron_end.inc.php index 03a548d2..f0b9fd8a 100644 --- a/cronjobs/cron_end.inc.php +++ b/cronjobs/cron_end.inc.php @@ -19,10 +19,7 @@ limitations under the License. */ // Monitoring cleanup and status update -$monitoring->setStatus($cron_name . "_message", "message", "OK"); -$monitoring->setStatus($cron_name . "_status", "okerror", 0); +$monitoring->endCronjob($cron_name, 'OK', 0, false, false); $monitoring->setStatus($cron_name . "_runtime", "time", microtime(true) - $cron_start[$cron_name]); $monitoring->setStatus($cron_name . "_endtime", "date", time()); -// Mark cron as running for monitoring -$monitoring->setStatus($cron_name . '_active', "yesno", 0); ?> diff --git a/cronjobs/findblock.php b/cronjobs/findblock.php index 114d117b..f41493c7 100755 --- a/cronjobs/findblock.php +++ b/cronjobs/findblock.php @@ -35,10 +35,7 @@ if ( $bitcoin->can_connect() === true ){ $aTransactions = $bitcoin->query('listsinceblock', $strLastBlockHash); } else { $log->logFatal('Unable to conenct to RPC server backend'); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $monitoring->endCronjob($cron_name, 'E0006', 1, true); } // Nothing to do so bail out @@ -51,22 +48,22 @@ if (empty($aTransactions['transactions'])) { // Let us add those blocks as unaccounted foreach ($aTransactions['transactions'] as $iIndex => $aData) { if ( $aData['category'] == 'generate' || $aData['category'] == 'immature' ) { - $aBlockInfo = $bitcoin->query('getblock', $aData['blockhash']); + $aBlockRPCInfo = $bitcoin->query('getblock', $aData['blockhash']); $config['reward_type'] == 'block' ? $aData['amount'] = $aData['amount'] : $aData['amount'] = $config['reward']; - $aData['height'] = $aBlockInfo['height']; - $aData['difficulty'] = $aBlockInfo['difficulty']; + $aData['height'] = $aBlockRPCInfo['height']; + $aData['difficulty'] = $aBlockRPCInfo['difficulty']; $log->logInfo(substr($aData['blockhash'], 0, 15) . "...\t" . $aData['height'] . "\t" . $aData['amount'] . "\t" . $aData['confirmations'] . "\t\t" . $aData['difficulty'] . "\t" . strftime("%Y-%m-%d %H:%M:%S", $aData['time'])); - if ( ! empty($aBlockInfo['flags']) && preg_match('/proof-of-stake/', $aBlockInfo['flags']) ) { + if ( ! empty($aBlockRPCInfo['flags']) && preg_match('/proof-of-stake/', $aBlockRPCInfo['flags']) ) { $log->logInfo("Block above with height " . $aData['height'] . " not added to database, proof-of-stake block!"); continue; } if (!$block->addBlock($aData) ) { - $log->logFatal('Unable to add this block to database: ' . $aData['height']); + $log->logFatal('Unable to add block: ' . $aData['height'] . ': ' . $block->getCronError()); } } } @@ -78,38 +75,52 @@ if (empty($aAllBlocks)) { $log->logDebug('No new blocks without share_id found in database'); } else { // Loop through our unaccounted blocks - $log->logInfo("Block ID\t\tHeight\tAmount\tShare ID\tShares\tFinder\tType"); + $log->logInfo("Block ID\tHeight\t\tAmount\tShare ID\tShares\tFinder\tWorker\t\tType"); foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aBlock['share_id'])) { - // Fetch this blocks upstream ID - $aBlockInfo = $bitcoin->query('getblock', $aBlock['blockhash']); - if ($share->setUpstream($aBlockInfo, $block->getLastUpstreamId())) { - $iCurrentUpstreamId = $share->getUpstreamId(); - $iAccountId = $user->getUserId($share->getUpstreamFinder()); - } else { - $log->logFatal('Unable to fetch blocks upstream share, aborted:' . $share->getError()); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Unable to fetch blocks " . $aBlock['height'] . " upstream share: " . $share->getError()); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit; - } - // Fetch share information - if (!$iPreviousShareId = $block->getLastShareId()) { + if ( !$iPreviousShareId = $block->getLastShareId()) $iPreviousShareId = 0; - $log->logInfo('Unable to find highest share ID found so far, if this is your first block, this is normal.'); - } - $iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId); - - // Store new information - if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId)) - $log->logError('Failed to update share ID in database for block ' . $aBlock['height']); - if (!$block->setFinder($aBlock['id'], $iAccountId)) - $log->logError('Failed to update finder account ID in database for block ' . $aBlock['height']); - if (!$block->setShares($aBlock['id'], $iRoundShares)) - $log->logError('Failed to update share count in database for block ' . $aBlock['height']); - if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) { - $log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height']); + // Fetch this blocks upstream ID + $aBlockRPCInfo = $bitcoin->query('getblock', $aBlock['blockhash']); + if ($share->findUpstreamShare($aBlockRPCInfo, $iPreviousShareId)) { + $iCurrentUpstreamId = $share->getUpstreamShareId(); + // Rarely happens, but did happen once to me + if ($iCurrentUpstreamId == $iPreviousShareId) { + $log->logFatal($share->getErrorMsg('E0063')); + $monitoring->endCronjob($cron_name, 'E0063', 1, true); + } + // Out of order share detection + if ($iCurrentUpstreamId < $iPreviousShareId) { + // Fetch our offending block + $aBlockError = $block->getBlockByShareId($iPreviousShareId); + $log->logError('E0001: The block with height ' . $aBlock['height'] . ' found share ' . $iCurrentUpstreamId . ' which is < than ' . $iPreviousShareId . ' of block ' . $aBlockError['height'] . '.'); + if ( !$aShareError = $share->getShareById($aBlockError['share_id']) || !$aShareCurrent = $share->getShareById($iCurrentUpstreamId)) { + // We were not able to fetch all shares that were causing this detection to trigger, bail out + $log->logFatal('E0002: Failed to fetch both offending shares ' . $iCurrentUpstreamId . ' and ' . $iPreviousShareId . '. Block height: ' . $aBlock['height']); + $monitoring->endCronjob($cron_name, 'E0002', 1, true); + } + // Shares seem to be out of order, so lets change them + if ( !$share->updateShareById($iCurrentUpstreamId, $aShareError) || !$share->updateShareById($iPreviousShareId, $aShareCurrent)) { + // We couldn't update one of the shares! That might mean they have been deleted already + $log->logFatal('E0003: Failed to change shares order: ' . $share->getCronError()); + $monitoring->endCronjob($cron_name, 'E0003', 1, true); + } + // Reset our offending block so the next run re-checks the shares + if (!$block->setShareId($aBlockError['id'], NULL) && !$block->setFinder($aBlockError['id'], NULL) || !$block->setShares($aBlockError['id'], NULL)) { + $log->logFatal('E0004: Failed to reset previous block: ' . $aBlockError['height']); + $log->logError('Failed to reset block in database: ' . $aBlockError['height']); + $monitoring->endCronjob($cron_name, 'E0004', 1, true); + } + $monitoring->endCronjob($cron_name, 'E0007', 0, true); + } else { + $iRoundShares = $share->getRoundShares($iPreviousShareId, $iCurrentUpstreamId); + $iAccountId = $user->getUserId($share->getUpstreamFinder()); + $iWorker = $share->getUpstreamWorker(); + } + } else { + $log->logFatal('E0005: Unable to fetch blocks upstream share, aborted:' . $share->getCronError()); + $monitoring->endCronjob($cron_name, 'E0005', 1, true); } $log->logInfo( @@ -119,9 +130,23 @@ if (empty($aAllBlocks)) { . $iCurrentUpstreamId . "\t\t" . $iRoundShares . "\t" . "[$iAccountId] " . $user->getUserName($iAccountId) . "\t" + . $iWorker . "\t" . $share->share_type ); + // Store new information + if (!$block->setShareId($aBlock['id'], $iCurrentUpstreamId)) + $log->logError('Failed to update share ID in database for block ' . $aBlock['height'] . ': ' . $block->getCronError()); + if (!$block->setFinder($aBlock['id'], $iAccountId)) + $log->logError('Failed to update finder account ID in database for block ' . $aBlock['height'] . ': ' . $block->getCronError()); + if (!$block->setFindingWorker($aBlock['id'], $iWorker)) + $log->logError('Failed to update worker ID in database for block ' . $aBlock['height'] . ': ' . $block->getCronError()); + if (!$block->setShares($aBlock['id'], $iRoundShares)) + $log->logError('Failed to update share count in database for block ' . $aBlock['height'] . ': ' . $block->getCronError()); + if ($config['block_bonus'] > 0 && !$transaction->addTransaction($iAccountId, $config['block_bonus'], 'Bonus', $aBlock['id'])) { + $log->logError('Failed to create Bonus transaction in database for user ' . $user->getUserName($iAccountId) . ' for block ' . $aBlock['height'] . ': ' . $transaction->getCronError()); + } + if ($setting->getValue('disable_notifications') != 1) { // Notify users $aAccounts = $notification->getNotificationAccountIdByType('new_block'); diff --git a/cronjobs/manual_payout.php b/cronjobs/manual_payout.php index befd720a..b0c909ea 100755 --- a/cronjobs/manual_payout.php +++ b/cronjobs/manual_payout.php @@ -26,20 +26,13 @@ chdir(dirname(__FILE__)); require_once('shared.inc.php'); if ($setting->getValue('disable_mp') == 1) { - $log->logInfo(" auto payout disabled via admin panel"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Auto-Payout disabled"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - $monitoring->setStatus($cron_name . "_endtime", "date", time()); - exit(0); + $log->logInfo(" manual payout disabled via admin panel"); + $monitoring->endCronjob($cron_name, 'E0009', 0, true); } if ($bitcoin->can_connect() !== true) { $log->logFatal(" unable to connect to RPC server, exiting"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Unable to connect to RPC server"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $monitoring->endCronjob($cron_name, 'E0006', 1, true); } // Fetch outstanding payout requests @@ -53,6 +46,12 @@ if (count($aPayouts) > 0) { $aData['coin_address'] = $user->getCoinAddress($aData['account_id']); $aData['username'] = $user->getUserName($aData['account_id']); if ($dBalance > $config['txfee']) { + // To ensure we don't run this transaction again, lets mark it completed + if (!$oPayout->setProcessed($aData['id'])) { + $log->logFatal('unable to mark transactions ' . $aData['id'] . ' as processed.'); + $monitoring->endCronjob($cron_name, 'E0010', 1, true); + } + $log->logInfo("\t" . $aData['account_id'] . "\t\t" . $aData['username'] . "\t" . $dBalance . "\t\t" . $aData['coin_address']); try { $aStatus = $bitcoin->validateaddress($aData['coin_address']); @@ -65,21 +64,13 @@ if (count($aPayouts) > 0) { continue; } try { - $bitcoin->sendtoaddress($aData['coin_address'], $dBalance - $config['txfee']); + $txid = $bitcoin->sendtoaddress($aData['coin_address'], $dBalance - $config['txfee']); } catch (BitcoinClientException $e) { $log->logError('Failed to send requested balance to coin address, please check payout process'); continue; } - // To ensure we don't run this transaction again, lets mark it completed - if (!$oPayout->setProcessed($aData['id'])) { - $log->logFatal('unable to mark transactions ' . $aData['id'] . ' as processed.'); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Unable set payout as processed"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); - } - if ($transaction->addTransaction($aData['account_id'], $dBalance - $config['txfee'], 'Debit_MP', NULL, $aData['coin_address']) && $transaction->addTransaction($aData['account_id'], $config['txfee'], 'TXFee', NULL, $aData['coin_address'])) { + if ($transaction->addTransaction($aData['account_id'], $dBalance - $config['txfee'], 'Debit_MP', NULL, $aData['coin_address'], $txid) && $transaction->addTransaction($aData['account_id'], $config['txfee'], 'TXFee', NULL, $aData['coin_address'])) { // Mark all older transactions as archived if (!$transaction->setArchived($aData['account_id'], $transaction->insert_id)) $log->logError('Failed to mark transactions for #' . $aData['account_id'] . ' prior to #' . $transaction->insert_id . ' as archived'); @@ -91,7 +82,8 @@ if (count($aPayouts) > 0) { if (!$notification->sendNotification($aData['account_id'], 'manual_payout', $aMailData)) $log->logError('Failed to send notification email to users address: ' . $aMailData['email']); } else { - $log->logError('Failed to add new Debit_MP transaction in database for user ' . $user->getUserName($aData['account_id'])); + $log->logFatal('Failed to add new Debit_MP transaction in database for user ' . $user->getUserName($aData['account_id'])); + $monitoring->endCronjob($cron_name, 'E0064', 1, true); } } diff --git a/cronjobs/notifications.php b/cronjobs/notifications.php index 8e3ac98a..4bb80a80 100755 --- a/cronjobs/notifications.php +++ b/cronjobs/notifications.php @@ -26,10 +26,7 @@ chdir(dirname(__FILE__)); require_once('shared.inc.php'); if ($setting->getValue('disable_notifications') == 1) { - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Cron disabled by admin"); - $monitoring->setStatus($cron_name . "_status", "okerror", 0); - exit(0); + $monitoring->endCronjob($cron_name, 'E0009', 0, true); } $log->logDebug(" IDLE Worker Notifications ..."); @@ -45,9 +42,9 @@ if (empty($aWorkers)) { $aData['subject'] = 'IDLE Worker : ' . $aWorker['username']; $aData['worker'] = $aWorker['username']; $aData['email'] = $user->getUserEmail($aData['username']); - $log->logInfo(" " . $aWorker['username'] . "..."); + $log->logDebug(" " . $aWorker['username'] . "..."); if (!$notification->sendNotification($aWorker['account_id'], 'idle_worker', $aData)) - $log->logError(" Failed sending notifications: " . $notification->getError() . "\n"); + $log->logDebug(" Failed sending notifications: " . $notification->getCronError() . "\n"); } } @@ -60,15 +57,15 @@ if (!empty($aNotifications)) { foreach ($aNotifications as $aNotification) { $aData = json_decode($aNotification['data'], true); $aWorker = $worker->getWorker($aData['id']); - $log->logInfo(" " . $aWorker['username'] . " ..."); + $log->logDebug(" " . $aWorker['username'] . " ..."); if ($aWorker['hashrate'] > 0) { if ($notification->setInactive($aNotification['id'])) { - $log->logInfo(" updated #" . $aNotification['id'] . " for " . $aWorker['username'] . " as inactive\n"); + $log->logDebug(" updated #" . $aNotification['id'] . " for " . $aWorker['username'] . " as inactive\n"); } else { - $log->logInfo(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n"); + $log->logError(" failed to update #" . $aNotification['id'] . " for " . $aWorker['username'] . "\n"); } } else { - $log->logInfo(" still inactive\n"); + $log->logDebug(" still inactive\n"); } } } else { diff --git a/cronjobs/pplns_payout.php b/cronjobs/pplns_payout.php index 39ea294a..8d60ae2e 100755 --- a/cronjobs/pplns_payout.php +++ b/cronjobs/pplns_payout.php @@ -35,37 +35,50 @@ if ($config['payout_system'] != 'pplns') { $aAllBlocks = $block->getAllUnaccounted('ASC'); if (empty($aAllBlocks)) { $log->logDebug("No new unaccounted blocks found"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks"); - $monitoring->setStatus($cron_name . "_status", "okerror", 0); - exit(0); + $monitoring->endCronjob($cron_name, 'E0011', 0, true, false); } $count = 0; foreach ($aAllBlocks as $iIndex => $aBlock) { + // If we have unaccounted blocks without share_ids, they might not have been inserted yet + if (!$aBlock['share_id']) { + $log->logError('E0062: Block has no share_id, not running payouts'); + $monitoring->endCronjob($cron_name, 'E0062', 0, true); + } + // We support some dynamic share targets but fall back to our fixed value // Re-calculate after each run due to re-targets in this loop if ($config['pplns']['shares']['type'] == 'blockavg' && $block->getBlockCount() > 0) { - $pplns_target = round($block->getAvgBlockShares($aBlock['height'], $config['pplns']['blockavg']['blockcount'])); + $pplns_target = round($block->getAvgBlockShares($aBlock['height'], $config['pplns']['blockavg']['blockcount'])); + } else if ($config['pplns']['shares']['type'] == 'dynamic' && $block->getBlockCount() > 0) { + $pplns_target = round($block->getAvgBlockShares($aBlock['height'], $config['pplns']['blockavg']['blockcount']) * (100 - $config['pplns']['dynamic']['percent'])/100 + $aBlock['shares'] * $config['pplns']['dynamic']['percent']/100); } else { $pplns_target = $config['pplns']['shares']['default']; } - if (!$aBlock['accounted'] && $aBlock['height'] > $setting->getValue('last_accounted_block_height')) { + // Fetch our last paid block information + if ($iLastBlockId = $setting->getValue('last_accounted_block_id')) { + $aLastAccountedBlock = $block->getBlockById($iLastBlockId); + } else { + // A fake block to ensure payouts get started on first round + $iLastBlockId = 0; + $aLastAccountedBlock = array('height' => 0, 'confirmations' => 1); + } + // Ensure we are not paying out twice, ignore if the previous paid block is orphaned (-1 confirmations) and payout anyway + if ((!$aBlock['accounted'] && $aBlock['height'] > $aLastAccountedBlock['height']) || (@$aLastAccountedBlock['confirmations'] == -1)) { $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; $iCurrentUpstreamId = $aBlock['share_id']; if (!is_numeric($iCurrentUpstreamId)) { $log->logFatal("Block " . $aBlock['height'] . " has no share_id associated with it, not going to continue"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Block " . $aBlock['height'] . " has no share_id associated with it"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $monitoring->endCronjob($cron_name, 'E0012', 1, true); } $iRoundShares = $share->getRoundShares($iPreviousShareId, $aBlock['share_id']); $iNewRoundShares = 0; $config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward']; $aRoundAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); + $log->logInfo('Target: ' . $pplns_target . '; Shares: ' . $iRoundShares . '; Height: ' . $aBlock['height'] . '; Amount: ' . $aBlock['amount'] . '; Found by ID: ' . $aBlock['account_id']); + if ($iRoundShares >= $pplns_target) { $log->logDebug("Matching or exceeding PPLNS target of $pplns_target with $iRoundShares"); $iMinimumShareId = $share->getMinimumShareId($pplns_target, $aBlock['share_id']); @@ -73,15 +86,12 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $aAccountShares = $share->getSharesForAccounts($iMinimumShareId - 1, $aBlock['share_id']); if (empty($aAccountShares)) { $log->logFatal("No shares found for this block, aborted! Block Height : " . $aBlock['height']); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $monitoring->endCronjob($cron_name, 'E0013', 1, true); } foreach($aAccountShares as $key => $aData) { $iNewRoundShares += $aData['valid']; } - $log->logInfo('Adjusting round target to PPLNS target ' . $iNewRoundShares); + $log->logInfo('Adjusting round to PPLNS target of ' . $pplns_target . ' shares used ' . $iNewRoundShares); $iRoundShares = $iNewRoundShares; } else { $log->logDebug("Not able to match PPLNS target of $pplns_target with $iRoundShares"); @@ -90,10 +100,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $aAccountShares = $aRoundAccountShares; if (empty($aAccountShares)) { $log->logFatal("No shares found for this block, aborted! Block height: " . $aBlock['height']); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block: " . $aBlock['height']); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $monitoring->endCronjob($cron_name, 'E0013', 1, true); } // Grab only the most recent shares from Archive that fill the missing shares @@ -114,6 +121,24 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $aAccountShares[$key]['invalid'] += $aArchiveShares[$aData['username']]['invalid']; } } + // reverse payout + if ($config['pplns']['reverse_payout']) { + $aSharesData = NULL; + foreach($aAccountShares as $key => $aData) { + $aSharesData[$aData['username']] = $aData; + } + // Add users from archive not in current round + foreach($aArchiveShares as $key => $aArchData) { + if (!array_key_exists($aArchData['account'], $aSharesData)) { + $log->logDebug('Adding user ' . $aArchData['account'] . ' to round shares'); + $log->logDebug(' valid : ' . $aArchData['valid']); + $log->logDebug(' invalid : ' . $aArchData['invalid']); + $aArchData['username'] = $aArchData['account']; + $aSharesData[$aArchData['account']] = $aArchData; + } + } + $aAccountShares = $aSharesData; + } } // We tried to fill up to PPLNS target, now we need to check the actual shares to properly payout users foreach($aAccountShares as $key => $aData) { @@ -159,44 +184,55 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { foreach ($aRoundAccountShares as $key => $aRoundData) { if ($aRoundData['username'] == $aData['username']) if (!$statistics->updateShareStatistics($aRoundData, $aBlock['id'])) - $log->logError('Failed to update share statistics for ' . $aData['username']); + $log->logError('Failed to update share statistics for ' . $aData['username'] . ': ' . $statistics->getCronError()); + } + + // Add PPLNS share statistics + foreach ($aAccountShares as $key => $aRoundData) { + if ($aRoundData['username'] == $aData['username']){ + if (@$statistics->getIdShareStatistics($aRoundData, $aBlock['id'])){ + if (!$statistics->updatePPLNSShareStatistics($aRoundData, $aBlock['id'])) + $log->logError('Failed to update pplns statistics for ' . $aData['username'] . ': ' . $statistics->getCronError()); + } else { + if (!$statistics->insertPPLNSShareStatistics($aRoundData, $aBlock['id'])) + $log->logError('Failed to insert pplns statistics for ' . $aData['username'] . ': ' . $statistics->getCronError()); + } + } } // Add new credit transaction if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id'])) - $log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']); + $log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError()); // Add new fee debit for this block if ($aData['fee'] > 0 && $config['fees'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id'])) - $log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']); + $log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError()); // Add new donation debit if ($aData['donation'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id'])) - $log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']); + $log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError()); } // Store this blocks height as last accounted for - $setting->setValue('last_accounted_block_height', $aBlock['height']); + $setting->setValue('last_accounted_block_id', $aBlock['id']); // Move counted shares to archive before this blockhash upstream share if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId)) - $log->logError('Failed to copy shares to archive table'); + $log->logError('Failed to copy shares to archive table: ' . $share->getCronError() . ': ' . $share->getCronError()); // Delete all accounted shares if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) { - $log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting!"); - exit(1); + $log->logFatal("Failed to delete accounted shares from $iPreviousShareId to $iCurrentUpstreamId, aborting! Error: " . $share->getCronError()); + $monitoring->endCronjob($cron_name, 'E0016', 1, true); } // Mark this block as accounted for if (!$block->setAccounted($aBlock['id'])) { - $log->logFatal("Failed to mark block as accounted! Aborting!"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $log->logFatal("Failed to mark block as accounted! Aborting! Error: " . $block->getCronError()); + $monitoring->endCronjob($cron_name, 'E0014', 1, true); } } else { + $log->logFatal('Potential double payout detected. Aborted.'); $aMailData = array( - 'email' => $setting->getValue('website_email'), + 'email' => $setting->getValue('system_error_email'), 'subject' => 'Payout processing aborted', 'Error' => 'Potential double payout detected. All payouts halted until fixed!', 'BlockID' => $aBlock['id'], @@ -204,12 +240,8 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { 'Block Share ID' => $aBlock['share_id'] ); if (!$mail->sendMail('notifications/error', $aMailData)) - $log->logError(" Failed sending notifications: " . $notification->getError() . "\n"); - $log->logFatal('Potential double payout detected. Aborted.'); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Block height for block too low! Potential double payout detected."); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $log->logError(" Failed sending notifications: " . $notification->getCronError() . "\n"); + $monitoring->endCronjob($cron_name, 'E0015', 1, true); } } diff --git a/cronjobs/pps_payout.php b/cronjobs/pps_payout.php index 7c3515fb..558c3e85 100755 --- a/cronjobs/pps_payout.php +++ b/cronjobs/pps_payout.php @@ -54,19 +54,17 @@ if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > if ($config['pps']['reward']['type'] == 'block') { if ($aLastBlock = $block->getLast()) { $pps_reward = $aLastBlock['amount']; - $log->logInfo("PPS reward using last block, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty); } else { $pps_reward = $config['pps']['reward']['default']; - $log->logInfo("PPS reward using default, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty); } } else { $pps_reward = $config['pps']['reward']['default']; - $log->logInfo("PPS reward fixed default, amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty); } } // Per-share value to be paid out to users -$pps_value = round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12); +$pps_value = round($pps_reward / (pow(2, $config['target_bits']) * $dDifficulty), 12); + // Find our last share accounted and last inserted share for PPS calculations $iPreviousShareId = $setting->getValue('pps_last_share_id'); @@ -75,11 +73,16 @@ $iLastShareId = $share->getLastInsertedShareId(); // Check for all new shares, we start one higher as our last accounted share to avoid duplicates $aAccountShares = $share->getSharesForAccounts($iPreviousShareId + 1, $iLastShareId); -$log->logInfo("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee"); +if (!empty($aAccountShares)) { + // Info for this payout + $log->logInfo("PPS reward type: " . $config['pps']['reward']['type'] . ", amount: " . $pps_reward . "\tdifficulty: " . $dDifficulty . "\tPPS value: " . $pps_value); + $log->logInfo("ID\tUsername\tInvalid\tValid\t\tPPS Value\t\tPayout\t\tDonation\tFee"); +} foreach ($aAccountShares as $aData) { - // Take our valid shares and multiply by per share value - $aData['payout'] = round($aData['valid'] * $pps_value, 8); + // MPOS uses a base difficulty setting to avoid showing weightened shares + // Since we need weightened shares here, we go back to the proper value for payouts + $aData['payout'] = round($aData['valid'] * pow(2, ($config['difficulty'] - 16)) * $pps_value, 8); // Defaults $aData['fee' ] = 0; @@ -94,7 +97,7 @@ foreach ($aAccountShares as $aData) { $log->logInfo($aData['id'] . "\t" . $aData['username'] . "\t" . $aData['invalid'] . "\t" . - $aData['valid'] . "\t*\t" . + $aData['valid'] * pow(2, ($config['difficulty'] - 16)) . "\t*\t" . number_format($pps_value, 12) . "\t=\t" . number_format($aData['payout'], 8) . "\t" . number_format($aData['donation'], 8) . "\t" . @@ -102,15 +105,15 @@ foreach ($aAccountShares as $aData) { // Add new credit transaction if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit_PPS')) - $log->logError('Failed to add Credit_PPS transaction in database'); + $log->logError('Failed to add Credit_PPS transaction in database: ' . $transaction->getCronError()); // Add new fee debit for this block if ($aData['fee'] > 0 && $config['fees'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee_PPS')) - $log->logError('Failed to add Fee_PPS transaction in database'); + $log->logError('Failed to add Fee_PPS transaction in database: ' . $transaction->getCronError()); // Add new donation debit if ($aData['donation'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation_PPS')) - $log->logError('Failed to add Donation_PPS transaction in database'); + $log->logError('Failed to add Donation_PPS transaction in database: ' . $transaction->getCronError()); } // Store our last inserted ID for the next run @@ -118,7 +121,10 @@ $setting->setValue('pps_last_share_id', $iLastShareId); // Fetch all unaccounted blocks $aAllBlocks = $block->getAllUnaccounted('ASC'); -if (empty($aAllBlocks)) $log->logDebug("No new unaccounted blocks found"); +if (empty($aAllBlocks)) { + $log->logDebug("No new unaccounted blocks found"); + // No monitoring event here, not fatal for PPS +} // Go through blocks and archive/delete shares that have been accounted for foreach ($aAllBlocks as $iIndex => $aBlock) { @@ -135,28 +141,22 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { $aAccountShares = $share->getSharesForAccounts(@$iLastBlockShare, $aBlock['share_id']); foreach ($aAccountShares as $key => $aData) { if (!$statistics->updateShareStatistics($aData, $aBlock['id'])) - $log->logError("Failed to update stats for this block on : " . $aData['username']); + $log->logError("Failed to update stats for this block on : " . $aData['username'] . ': ' . $statistics->getCronError()); } // Move shares to archive if ($aBlock['share_id'] < $iLastShareId) { if (!$share->moveArchive($aBlock['share_id'], $aBlock['id'], @$iLastBlockShare)) - $log->logError("Archving failed"); + $log->logError("Failed to copy shares to archive: " . $share->getCronError() . ': ' . $share->getCronError()); } // Delete shares if ($aBlock['share_id'] < $iLastShareId && !$share->deleteAccountedShares($aBlock['share_id'], $iLastBlockShare)) { - $log->logFatal("Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting!"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $log->logFatal("Failed to delete accounted shares from " . $aBlock['share_id'] . " to " . $iLastBlockShare . ", aborting! Error: " . $share->getCronError()); + $monitoring->endCronjob($cron_name, 'E0016', 1, true); } // Mark this block as accounted for if (!$block->setAccounted($aBlock['id'])) { - $log->logFatal("Failed to mark block as accounted! Aborting!"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $log->logFatal("Failed to mark block as accounted! Aborting! Error: " . $block->getCronError()); + $monitoring->endCronjob($cron_name, 'E0014', 1, true); } } diff --git a/cronjobs/proportional_payout.php b/cronjobs/proportional_payout.php index d863cc0d..59819b29 100755 --- a/cronjobs/proportional_payout.php +++ b/cronjobs/proportional_payout.php @@ -35,17 +35,30 @@ if ($config['payout_system'] != 'prop') { $aAllBlocks = $block->getAllUnaccounted('ASC'); if (empty($aAllBlocks)) { $log->logDebug('No new unaccounted blocks found in database'); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "No new unaccounted blocks"); - $monitoring->setStatus($cron_name . "_status", "okerror", 0); - exit(0); + $monitoring->endCronjob($cron_name, 'E0011', 0, true, false); } $count = 0; // Table header for account shares $log->logInfo("ID\tUsername\tValid\tInvalid\tPercentage\tPayout\t\tDonation\tFee"); foreach ($aAllBlocks as $iIndex => $aBlock) { - if (!$aBlock['accounted'] && $aBlock['height'] > $setting->getValue('last_accounted_block_height')) { + // If we have unaccounted blocks without share_ids, they might not have been inserted yet + if (!$aBlock['share_id']) { + $log->logError('E0062: Block has no share_id, not running payouts'); + $monitoring->endCronjob($cron_name, 'E0062', 0, true); + } + + // Fetch last paid block information + if ($iLastBlockId = $setting->getValue('last_accounted_block_id')) { + $aLastAccountedBlock = $block->getBlockById($iLastBlockId); + } else { + // A fake block to ensure payouts get started on first round + $iLastBlockId = 0; + $aLastAccountedBlock = array('height' => 0, 'confirmations' => 1); + } + + // Ensure we are not paying out twice, ignore if the previous paid block is orphaned (-1 confirmations) and payout anyway + if ((!$aBlock['accounted'] && $aBlock['height'] > $aLastAccountedBlock['height']) || (@$aLastAccountedBlock['confirmations'] == -1)) { $iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0; $iCurrentUpstreamId = $aBlock['share_id']; $aAccountShares = $share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']); @@ -54,10 +67,7 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { if (empty($aAccountShares)) { $log->logFatal('No shares found for this block, aborted: ' . $aBlock['height']); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "No shares found for this block, aborted: " . $aBlock['height']); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $monitoring->endCronjob($cron_name, 'E0013', 1, true); } // Loop through all accounts that have found shares for this round @@ -86,46 +96,40 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { // Update user share statistics if (!$statistics->updateShareStatistics($aData, $aBlock['id'])) - $log->logFatal('Failed to update share statistics for ' . $aData['username']); + $log->logFatal('Failed to update share statistics for ' . $aData['username'] . ': ' . $statistics->getCronError()); // Add new credit transaction if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id'])) - $log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username']); + $log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError()); // Add new fee debit for this block if ($aData['fee'] > 0 && $config['fees'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['fee'], 'Fee', $aBlock['id'])) - $log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username']); + $log->logFatal('Failed to insert new Fee transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError()); // Add new donation debit if ($aData['donation'] > 0) if (!$transaction->addTransaction($aData['id'], $aData['donation'], 'Donation', $aBlock['id'])) - $log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username']); + $log->logFatal('Failed to insert new Donation transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError()); } // Add block as accounted for into settings table - $setting->setValue('last_accounted_block_height', $aBlock['height']); + $setting->setValue('last_accounted_block_id', $aBlock['id']); // Move counted shares to archive before this blockhash upstream share if (!$share->moveArchive($iCurrentUpstreamId, $aBlock['id'], $iPreviousShareId)) - $log->logError('Failed to copy shares to archive'); + $log->logError('Failed to copy shares to archive: ' . $share->getCronError()); // Delete all accounted shares if (!$share->deleteAccountedShares($iCurrentUpstreamId, $iPreviousShareId)) { - $log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted'); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Failed to delete accounted shares from " . $iPreviousShareId . " to " . $iCurrentUpstreamId); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $log->logFatal('Failed to delete accounted shares from ' . $iPreviousShareId . ' to ' . $iCurrentUpstreamId . ', aborted! Error: ' . $share->getCronError()); + $monitoring->endCronjob($cron_name, 'E0016', 1, true); } // Mark this block as accounted for if (!$block->setAccounted($aBlock['id'])) { - $log->logFatal('Failed to mark block as accounted! Aborted.'); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", "Failed to mark block " . $aBlock['height'] . " as accounted"); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $log->logFatal('Failed to mark block as accounted! Aborted! Error: ' . $block->getCronError()); + $monitoring->endCronjob($cron_name, 'E0014', 1, true); } } else { - $log->logFatal('Possible double payout detected. Aborted.'); + $log->logFatal('Potential double payout detected. Aborted.'); $aMailData = array( - 'email' => $setting->getValue('website_email'), + 'email' => $setting->getValue('system_error_email'), 'subject' => 'Payout Failure: Double Payout', 'Error' => 'Possible double payout detected', 'BlockID' => $aBlock['id'], @@ -133,11 +137,8 @@ foreach ($aAllBlocks as $iIndex => $aBlock) { 'Block Share ID' => $aBlock['share_id'] ); if (!$mail->sendMail('notifications/error', $aMailData)) - $log->logError(" Failed sending notifications: " . $notification->getError() . "\n"); - $monitoring->setStatus($cron_name . "_active", "yesno", 0); - $monitoring->setStatus($cron_name . "_message", "message", 'Possible double payout detected. Aborted.'); - $monitoring->setStatus($cron_name . "_status", "okerror", 1); - exit(1); + $log->logFatal('Potential double payout detected. Aborted.'); + $monitoring->endCronjob($cron_name, 'E0015', 1, true); } } diff --git a/cronjobs/run-crons.sh b/cronjobs/run-crons.sh index c4877c8f..d908fed5 100755 --- a/cronjobs/run-crons.sh +++ b/cronjobs/run-crons.sh @@ -29,17 +29,32 @@ SUBFOLDER="" # # ################################################################ +# Mac OS detection +OS=`uname` + + +case "$OS" in + Darwin) READLINK=$( which greadlink ) ;; + *) READLINK=$( which readlink ) ;; +esac + +if [[ ! -x $READLINK ]]; then + echo "readlink not found, please install first"; + exit 1; +fi + # My own name ME=$( basename $0 ) # Overwrite some settings via command line arguments -while getopts "hvp:d:" opt; do +while getopts "hfvp:d:" opt; do case "$opt" in h|\?) echo "Usage: $0 [-v] [-p PHP_BINARY] [-d SUBFOLDER]"; exit 0 ;; v) VERBOSE=1 ;; + f) PHP_OPTS="$PHP_OPTS -f";; p) PHP_BIN=$OPTARG ;; d) SUBFOLDER=$OPTARG ;; :) @@ -52,7 +67,7 @@ done # Path to PID file, needs to be writable by user running this PIDFILE="${BASEPATH}/${SUBFOLDER}/${ME}.pid" # Clean PIDFILE path -PIDFILE=$(readlink -m "$PIDFILE") +PIDFILE=$($READLINK -m "$PIDFILE") # Create folders recursively if necessary if ! $(mkdir -p $( dirname $PIDFILE)); then @@ -62,7 +77,7 @@ fi # Find scripts path if [[ -L $0 ]]; then - CRONHOME=$( dirname $( readlink $0 ) ) + CRONHOME=$( dirname $( $READLINK $0 ) ) else CRONHOME=$( dirname $0 ) fi @@ -104,7 +119,7 @@ echo $PID > $PIDFILE for cron in $CRONS; do [[ $VERBOSE == 1 ]] && echo "Running $cron, check logfile for details" - $PHP_BIN $cron + $PHP_BIN $cron $PHP_OPTS done # Remove pidfile diff --git a/cronjobs/shared.inc.php b/cronjobs/shared.inc.php index 50692508..4d41a54e 100644 --- a/cronjobs/shared.inc.php +++ b/cronjobs/shared.inc.php @@ -41,6 +41,16 @@ require_once(BASEPATH . 'include/config/global.inc.php'); // We include all needed files here, even though our templates could load them themself require_once(INCLUDE_DIR . '/autoloader.inc.php'); +// Command line switches +array_shift($argv); +foreach ($argv as $option) { + switch ($option) { + case '-f': + $monitoring->setStatus($cron_name . "_disabled", "yesno", 0); + break; + } +} + // Load 3rd party logging library for running crons $log = new KLogger ( 'logs/' . $cron_name . '.txt' , KLogger::INFO ); $log->LogDebug('Starting ' . $cron_name); @@ -48,8 +58,13 @@ $log->LogDebug('Starting ' . $cron_name); // Load the start time for later runtime calculations for monitoring $cron_start[$cron_name] = microtime(true); +// Check if our cron is activated +if ($monitoring->isDisabled($cron_name)) { + $log->logFatal('Cronjob is currently disabled due to errors, use -f option to force running cron.'); + $monitoring->endCronjob($cron_name, 'E0018', 1, true, false); +} + // Mark cron as running for monitoring $log->logDebug('Marking cronjob as running for monitoring'); -$monitoring->setStatus($cron_name . '_active', 'yesno', 1); $monitoring->setStatus($cron_name . '_starttime', 'date', time()); ?> diff --git a/cronjobs/tickerupdate.php b/cronjobs/tickerupdate.php index 6afff97d..69ad26d3 100755 --- a/cronjobs/tickerupdate.php +++ b/cronjobs/tickerupdate.php @@ -28,12 +28,26 @@ require_once('shared.inc.php'); // Include additional file not set in autoloader require_once(CLASS_DIR . '/tools.class.php'); +// Fetch latest coin price via API call if ($price = $tools->getPrice()) { - $log->logInfo("Price update: found $price as price"); + $log->logDebug("Price update: found $price as price"); if (!$setting->setValue('price', $price)) $log->logError("unable to update value in settings table"); } else { - $log->logFatal("failed to fetch API data: " . $tools->getError()); + $log->logError("failed to fetch API data: " . $tools->getCronError()); +} + +// Update Uptime Robot status in Settings table via API call +if ($api_keys = $setting->getValue('monitoring_uptimerobot_api_keys')) { + if (!strstr($api_keys, '|')) { + $monitoring->setTools($tools); + if (!$monitoring->storeUptimeRobotStatus()) { + $log->logError($monitoring->getCronError()); + $monitoring->endCronjob($cron_name, 'E0017', 1, false, false); + } + } +} else { + $log->logDebug('Skipped Uptime Robot API update, missing api keys'); } require_once('cron_end.inc.php'); diff --git a/public/.htaccess b/public/.htaccess index 1caaddc0..9750f3bb 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -1,3 +1,4 @@ +ErrorDocument 404 /index.php?page=error&action=404 RedirectMatch 404 /templates(/|$) RedirectMatch 404 /include(/|$) RedirectMatch 404 /.git(/|$) diff --git a/public/include/autoloader.inc.php b/public/include/autoloader.inc.php index 8950f103..3520cc05 100644 --- a/public/include/autoloader.inc.php +++ b/public/include/autoloader.inc.php @@ -1,10 +1,17 @@ table; + } public function setDebug($debug) { $this->debug = $debug; } @@ -19,6 +28,9 @@ class Base { public function setMail($mail) { $this->mail = $mail; } + public function setSalt($salt) { + $this->salt = $salt; + } public function setSmarty($smarty) { $this->smarty = $smarty; } @@ -28,39 +40,106 @@ class Base { public function setConfig($config) { $this->config = $config; } + public function setErrorCodes(&$aErrorCodes) { + $this->aErrorCodes =& $aErrorCodes; + } public function setToken($token) { $this->token = $token; } public function setBlock($block) { $this->block = $block; } + public function setTransaction($transaction) { + $this->transaction = $transaction; + } + public function setMemcache($memcache) { + $this->memcache = $memcache; + } + public function setStatistics($statistics) { + $this->statistics = $statistics; + } public function setSetting($setting) { $this->setting = $setting; } + public function setTools($tools) { + $this->tools = $tools; + } public function setBitcoin($bitcoin) { $this->bitcoin = $bitcoin; } public function setTokenType($tokentype) { $this->tokentype = $tokentype; } + public function setShare($share) { + $this->share = $share; + } public function setErrorMessage($msg) { $this->sError = $msg; + // Default to same error for crons + $this->sCronError = $msg; + } + public function setCronMessage($msg) { + // Used to overwrite any errors with a custom cron one + $this->sCronError = $msg; } public function getError() { return $this->sError; } + /** + * Additional information in error string for cronjobs logging + **/ + public function getCronError() { + return $this->sCronError; + } + /** + * Get error message from error code array + * @param errCode string Error code string + * @param optional string Optional addtitional error strings to append + * @retrun string Error Message + **/ + public function getErrorMsg($errCode='') { + if (!is_array($this->aErrorCodes)) return 'Error codes not loaded'; + if (!array_key_exists($errCode, $this->aErrorCodes)) return 'Unknown Error Code: ' . $errCode; + if (func_num_args() > 1) { + $args = func_get_args(); + array_shift($args); + $param_count = substr_count($this->aErrorCodes[$errCode], '%s'); + if ($param_count == count($args)) { + return vsprintf($this->aErrorCodes[$errCode], $args); + } else { + return $this->aErrorCodes[$errCode] . ' (missing information to complete string)'; + } + } else { + return $this->aErrorCodes[$errCode]; + } + } + + /** + * Get an element as an associated array + **/ + protected function getAllAssoc($value, $field='id', $type='i') { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE $field = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param($type, $value) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_assoc(); + return false; + } /** * Get a single row from the table * @param value string Value to search for * @param search Return column to search for * @param field string Search column * @param type string Type of value + * @param lower bool try with LOWER comparision * @return array Return result **/ - protected function getSingle($value, $search='id', $field='id', $type="i") { + protected function getSingle($value, $search='id', $field='id', $type="i", $lower=false) { $this->debug->append("STA " . __METHOD__, 4); - $stmt = $this->mysqli->prepare("SELECT $search FROM $this->table WHERE $field = ? LIMIT 1"); + $sql = "SELECT $search FROM $this->table WHERE"; + $lower ? $sql .= " LOWER($field) = LOWER(?)" : $sql .= " $field = ?"; + $sql .= " LIMIT 1"; + $stmt = $this->mysqli->prepare($sql); if ($this->checkStmt($stmt)) { $stmt->bind_param($type, $value); $stmt->execute(); @@ -72,18 +151,38 @@ class Base { return false; } + /** + * Check if the prepared statement is valid + * @param $bState Statement return value + * @return bool true or false + **/ function checkStmt($bState) { $this->debug->append("STA " . __METHOD__, 4); - if ($bState ===! true) { - $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); - $this->setErrorMessage('Internal application Error'); - return false; - } + if ($bState ===! true) + return $this->sqlError(); return true; } + + /** + * Catch SQL errors with this method + * @param error_code string Error code to read + **/ + protected function sqlError($error_code='E0020') { + // More human-readable error for UI + if (func_num_args() == 0) { + $this->setErrorMessage($this->getErrorMsg($error_code)); + } else { + $this->setErrorMessage(call_user_func_array(array($this, 'getErrorMsg'), func_get_args())); + } + // Default to SQL error for debug and cron errors + $this->debug->append($this->getErrorMsg('E0019', $this->mysqli->error)); + $this->setCronMessage($this->getErrorMsg('E0019', $this->mysqli->error)); + return false; + } + /** - * Update a single row in a table * @param userID int Account ID + * Update a single row in a table * @param field string Field to update * @return bool **/ @@ -94,7 +193,7 @@ class Base { if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute()) return true; $this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id"); - return false; + $this->sqlError(); } /** diff --git a/public/include/classes/bitcoinwrapper.class.php b/public/include/classes/bitcoinwrapper.class.php index 6881babc..dc6102b6 100644 --- a/public/include/classes/bitcoinwrapper.class.php +++ b/public/include/classes/bitcoinwrapper.class.php @@ -22,6 +22,12 @@ class BitcoinWrapper extends BitcoinClient { /** * Wrap variouns methods to add caching **/ + // Caching this, used for each can_connect call + public function getinfo() { + $this->oDebug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__)) return $data; + return $this->memcache->setCache(__FUNCTION__, parent::getinfo(), 30); + } public function getblockcount() { $this->oDebug->append("STA " . __METHOD__, 4); if ($data = $this->memcache->get(__FUNCTION__)) return $data; @@ -48,15 +54,21 @@ class BitcoinWrapper extends BitcoinClient { if ($data = $this->memcache->get(__FUNCTION__)) return $data; try { $dNetworkHashrate = $this->query('getmininginfo'); - if (is_array($dNetworkHashrate) && array_key_exists('networkhashps', $dNetworkHashrate)) { - $dNetworkHashrate = $dNetworkHashrate['networkhashps']; - } else if (is_array($dNetworkHashrate) && array_key_exists('hashespersec', $dNetworkHashrate)) { - $dNetworkHashrate = $dNetworkHashrate['hashespersec']; - } else if (is_array($dNetworkHashrate) && array_key_exists('netmhashps', $dNetworkHashrate)) { - $dNetworkHashrate = $dNetworkHashrate['netmhashps'] * 1000 * 1000; + if (is_array($dNetworkHashrate)) { + if (array_key_exists('networkhashps', $dNetworkHashrate)) { + $dNetworkHashrate = $dNetworkHashrate['networkhashps']; + } else if (array_key_exists('hashespersec', $dNetworkHashrate)) { + $dNetworkHashrate = $dNetworkHashrate['hashespersec']; + } else if (array_key_exists('netmhashps', $dNetworkHashrate)) { + $dNetworkHashrate = $dNetworkHashrate['netmhashps'] * 1000 * 1000; + } else { + // Unsupported implementation + $dNetworkHashrate = 0; + } } } catch (Exception $e) { - return false; + // getmininginfo does not exist, cache for an hour + return $this->memcache->setCache(__FUNCTION__, 0, 3600); } return $this->memcache->setCache(__FUNCTION__, $dNetworkHashrate, 30); } diff --git a/public/include/classes/block.class.php b/public/include/classes/block.class.php index 027d06c5..3ed11d1f 100644 --- a/public/include/classes/block.class.php +++ b/public/include/classes/block.class.php @@ -1,32 +1,10 @@ debug = $debug; - $this->mysqli = $mysqli; - $this->config = $config; - $this->debug->append("Instantiated Block class", 2); - } - - // get and set methods - private function setErrorMessage($msg) { - $this->sError = $msg; - } - public function getError() { - return $this->sError; - } - public function getTableName() { - return $this->table; - } +class Block extends Base { + protected $table = 'blocks'; /** * Specific method to fetch the latest block found @@ -35,13 +13,9 @@ class Block { **/ public function getLast() { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height DESC LIMIT 1"); - if ($this->checkStmt($stmt)) { - $stmt->execute(); - $result = $stmt->get_result(); - $stmt->close(); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_assoc(); - } - return false; + return $this->sqlError(); } /** @@ -53,7 +27,31 @@ class Block { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE height = ? LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('i', $height) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_assoc(); - return false; + return $this->sqlError(); + } + + /** + * Get a specific block, by share_id + * @param share_id int Blocks share_id + * @return data array Block information from DB + **/ + public function getBlockByShareId($share_id) { + $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE share_id = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $share_id) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_assoc(); + return $this->sqlError(); + } + + /** + * Get a specific block, by id + * @param share_id int Blocks share_id + * @return data array Block information from DB + **/ + public function getBlockById($id) { + $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_assoc(); + return $this->sqlError(); } /** @@ -65,7 +63,7 @@ class Block { $stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->table LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->share_id; - return false; + return $this->sqlError(); } /** @@ -77,7 +75,7 @@ class Block { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE ISNULL(share_id) ORDER BY height $order"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - return false; + return $this->sqlError(); } /** @@ -89,7 +87,7 @@ class Block { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE accounted = 0 ORDER BY height $order"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - return false; + return $this->sqlError(); } /** @@ -101,7 +99,7 @@ class Block { $stmt = $this->mysqli->prepare("SELECT COUNT(id) AS blocks FROM $this->table"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return (int)$result->fetch_object()->blocks; - return false; + return $this->sqlError(); } /** @@ -113,7 +111,7 @@ class Block { $stmt = $this->mysqli->prepare("SELECT AVG(x.shares) AS average FROM (SELECT shares FROM $this->table WHERE height <= ? ORDER BY height DESC LIMIT ?) AS x"); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $height, $limit) && $stmt->execute() && $result = $stmt->get_result()) return (float)$result->fetch_object()->average; - return false; + return $this->sqlError(); } /** @@ -125,7 +123,7 @@ class Block { $stmt = $this->mysqli->prepare("SELECT AVG(x.amount) AS average FROM (SELECT amount FROM $this->table ORDER BY height DESC LIMIT ?) AS x"); if ($this->checkStmt($stmt) && $stmt->bind_param('i', $limit) && $stmt->execute() && $result = $stmt->get_result()) return (float)$result->fetch_object()->average; - return false; + return $this->sqlError(); } /** @@ -137,7 +135,7 @@ class Block { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE confirmations < ? AND confirmations > -1"); if ($this->checkStmt($stmt) && $stmt->bind_param("i", $confirmations) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - return false; + return $this->sqlError(); } /** @@ -148,13 +146,9 @@ class Block { **/ public function setConfirmations($block_id, $confirmations) { $stmt = $this->mysqli->prepare("UPDATE $this->table SET confirmations = ? WHERE id = ?"); - if ($this->checkStmt($stmt)) { - $stmt->bind_param("ii", $confirmations, $block_id) or die($stmt->error); - $stmt->execute() or die("Failed"); - $stmt->close(); + if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $confirmations, $block_id) && $stmt->execute()) return true; - } - return false; + return $this->sqlError(); } /** @@ -164,13 +158,9 @@ class Block { **/ public function getAll($order='DESC') { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table ORDER BY height $order"); - if ($this->checkStmt($stmt)) { - $stmt->execute(); - $result = $stmt->get_result(); - $stmt->close(); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - } - return false; + return $this->sqlError(); } /** @@ -180,50 +170,21 @@ class Block { **/ public function addBlock($block) { $stmt = $this->mysqli->prepare("INSERT INTO $this->table (height, blockhash, confirmations, amount, difficulty, time) VALUES (?, ?, ?, ?, ?, ?)"); - if ($this->checkStmt($stmt)) { - $stmt->bind_param('isiddi', $block['height'], $block['blockhash'], $block['confirmations'], $block['amount'], $block['difficulty'], $block['time']); - if (!$stmt->execute()) { - $this->debug->append("Failed to execute statement: " . $stmt->error); - $this->setErrorMessage($stmt->error); - $stmt->close(); - return false; - } - $stmt->close(); + if ($this->checkStmt($stmt) && $stmt->bind_param('isiddi', $block['height'], $block['blockhash'], $block['confirmations'], $block['amount'], $block['difficulty'], $block['time']) && $stmt->execute()) return true; - } - return false; - } - - public function getLastUpstreamId() { - $stmt = $this->mysqli->prepare(" - SELECT MAX(share_id) AS share_id FROM $this->table - "); - if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($share_id) && $stmt->fetch()) - return $share_id ? $share_id : 0; - // Catchall - return false; + return $this->sqlError(); } /** - * Update a single column within a single row - * @param block_id int Block ID to update - * @param field string Column name to update - * @param value string Value to insert - * @return bool + * Get our last inserted upstream ID from table + * @param none + * @return mixed upstream ID or 0, false on error **/ - private function updateSingle($block_id, $field, $value) { - $stmt = $this->mysqli->prepare("UPDATE $this->table SET $field = ? WHERE id = ?"); - if ($this->checkStmt($stmt)) { - $stmt->bind_param('ii', $value, $block_id); - if (!$stmt->execute()) { - $this->debug->append("Failed to update block ID $block_id with finder ID $account_id"); - $stmt->close(); - return false; - } - $stmt->close(); - return true; - } - return false; + public function getLastUpstreamId() { + $stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->table"); + if ($this->checkStmt($stmt) && $stmt->execute() && $stmt->bind_result($share_id) && $stmt->fetch()) + return $share_id ? $share_id : 0; + return $this->sqlError(); } /** @@ -233,7 +194,19 @@ class Block { * @return bool **/ public function setFinder($block_id, $account_id=NULL) { - return $this->updateSingle($block_id, 'account_id', $account_id); + $field = array( 'name' => 'account_id', 'value' => $account_id, 'type' => 'i' ); + return $this->updateSingle($block_id, $field); + } + + /** + * Set finding worker of a block + * @param block_id int Block ID + * @param worker_id int Worker ID of finder + * @return bool + **/ + public function setFindingWorker($block_id, $worker=NULL) { + $field = array( 'name' => 'worker_name', 'value' => $worker, 'type' => 's' ); + return $this->updateSingle($block_id, $field); } /** @@ -243,7 +216,8 @@ class Block { * @return bool **/ public function setShareId($block_id, $share_id) { - return $this->updateSingle($block_id, 'share_id', $share_id); + $field = array( 'name' => 'share_id', 'value' => $share_id, 'type' => 'i'); + return $this->updateSingle($block_id, $field); } /** @@ -253,7 +227,8 @@ class Block { * @return bool **/ public function setShares($block_id, $shares=NULL) { - return $this->updateSingle($block_id, 'shares', $shares); + $field = array( 'name' => 'shares', 'value' => $shares, 'type' => 'i'); + return $this->updateSingle($block_id, $field); } /** @@ -263,21 +238,14 @@ class Block { **/ public function setAccounted($block_id=NULL) { if (empty($block_id)) return false; - return $this->updateSingle($block_id, 'accounted', 1); - } - - /** - * Helper function - **/ - private function checkStmt($bState) { - if ($bState ===! true) { - $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); - $this->setErrorMessage('Internal application Error'); - return false; - } - return true; + $field = array( 'name' => 'accounted', 'value' => 1, 'type' => 'i'); + return $this->updateSingle($block_id, $field); } } // Automatically load our class for furhter usage -$block = new Block($debug, $mysqli, $config); +$block = new Block(); +$block->setDebug($debug); +$block->setMysql($mysqli); +$block->setConfig($config); +$block->setErrorCodes($aErrorCodes); diff --git a/public/include/classes/invitation.class.php b/public/include/classes/invitation.class.php index 822dfef3..1d02caa8 100644 --- a/public/include/classes/invitation.class.php +++ b/public/include/classes/invitation.class.php @@ -16,9 +16,7 @@ class Invitation extends Base { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ?"); if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - $this->setErrorMessage('Unable to fetch invitiations send from your account'); - $this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro); - return false; + $this->sqlError('E0021'); } /** @@ -31,9 +29,7 @@ class Invitation extends Base { $stmt = $this->mysqli->prepare("SELECT count(id) AS total FROM $this->table WHERE account_id = ?"); if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $stmt->bind_result($total) && $stmt->fetch()) return $total; - $this->setErrorMessage('Unable to fetch invitiations send from your account'); - $this->debug->append('Failed to fetch invitations from database: ' . $this->mysqli->errro); - return false; + $this->sqlError('E0021'); } /** @@ -65,7 +61,7 @@ class Invitation extends Base { **/ public function setActivated($token_id) { if (!$iInvitationId = $this->getByTokenId($token_id)) { - $this->setErrorMessage('Unable to convert token ID to invitation ID'); + $this->setErrorMessage($this->getErrorMsg('E0030')); return false; } $field = array('name' => 'is_activated', 'type' => 'i', 'value' => 1); @@ -84,8 +80,9 @@ class Invitation extends Base { $stmt = $this->mysqli->prepare("INSERT INTO $this->table ( account_id, email, token_id ) VALUES ( ?, ?, ?)"); if ($stmt && $stmt->bind_param('isi', $account_id, $email, $token_id) && $stmt->execute()) return true; - return false; + $this->sqlError('E0022'); } + /** * Send an invitation out to a user * Uses the mail class to send mails @@ -97,39 +94,37 @@ class Invitation extends Base { $this->debug->append("STA " . __METHOD__, 4); // Check data input if (empty($aData['email']) || !filter_var($aData['email'], FILTER_VALIDATE_EMAIL)) { - $this->setErrorMessage( 'Invalid e-mail address' ); + $this->setErrorMessage($this->getErrorMsg('E0023')); return false; } if (preg_match('/[^a-z_\.\!\?\-0-9 ]/i', $aData['message'])) { - $this->setErrorMessage('Message may only contain alphanumeric characters'); + $this->setErrorMessage($this->getErrorMsg('E0024')); return false; } // Ensure this invitation does not exist yet nor do we have an account with that email if ($this->user->getEmail($aData['email'])) { - $this->setErrorMessage('This email is already registered as an account'); + $this->setErrorMessage($this->getErrorMsg('E0025')); return false; } if ($this->getByEmail($aData['email'])) { - $this->setErrorMessage('A pending invitation for this address already exists'); + $this->setErrorMessage($this->getErrorMsg('E0026')); return false; } if (!$aData['token'] = $this->token->createToken('invitation', $account_id)) { - $this->setErrorMessage('Unable to generate invitation token: ' . $this->token->getError()); + $this->setErrorMessage($this->getErrorMsg('E0027', $this->token->getError())); return false; } $aData['username'] = $this->user->getUserName($account_id); $aData['subject'] = 'Pending Invitation'; if ($this->mail->sendMail('invitations/body', $aData)) { $aToken = $this->token->getToken($aData['token']); - if (!$this->createInvitation($account_id, $aData['email'], $aToken['id'])) { - $this->setErrorMessage('Unable to create invitation record'); + if (!$this->createInvitation($account_id, $aData['email'], $aToken['id'])) return false; - } return true; } else { - $this->setErrorMessage('Unable to send email to recipient'); + $this->setErrorMessage($this->getErrorMsg('E0028')); } - $this->setErrorMessage('Unable to send invitation'); + $this->setErrorMessage($this->getErrorMsg('E0029')); return false; } } @@ -142,5 +137,5 @@ $invitation->setMail($mail); $invitation->setUser($user); $invitation->setToken($oToken); $invitation->setConfig($config); - +$invitation->setErrorCodes($aErrorCodes); ?> diff --git a/public/include/classes/mail.class.php b/public/include/classes/mail.class.php index ea00cef6..08b34ddd 100644 --- a/public/include/classes/mail.class.php +++ b/public/include/classes/mail.class.php @@ -5,16 +5,6 @@ if (!defined('SECURITY')) die('Hacking attempt'); class Mail extends Base { - function checkStmt($bState) { - $this->debug->append("STA " . __METHOD__, 4); - if ($bState ===! true) { - $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); - $this->setErrorMessage('Internal application Error'); - return false; - } - return true; - } - /** * Mail form contact site admin * @param senderName string senderName @@ -28,19 +18,19 @@ class Mail extends Base { public function contactform($senderName, $senderEmail, $senderSubject, $senderMessage) { $this->debug->append("STA " . __METHOD__, 4); if (preg_match('/[^a-z_\.\!\?\-0-9\\s ]/i', $senderName)) { - $this->setErrorMessage('Username may only contain alphanumeric characters'); + $this->setErrorMessage($this->getErrorMsg('E0024')); return false; } if (empty($senderEmail) || !filter_var($senderEmail, FILTER_VALIDATE_EMAIL)) { - $this->setErrorMessage( 'Invalid e-mail address' ); + $this->setErrorMessage($this->getErrorMsg('E0023')); return false; } if (preg_match('/[^a-z_\.\!\?\-0-9\\s ]/i', $senderSubject)) { - $this->setErrorMessage('Subject may only contain alphanumeric characters'); + $this->setErrorMessage($this->getErrorMsg('E0034')); return false; } if (strlen(strip_tags($senderMessage)) < strlen($senderMessage)) { - $this->setErrorMessage('Your message may only contain alphanumeric characters'); + $this->setErrorMessage($this->getErrorMsg('E0024')); return false; } $aData['senderName'] = $senderName; @@ -58,16 +48,28 @@ class Mail extends Base { return false; } + /** + * Send a mail with templating via Smarty + * @param template string Template name within the mail folder, no extension + * @param aData array Data array with some required fields + * SUBJECT : Mail Subject + * email : Destination address + **/ public function sendMail($template, $aData) { + // Make sure we don't load a cached filed + $this->smarty->clearCache(BASEPATH . 'templates/mail/' . $template . '.tpl'); + $this->smarty->clearCache(BASEPATH . 'templates/mail/subject.tpl'); $this->smarty->assign('WEBSITENAME', $this->setting->getValue('website_name')); $this->smarty->assign('SUBJECT', $aData['subject']); $this->smarty->assign('DATA', $aData); $headers = 'From: Website Administration <' . $this->setting->getValue('website_email') . ">\n"; $headers .= "MIME-Version: 1.0\n"; $headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n"; + if (strlen(@$aData['senderName']) > 0 && @strlen($aData['senderEmail']) > 0 ) + $headers .= 'Reply-To: ' . $aData['senderName'] . ' <' . $aData['senderEmail'] . ">\n"; if (mail($aData['email'], $this->smarty->fetch(BASEPATH . 'templates/mail/subject.tpl'), $this->smarty->fetch(BASEPATH . 'templates/mail/' . $template . '.tpl'), $headers)) return true; - $this->setErrorMessage('Unable to send mail'); + $this->setErrorMessage($this->sqlError('E0031')); return false; } } @@ -79,4 +81,5 @@ $mail->setMysql($mysqli); $mail->setSmarty($smarty); $mail->setConfig($config); $mail->setSetting($setting); +$mail->setErrorCodes($aErrorCodes); ?> diff --git a/public/include/classes/monitoring.class.php b/public/include/classes/monitoring.class.php index ff69007f..04a1324b 100644 --- a/public/include/classes/monitoring.class.php +++ b/public/include/classes/monitoring.class.php @@ -1,14 +1,65 @@ debug = $debug; - $this->mysqli = $mysqli; - $this->table = 'monitoring'; +class Monitoring extends Base { + protected $table = 'monitoring'; + + /** + * Store Uptime Robot status information as JSON in settings table + * @param none + * @return bool true on success, false on error + **/ + public function storeUptimeRobotStatus() { + if ($api_keys = $this->setting->getValue('monitoring_uptimerobot_api_keys')) { + $aJSONData = array(); + $url = 'http://api.uptimerobot.com'; + $aMonitors = explode(',', $api_keys); + foreach ($aMonitors as $aData) { + $temp = explode('|', $aData); + $aMonitor['api_key'] = $temp[0]; + $aMonitor['monitor_id'] = $temp[1]; + $target = '/getMonitors?apiKey=' . $aMonitor['api_key'] . '&monitors=' . $aMonitor['monitor_id'] . '&format=json&noJsonCallback=1&customUptimeRatio=1-7-30&logs=1'; + $aMonitorStatus = $this->tools->getApi($url, $target); + if (!$aMonitorStatus || @$aMonitorStatus['stat'] == 'fail') { + if (is_array($aMonitorStatus) && array_key_exists('message', @$aMonitorStatus)) { + $this->setErrorMessage($this->getErrorMsg('E0032', $aMonitorStatus['message'])); + } else { + $this->setErrorMessage($this->getErrorMsg('E0032', $this->tools->getError())); + } + return false; + } + $aMonitorStatus['monitors']['monitor'][0]['customuptimeratio'] = explode('-', $aMonitorStatus['monitors']['monitor'][0]['customuptimeratio']); + $aAllMonitorsStatus[] = $aMonitorStatus['monitors']['monitor'][0]; + } + if (!$this->setting->setValue('monitoring_uptimerobot_status', json_encode($aAllMonitorsStatus)) || !$this->setting->setValue('monitoring_uptimerobot_lastcheck', time())) { + $this->setErrorMessage($this->getErrorMsg('E0033'), $setting->getError()); + return false; + } + } + return true; + } + + /** + * Fetch Uptime Robot Status from settings table + * @param none + * @return array Data on success, false on failure + **/ + public function getUptimeRobotStatus() { + if ($json = $this->setting->getValue('monitoring_uptimerobot_status')) + return json_decode($json, true); + return false; + } + + /** + * Check that our cron is currently activated + * @param name string Cronjob name + * @return bool true or false + **/ + public function isDisabled($name) { + $aStatus = $this->getStatus($name . '_disabled'); + return $aStatus['value']; } /** @@ -21,8 +72,7 @@ class Monitoring { if ($query && $query->bind_param('s', $name) && $query->execute() && $result = $query->get_result()) { return $result->fetch_assoc(); } else { - $this->debug->append("Failed to fetch variable $name from $this->table"); - return false; + $this->sqlError(); } return $value; } @@ -44,6 +94,41 @@ class Monitoring { $this->debug->append("Failed to set $name to $value"); return false; } + + /** + * End cronjob with an error message + * @param cron_name string Cronjob Name + * @param msgCode string Message code as stored in error_codes array + * @param exitCode int Exit code to pass on to exit function and monitor report + * @param fatal boolean Should we exit out entirely + * @return none + **/ + public function endCronjob($cron_name, $msgCode, $exitCode=0, $fatal=false, $mail=true) { + $this->setStatus($cron_name . "_active", "yesno", 0); + $this->setStatus($cron_name . "_message", "message", $this->getErrorMsg($msgCode)); + $this->setStatus($cron_name . "_status", "okerror", $exitCode); + $this->setStatus($cron_name . "_endtime", "date", time()); + if ($mail) { + $aMailData = array( + 'email' => $this->setting->getValue('system_error_email'), + 'subject' => 'Cronjob Failure', + 'Error Code' => $msgCode, + 'Error Message' => $this->getErrorMsg($msgCode) + ); + if (!$this->mail->sendMail('notifications/error', $aMailData)) + $this->setErrorMessage('Failed to send mail notification'); + } + if ($fatal) { + if ($exitCode != 0) $this->setStatus($cron_name . "_disabled", "yesno", 1); + exit($exitCode); + } + } } -$monitoring = new Monitoring($debug, $mysqli); +$monitoring = new Monitoring(); +$monitoring->setErrorCodes($aErrorCodes); +$monitoring->setConfig($config); +$monitoring->setDebug($debug); +$monitoring->setMail($mail); +$monitoring->setMysql($mysqli); +$monitoring->setSetting($setting); diff --git a/public/include/classes/news.class.php b/public/include/classes/news.class.php index 3f3f8631..f83e4253 100644 --- a/public/include/classes/news.class.php +++ b/public/include/classes/news.class.php @@ -5,13 +5,23 @@ if (!defined('SECURITY')) die('Hacking attempt'); class News extends Base { - var $table = 'news'; + protected $table = 'news'; + /** + * Get activation status of post + * @param id int News ID + * @return bool true or false + **/ public function getActive($id) { $this->debug->append("STA " . __METHOD__, 5); return $this->getSingle($id, 'active', 'id'); } + /** + * Switch activation status + * @param id int News ID + * @return bool true or false + **/ public function toggleActive($id) { $this->debug->append("STA " . __METHOD__, 5); $field = array('name' => 'active', 'type' => 'i', 'value' => !$this->getActive($id)); @@ -26,8 +36,7 @@ class News extends Base { $stmt = $this->mysqli->prepare("SELECT n.*, a.username AS author FROM $this->table AS n LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = n.account_id WHERE active = 1 ORDER BY time DESC"); if ($stmt && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - // Catchall - return false; + return $this->sqlError('E0040'); } /** @@ -38,8 +47,7 @@ class News extends Base { $stmt = $this->mysqli->prepare("SELECT n.*, a.username AS author FROM $this->table AS n LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = n.account_id ORDER BY time DESC"); if ($stmt && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - // Catchall - return false; + return $this->sqlError('E0039'); } /** @@ -50,8 +58,7 @@ class News extends Base { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE id = ?"); if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_assoc(); - // Catchall - return false; + return $this->sqlError('E0038'); } /** @@ -62,8 +69,7 @@ class News extends Base { $stmt = $this->mysqli->prepare("UPDATE $this->table SET content = ?, header = ?, active = ? WHERE id = ?"); if ($stmt && $stmt->bind_param('ssii', $content, $header, $active, $id) && $stmt->execute() && $stmt->affected_rows == 1) return true; - $this->setErrorMessage("Failed to update news entry $id"); - return false; + return $this->sqlError('E0037'); } public function deleteNews($id) { @@ -72,8 +78,7 @@ class News extends Base { $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id = ?"); if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $stmt->affected_rows == 1) return true; - $this->setErrorMessage("Failed to delete news entry $id"); - return false; + return $this->sqlError('E0036'); } /** @@ -89,9 +94,7 @@ class News extends Base { $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, header, content, active) VALUES (?,?,?,?)"); if ($stmt && $stmt->bind_param('issi', $account_id, $aData['header'], $aData['content'], $active) && $stmt->execute()) return true; - $this->debug->append("Failed to add news: " . $this->mysqli->error); - $this->setErrorMessage("Unable to add new news: " . $this->mysqli->error); - return false; + return $this->sqlError('E0035'); } } diff --git a/public/include/classes/notification.class.php b/public/include/classes/notification.class.php index ac61d105..ddf59e9c 100644 --- a/public/include/classes/notification.class.php +++ b/public/include/classes/notification.class.php @@ -27,9 +27,7 @@ class Notification extends Mail { $stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE data = ? AND active = 1 LIMIT 1"); if ($stmt && $stmt->bind_param('s', $data) && $stmt->execute() && $stmt->store_result() && $stmt->num_rows == 1) return true; - // Catchall - // Does not seem to have a notification set - return false; + return $this->sqlError('E0041'); } /** @@ -40,8 +38,7 @@ class Notification extends Mail { $stmt =$this->mysqli->prepare("SELECT id, data FROM $this->table WHERE active = 1 AND type = ?"); if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - // Catchall - return false; + return $this->sqlError('E0042'); } /** @@ -56,9 +53,7 @@ class Notification extends Mail { $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, type, data, active) VALUES (?, ?,?,1)"); if ($stmt && $stmt->bind_param('iss', $account_id, $type, $data) && $stmt->execute()) return true; - $this->debug->append("Failed to add notification for $type with $data: " . $this->mysqli->error); - $this->setErrorMessage("Unable to add new notification " . $this->mysqli->error); - return false; + return $this->sqlError('E0043'); } /** @@ -71,8 +66,7 @@ class Notification extends Mail { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE account_id = ? ORDER BY time DESC"); if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - // Catchall - return false; + return $this->getError(); } /** @@ -91,10 +85,7 @@ class Notification extends Mail { return $aData; } } - // Catchall - $this->setErrorMessage('Unable to fetch notification settings'); - $this->debug->append('Failed fetching notification settings for ' . $account_id . ': ' . $this->mysqli->error); - return false; + return $this->sqlError('E0045'); } /** @@ -108,8 +99,7 @@ class Notification extends Mail { if ($stmt && $stmt->bind_param('s', $strType) && $stmt->execute() && $result = $stmt->get_result()) { return $result->fetch_all(MYSQLI_ASSOC); } - // Catchall - return false; + return $this->sqlError('E0046'); } /** @@ -142,7 +132,7 @@ class Notification extends Mail { } } if ($failed > 0) { - $this->setErrorMessage('Failed to update ' . $failed . ' settings'); + $this->setErrorMessage($this->getErrorMsg('E0047', $failed)); return false; } return true; @@ -183,4 +173,5 @@ $notification->setMysql($mysqli); $notification->setSmarty($smarty); $notification->setConfig($config); $notification->setSetting($setting); +$notification->setErrorCodes($aErrorCodes); ?> diff --git a/public/include/classes/payout.class.php b/public/include/classes/payout.class.php index 65f353bd..d7c2cae1 100644 --- a/public/include/classes/payout.class.php +++ b/public/include/classes/payout.class.php @@ -4,7 +4,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); class Payout Extends Base { - var $table = 'payouts'; + protected $table = 'payouts'; /** * Check if the user has an active payout request already @@ -15,7 +15,7 @@ class Payout Extends Base { $stmt = $this->mysqli->prepare("SELECT id FROM $this->table WHERE completed = 0 AND account_id = ? LIMIT 1"); if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute( )&& $stmt->store_result() && $stmt->num_rows > 0) return true; - return false; + return $this->sqlError('E0048'); } /** @@ -27,7 +27,7 @@ class Payout Extends Base { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE completed = 0"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - return false; + return $this->sqlError('E0050'); } /** @@ -40,9 +40,7 @@ class Payout Extends Base { if ($stmt && $stmt->bind_param('i', $account_id) && $stmt->execute()) { return $stmt->insert_id; } - $this->setErrorMessage('Unable to create new payout request'); - $this->debug->append('Failed to create new payout request in database: ' . $this->mysqli->error); - return false; + return $this->sqlError('E0049'); } /** @@ -54,10 +52,13 @@ class Payout Extends Base { $stmt = $this->mysqli->prepare("UPDATE $this->table SET completed = 1 WHERE id = ?"); if ($stmt && $stmt->bind_param('i', $id) && $stmt->execute()) return true; - return false; + return $this->sqlError('E0051'); } } $oPayout = new Payout(); $oPayout->setDebug($debug); $oPayout->setMysql($mysqli); +$oPayout->setErrorCodes($aErrorCodes); + +?> diff --git a/public/include/classes/roundstats.class.php b/public/include/classes/roundstats.class.php index 6b187973..5e4ee347 100644 --- a/public/include/classes/roundstats.class.php +++ b/public/include/classes/roundstats.class.php @@ -4,41 +4,25 @@ if (!defined('SECURITY')) die('Hacking attempt'); -class RoundStats { - private $sError = ''; +class RoundStats extends Base { private $tableTrans = 'transactions'; private $tableStats = 'statistics_shares'; private $tableBlocks = 'blocks'; private $tableUsers = 'accounts'; - public function __construct($debug, $mysqli, $config) { - $this->debug = $debug; - $this->mysqli = $mysqli; - $this->config = $config; - $this->debug->append("Instantiated RoundStats class", 2); - } - - // get and set methods - private function setErrorMessage($msg) { - $this->sError = $msg; - } - public function getError() { - return $this->sError; - } - /** * Get next block for round stats **/ public function getNextBlock($iHeight=0) { $stmt = $this->mysqli->prepare(" SELECT height - FROM $this->tableBlocks + FROM " . $this->block->getTableName() . " WHERE height > ? ORDER BY height ASC LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->height; - return false; + return $this->sqlError(); } /** @@ -47,13 +31,44 @@ class RoundStats { public function getPreviousBlock($iHeight=0) { $stmt = $this->mysqli->prepare(" SELECT height - FROM $this->tableBlocks + FROM " . $this->block->getTableName() . " WHERE height < ? ORDER BY height DESC LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->height; - return false; + return $this->sqlError(); + } + + /** + * search for block height + **/ + public function searchForBlockHeight($iHeight=0) { + $stmt = $this->mysqli->prepare(" + SELECT height + FROM " . $this->block->getTableName() . " + WHERE height >= ? + ORDER BY height ASC + LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->height; + return $this->sqlError(); + } + + /** + * get next block for stats paging + **/ + public function getNextBlockForStats($iHeight=0, $limit=10) { + $stmt = $this->mysqli->prepare(" + SELECT MAX(x.height) AS height + FROM ( + SELECT height FROM " . $this->block->getTableName() . " + WHERE height >= ? + ORDER BY height ASC LIMIT ? + ) AS x"); + if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iHeight, $limit) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->height; + return $this->sqlError(); } /** @@ -61,17 +76,19 @@ class RoundStats { * @param height int Block Height * @return data array Block information from DB **/ - public function getDetailsForBlockHeight($iHeight=0, $isAdmin=0) { + public function getDetailsForBlockHeight($iHeight=0) { $stmt = $this->mysqli->prepare(" SELECT b.id, height, blockhash, amount, confirmations, difficulty, FROM_UNIXTIME(time) as time, shares, - IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS finder - FROM $this->tableBlocks as b - LEFT JOIN $this->tableUsers AS a ON b.account_id = a.id - WHERE b.height = ? LIMIT 1"); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $isAdmin, $iHeight) && $stmt->execute() && $result = $stmt->get_result()) + IF(a.is_anonymous, 'anonymous', a.username) AS finder, + ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares, + (time - (SELECT time FROM $this->tableBlocks WHERE height < ? ORDER BY height DESC LIMIT 1)) AS round_time + FROM " . $this->block->getTableName() . " as b + LEFT JOIN " . $this->user->getTableName() . " AS a ON b.account_id = a.id + WHERE b.height = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $iHeight) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_assoc(); - return false; + return $this->sqlError(); } /** @@ -79,22 +96,68 @@ class RoundStats { * @param height int Block Height * @return data array Block information from DB **/ - public function getRoundStatsForAccounts($iHeight=0, $isAdmin=0) { + public function getRoundStatsForAccounts($iHeight=0) { $stmt = $this->mysqli->prepare(" SELECT - IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS username, + a.id, + a.username, + a.is_anonymous, s.valid, s.invalid - FROM $this->tableStats AS s - LEFT JOIN $this->tableBlocks AS b ON s.block_id = b.id - LEFT JOIN $this->tableUsers AS a ON a.id = s.account_id + FROM " . $this->statistics->getTableName() . " AS s + LEFT JOIN " . $this->block->getTableName() . " AS b ON s.block_id = b.id + LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = s.account_id WHERE b.height = ? GROUP BY username ASC ORDER BY valid DESC "); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $isAdmin, $iHeight) && $stmt->execute() && $result = $stmt->get_result()) + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) { + while ($row = $result->fetch_assoc()) { + $aData[$row['id']] = $row; + } + return $aData; + } + return $this->sqlError(); + } + + /** + * Get pplns statistics for round block height + * @param height int Block Height + * @return data array Block information from DB + **/ + public function getPPLNSRoundStatsForAccounts($iHeight=0) { + $stmt = $this->mysqli->prepare(" + SELECT + a.username, + a.is_anonymous, + s.pplns_valid, + s.pplns_invalid + FROM " . $this->statistics->getTableName() . " AS s + LEFT JOIN " . $this->block->getTableName() . " AS b ON s.block_id = b.id + LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = s.account_id + WHERE b.height = ? + GROUP BY username ASC + ORDER BY pplns_valid DESC + "); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - return false; + return $this->sqlError(); + } + + /** + * Get total valid pplns shares for block height + **/ + public function getPPLNSRoundShares($iHeight=0) { + $stmt = $this->mysqli->prepare(" + SELECT + SUM(s.pplns_valid) AS pplns_valid + FROM " . $this->statistics->getTableName() . " AS s + LEFT JOIN " . $this->block->getTableName() . " AS b ON s.block_id = b.id + WHERE b.height = ? + "); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->pplns_valid; + return $this->sqlError(); } /** @@ -102,23 +165,25 @@ class RoundStats { * @param height int Block Height * @return data array Block round transactions **/ - public function getAllRoundTransactions($iHeight=0, $admin) { + public function getAllRoundTransactions($iHeight=0) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare(" SELECT t.id AS id, - IF(a.is_anonymous, IF( ? , a.username, 'anonymous'), a.username) AS username, + a.id AS uid, + a.username AS username, + a.is_anonymous, t.type AS type, t.amount AS amount - FROM $this->tableTrans AS t - LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id - LEFT JOIN $this->tableUsers AS a ON t.account_id = a.id - WHERE b.height = ? - ORDER BY id ASC"); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $admin, $iHeight) && $stmt->execute() && $result = $stmt->get_result()) + FROM " . $this->transaction->getTableName() . " AS t + LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id + LEFT JOIN " . $this->user->getTableName() . " AS a ON t.account_id = a.id + WHERE b.height = ? AND t.type = 'Credit' + ORDER BY amount DESC"); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $iHeight) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); $this->debug->append('Unable to fetch transactions'); - return false; + return $this->sqlError(); } /** @@ -135,26 +200,93 @@ class RoundStats { a.username AS username, t.type AS type, t.amount AS amount - FROM $this->tableTrans AS t - LEFT JOIN $this->tableBlocks AS b ON t.block_id = b.id - LEFT JOIN $this->tableUsers AS a ON t.account_id = a.id + FROM " . $this->transaction->getTableName() . " AS t + LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id + LEFT JOIN " . $this->user->getTableName() . " AS a ON t.account_id = a.id WHERE b.height = ? AND a.id = ? ORDER BY id ASC"); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $id) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); $this->debug->append('Unable to fetch transactions'); - return false; + return $this->sqlError(); } - private function checkStmt($bState) { - if ($bState ===! true) { - $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); - $this->setErrorMessage('Internal application Error'); - return false; - } - return true; + /** + * Get ALL last blocks from height for admin panel + **/ + public function getAllReportBlocksFoundHeight($iHeight=0, $limit=10) { + $stmt = $this->mysqli->prepare(" + SELECT + height, shares + FROM " . $this->block->getTableName() . " + WHERE height <= ? + ORDER BY height DESC LIMIT ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iHeight, $limit) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + return $this->sqlError(); } + /** + * Get USER last blocks from height for admin panel + **/ + public function getUserReportBlocksFoundHeight($iHeight=0, $limit=10, $iUser) { + $stmt = $this->mysqli->prepare(" + SELECT + b.height, b.shares + FROM " . $this->block->getTableName() . " AS b + LEFT JOIN " . $this->statistics->getTableName() . " AS s ON s.block_id = b.id + LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = s.account_id + WHERE b.height <= ? AND a.id = ? + ORDER BY height DESC LIMIT ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $iHeight, $iUser, $limit) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + return $this->sqlError(); + } + + /** + * Get shares for block height for user admin panel + **/ + public function getRoundStatsForUser($iHeight=0, $iUser) { + $stmt = $this->mysqli->prepare(" + SELECT + s.valid, + s.invalid, + s.pplns_valid, + s.pplns_invalid + FROM " . $this->statistics->getTableName() . " AS s + LEFT JOIN " . $this->block->getTableName() . " AS b ON s.block_id = b.id + LEFT JOIN " . $this->user->getTableName() . " AS a ON a.id = s.account_id + WHERE b.height = ? AND a.id = ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $iUser) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_assoc(); + return $this->sqlError(); + } + + /** + * Get credit transactions for round block height for admin panel + **/ + public function getUserRoundTransHeight($iHeight=0, $iUser) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare(" + SELECT + IFNULL(t.amount, 0) AS amount + FROM " . $this->transaction->getTableName() . " AS t + LEFT JOIN " . $this->block->getTableName() . " AS b ON t.block_id = b.id + LEFT JOIN " . $this->user->getTableName() . " AS a ON t.account_id = a.id + WHERE b.height = ? AND t.type = 'Credit' AND t.account_id = ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iHeight, $iUser) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->amount; + $this->debug->append('Unable to fetch transactions'); + return $this->sqlError(); + } } -$roundstats = new RoundStats($debug, $mysqli, $config); +$roundstats = new RoundStats(); +$roundstats->setDebug($debug); +$roundstats->setMysql($mysqli); +$roundstats->setConfig($config); +$roundstats->setErrorCodes($aErrorCodes); +$roundstats->setUser($user); +$roundstats->setStatistics($statistics); +$roundstats->setBlock($block); +$roundstats->setTransaction($transaction); diff --git a/public/include/classes/setting.class.php b/public/include/classes/setting.class.php index 56a0d460..82a889af 100644 --- a/public/include/classes/setting.class.php +++ b/public/include/classes/setting.class.php @@ -1,15 +1,10 @@ debug = $debug; - $this->mysqli = $mysqli; - $this->table = 'settings'; - } +class Setting extends Base { + protected $table = 'settings'; /** * Fetch a value from our table @@ -17,18 +12,13 @@ class Setting { * @return value string Value **/ public function getValue($name) { - $query = $this->mysqli->prepare("SELECT value FROM $this->table WHERE name=? LIMIT 1"); - if ($query) { - $query->bind_param('s', $name); - $query->execute(); - $query->bind_result($value); - $query->fetch(); - $query->close(); - } else { - $this->debug->append("Failed to fetch variable $name from $this->table"); - return false; - } - return $value; + $stmt = $this->mysqli->prepare("SELECT value FROM $this->table WHERE name = ? LIMIT 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param('s', $name) && $stmt->execute() && $result = $stmt->get_result()) + if ($result->num_rows > 0) + return $result->fetch_object()->value; + // Log error but return empty string + $this->sqlError(); + return ""; } /** @@ -41,13 +31,14 @@ class Setting { $stmt = $this->mysqli->prepare(" INSERT INTO $this->table (name, value) VALUES (?, ?) - ON DUPLICATE KEY UPDATE value = ? - "); + ON DUPLICATE KEY UPDATE value = ?"); if ($stmt && $stmt->bind_param('sss', $name, $value, $value) && $stmt->execute()) return true; - $this->debug->append("Failed to set $name to $value"); - return false; + return $this->sqlError(); } } $setting = new Setting($debug, $mysqli); +$setting->setDebug($debug); +$setting->setMysql($mysqli); +$setting->setErrorCodes($aErrorCodes); diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 705c41e1..30ab344c 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -4,32 +4,14 @@ if (!defined('SECURITY')) die('Hacking attempt'); -class Share { - private $sError = ''; - private $table = 'shares'; - private $tableArchive = 'shares_archive'; +class Share Extends Base { + protected $table = 'shares'; + protected $tableArchive = 'shares_archive'; private $oUpstream; private $iLastUpstreamId; // This defines each share public $rem_host, $username, $our_result, $upstream_result, $reason, $solution, $time, $difficulty; - public function __construct($debug, $mysqli, $user, $block, $config) { - $this->debug = $debug; - $this->mysqli = $mysqli; - $this->user = $user; - $this->config = $config; - $this->block = $block; - $this->debug->append("Instantiated Share class", 2); - } - - // get and set methods - private function setErrorMessage($msg) { - $this->sError = $msg; - } - public function getError() { - return $this->sError; - } - /** * Fetch archive tables name for this class * @param none @@ -38,13 +20,43 @@ class Share { public function getArchiveTableName() { return $this->tableArchive; } + /** - * Fetch normal table name for this class - * @param none - * @return data string Table name + * Fetch a single share by ID + * @param id int Share ID + * @return array Share data **/ - public function getTableName() { - return $this->table; + public function getShareById($id) { + return $this->getAllAssoc($id); + } + + /** + * Update an entire shares data + **/ + public function updateShareById($id, $data) { + $this->debug->append("STA " . __METHOD__, 4); + $sql = "UPDATE $this->table SET"; + $start = true; + // Remove ID column + unset($data['id']); + foreach ($data as $column => $value) { + $start == true ? $sql .= " $column = ? " : $sql .= ", $column = ?"; + $start = false; + switch($column) { + case 'difficulty': + $this->addParam('d', $value); + break; + default: + $this->addParam('s', $value); + break; + } + } + $sql .= " WHERE id = ? LIMIT 1"; + $this->addParam('i', $id); + $stmt = $this->mysqli->prepare($sql); + if ($this->checkStmt($stmt) && call_user_func_array( array($stmt, 'bind_param'), $this->getParam()) && $stmt->execute()) + return true; + return $this->sqlError(); } /** @@ -52,14 +64,10 @@ class Share { * Used for PPS calculations without moving to archive **/ public function getLastInsertedShareId() { - $stmt = $this->mysqli->prepare(" - SELECT MAX(id) AS id FROM $this->table - "); + $stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->table"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->id; - // Catchall - $this->setErrorMessage('Failed to fetch last inserted share ID'); - return false; + return $this->sqlError(); } /** @@ -75,14 +83,9 @@ class Share { WHERE our_result = 'Y' AND id > ? AND id <= ? "); - if ($this->checkStmt($stmt)) { - $stmt->bind_param('ii', $previous_upstream, $current_upstream); - $stmt->execute(); - $result = $stmt->get_result(); - $stmt->close(); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->total; - } - return false; + return $this->sqlError(); } /** @@ -108,7 +111,7 @@ class Share { "); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - return false; + return $this->sqlError(); } /** @@ -118,19 +121,17 @@ class Share { $stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->table"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->id; - return false; + return $this->sqlError(); } /** * Fetch the highest available share ID from archive **/ function getMaxArchiveShareId() { - $stmt = $this->mysqli->prepare(" - SELECT MAX(share_id) AS share_id FROM $this->tableArchive - "); + $stmt = $this->mysqli->prepare("SELECT MAX(share_id) AS share_id FROM $this->tableArchive"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->share_id; - return false; + return $this->sqlError(); } /** @@ -161,7 +162,7 @@ class Share { } if (is_array($aData)) return $aData; } - return false; + return $this->sqlError(); } /** @@ -185,8 +186,7 @@ class Share { if ($this->checkStmt($stmt) && $stmt->bind_param('i', $this->config['archive']['maxage']) && $stmt->execute()) return true; } - // Catchall - return false; + return $this->sqlError(); } /** @@ -202,20 +202,22 @@ class Share { SELECT id, username, our_result, upstream_result, ?, time, IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS difficulty FROM $this->table WHERE id > ? AND id <= ?"); - if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) { - $archive_stmt->close(); + if ($this->checkStmt($archive_stmt) && $archive_stmt->bind_param('iii', $block_id, $previous_upstream, $current_upstream) && $archive_stmt->execute()) return true; - } - // Catchall - return false; + return $this->sqlError(); } + /** + * Delete accounted shares from shares table + * @param current_upstream int Current highest upstream ID + * @param previous_upstream int Previous upstream ID + * @return bool true or false + **/ public function deleteAccountedShares($current_upstream, $previous_upstream=0) { $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE id > ? AND id <= ?"); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $previous_upstream, $current_upstream) && $stmt->execute()) return true; - // Catchall - return false; + return $this->sqlError(); } /** * Set/get last found share accepted by upstream: id and accounts @@ -229,7 +231,10 @@ class Share { public function getUpstreamFinder() { return @$this->oUpstream->account; } - public function getUpstreamId() { + public function getUpstreamWorker() { + return @$this->oUpstream->worker; + } + public function getUpstreamShareId() { return @$this->oUpstream->id; } /** @@ -240,7 +245,7 @@ class Share { * @param last int Skips all shares up to last to find new share * @return bool **/ - public function setUpstream($aBlock, $last=0) { + public function findUpstreamShare($aBlock, $last=0) { // Many use stratum, so we create our stratum check first $version = pack("I*", sprintf('%08d', $aBlock['version'])); $previousblockhash = pack("H*", swapEndian($aBlock['previousblockhash'])); @@ -252,39 +257,39 @@ class Share { $header_hex = implode(unpack("H*", $header_bin)); // Stratum supported blockhash solution entry - $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1"); + $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE solution = ? LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('s', $aBlock['hash']) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); - $this->share_type = 'startum_blockhash'; - if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + $this->share_type = 'stratum_blockhash'; + if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id)) return true; } // Stratum scrypt hash check $scrypt_hash = swapEndian(bin2hex(Scrypt::calc($header_bin, $header_bin, 1024, 1, 1, 32))); - $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution = ? LIMIT 1"); + $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE solution = ? LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('s', $scrypt_hash) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); - $this->share_type = 'startum_solution'; - if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + $this->share_type = 'stratum_solution'; + if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id)) return true; } // Failed to fetch via startum solution, try pushpoold // Fallback to pushpoold solution type $ppheader = sprintf('%08d', $aBlock['version']) . word_reverse($aBlock['previousblockhash']) . word_reverse($aBlock['merkleroot']) . dechex($aBlock['time']) . $aBlock['bits'] . dechex($aBlock['nonce']); - $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1"); + $stmt = $this->mysqli->prepare("SELECT SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE solution LIKE CONCAT(?, '%') LIMIT 1"); if ($this->checkStmt($stmt) && $stmt->bind_param('s', $ppheader) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); $this->share_type = 'pp_solution'; - if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id)) return true; } // Still no match, try upstream result with timerange $stmt = $this->mysqli->prepare(" SELECT - SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id + SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE upstream_result = 'Y' AND id > ? @@ -294,14 +299,14 @@ class Share { if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $last, $aBlock['time'], $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); $this->share_type = 'upstream_share'; - if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id)) return true; } // We failed again, now we take ANY result matching the timestamp $stmt = $this->mysqli->prepare(" SELECT - SUBSTRING_INDEX( `username` , '.', 1 ) AS account, id + SUBSTRING_INDEX( `username` , '.', 1 ) AS account, username as worker, id FROM $this->table WHERE our_result = 'Y' AND id > ? @@ -310,10 +315,10 @@ class Share { if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $last, $aBlock['time']) && $stmt->execute() && $result = $stmt->get_result()) { $this->oUpstream = $result->fetch_object(); $this->share_type = 'any_share'; - if (!empty($this->oUpstream->account) && is_int($this->oUpstream->id)) + if (!empty($this->oUpstream->account) && !empty($this->oUpstream->worker) && is_int($this->oUpstream->id)) return true; } - // Catchall + $this->setErrorMessage($this->getErrorMsg('E0052', $aBlock['height'])); return false; } @@ -332,21 +337,22 @@ class Share { AND id <= ? AND @total < ? ORDER BY id DESC ) AS b - WHERE total <= ? - "); + WHERE total <= ?"); if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $current_upstream, $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->id; - return false; + return $this->sqlError(); } /** * Fetch the lowest needed share ID from archive **/ function getMinArchiveShareId($iCount) { + // We don't use baseline here to be more accurate + $iCount = $iCount * pow(2, ($this->config['difficulty'] - 16)); $stmt = $this->mysqli->prepare(" SELECT MIN(b.share_id) AS share_id FROM ( - SELECT share_id, @total := @total + (IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) / POW(2, (" . $this->config['difficulty'] . " - 16))) AS total + SELECT share_id, @total := @total + IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty) AS total FROM $this->tableArchive, (SELECT @total := 0) AS a WHERE our_result = 'Y' AND @total < ? @@ -356,20 +362,14 @@ class Share { "); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $iCount, $iCount) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_object()->share_id; - return false; - } - - /** - * Helper function - **/ - private function checkStmt($bState) { - if ($bState ===! true) { - $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); - $this->setErrorMessage('Internal application Error'); - return false; - } - return true; + return $this->sqlError(); } } -$share = new Share($debug, $mysqli, $user, $block, $config); +$share = new Share(); +$share->setDebug($debug); +$share->setMysql($mysqli); +$share->setConfig($config); +$share->setUser($user); +$share->setBlock($block); +$share->setErrorCodes($aErrorCodes); diff --git a/public/include/classes/statistics.class.php b/public/include/classes/statistics.class.php index b607ec0c..fc24e7f9 100644 --- a/public/include/classes/statistics.class.php +++ b/public/include/classes/statistics.class.php @@ -10,31 +10,10 @@ if (!defined('SECURITY')) * Statistics should be non-intrusive and not change any * rows in our database to ensure data integrity for the backend **/ -class Statistics { - private $sError = ''; - private $table = 'statistics_shares'; +class Statistics extends Base { + protected $table = 'statistics_shares'; private $getcache = true; - public function __construct($debug, $mysqli, $config, $share, $user, $block, $memcache) { - $this->debug = $debug; - $this->mysqli = $mysqli; - $this->share = $share; - $this->config = $config; - $this->user = $user; - $this->block = $block; - $this->memcache = $memcache; - $this->debug->append("Instantiated Share class", 2); - } - - /* Some basic get and set methods - **/ - private function setErrorMessage($msg) { - $this->sError = $msg; - } - public function getError() { - return $this->sError; - } - // Disable fetching values from cache public function setGetCache($set=false) { $this->getcache = $set; @@ -43,13 +22,74 @@ class Statistics { return $this->getcache; } - private function checkStmt($bState) { - if ($bState ===! true) { - $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); - $this->setErrorMessage('Failed to prepare statement'); - return false; - } - return true; + /** + * Get our first block found + * + **/ + public function getFirstBlockFound() { + $this->debug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT IFNULL(MIN(time), 0) AS time FROM " . $this->block->getTableName()); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->time; + return false; + } + + /** + * Fetch last found blocks by time + **/ + function getLastBlocksbyTime() { + $this->debug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT + COUNT(id) AS Total, + IFNULL(SUM(IF(confirmations > 0, 1, 0)), 0) AS TotalValid, + IFNULL(SUM(IF(confirmations = -1, 1, 0)), 0) AS TotalOrphan, + IFNULL(SUM(IF(confirmations > 0, difficulty, 0)), 0) AS TotalDifficulty, + IFNULL(ROUND(SUM(IF(confirmations > -1, shares, 0))), 0) AS TotalShares, + IFNULL(ROUND(SUM(IF(confirmations > -1, POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS TotalEstimatedShares, + IFNULL(SUM(IF(confirmations > -1, amount, 0)), 0) AS TotalAmount, + IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), 1, 0)), 0) AS 1HourTotal, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), 1, 0)), 0) AS 1HourValid, + IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), 1, 0)), 0) AS 1HourOrphan, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), difficulty, 0)), 0) AS 1HourDifficulty, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), shares, 0))), 0) AS 1HourShares, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 1HourEstimatedShares, + IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 3600 SECOND), amount, 0)), 0) AS 1HourAmount, + IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), 1, 0)), 0) AS 24HourTotal, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), 1, 0)), 0) AS 24HourValid, + IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), 1, 0)), 0) AS 24HourOrphan, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), difficulty, 0)), 0) AS 24HourDifficulty, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), shares, 0))), 0) AS 24HourShares, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 24HourEstimatedShares, + IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 86400 SECOND), amount, 0)), 0) AS 24HourAmount, + IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), 1, 0)), 0) AS 7DaysTotal, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), 1, 0)), 0) AS 7DaysValid, + IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), 1, 0)), 0) AS 7DaysOrphan, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), difficulty, 0)), 0) AS 7DaysDifficulty, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), shares, 0))), 0) AS 7DaysShares, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 7DaysEstimatedShares, + IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 604800 SECOND), amount, 0)), 0) AS 7DaysAmount, + IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), 1, 0)), 0) AS 4WeeksTotal, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), 1, 0)), 0) AS 4WeeksValid, + IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), 1, 0)), 0) AS 4WeeksOrphan, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), difficulty, 0)), 0) AS 4WeeksDifficulty, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), shares, 0))), 0) AS 4WeeksShares, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 4WeeksEstimatedShares, + IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 2419200 SECOND), amount, 0)), 0) AS 4WeeksAmount, + IFNULL(SUM(IF(FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), 1, 0)), 0) AS 12MonthTotal, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), 1, 0)), 0) AS 12MonthValid, + IFNULL(SUM(IF(confirmations = -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), 1, 0)), 0) AS 12MonthOrphan, + IFNULL(SUM(IF(confirmations > 0 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), difficulty, 0)), 0) AS 12MonthDifficulty, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), shares, 0))), 0) AS 12MonthShares, + IFNULL(ROUND(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty / POW(2, (" . $this->config['difficulty'] . " -16)), 0))), 0) AS 12MonthEstimatedShares, + IFNULL(SUM(IF(confirmations > -1 AND FROM_UNIXTIME(time) >= DATE_SUB(now(), INTERVAL 29030400 SECOND), amount, 0)), 0) AS 12MonthAmount + FROM " . $this->block->getTableName()); + if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + return $this->memcache->setCache(__FUNCTION__, $result->fetch_assoc()); + return $this->sqlError(); } /** @@ -65,18 +105,88 @@ class Statistics { b.*, a.username AS finder, a.is_anonymous AS is_anonymous, - ROUND((difficulty * 65535) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares + ROUND((difficulty * POW(2, 32 - " . $this->config['target_bits'] . ")) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares FROM " . $this->block->getTableName() . " AS b LEFT JOIN " . $this->user->getTableName() . " AS a ON b.account_id = a.id ORDER BY height DESC LIMIT ?"); if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) return $this->memcache->setCache(__FUNCTION__ . $limit, $result->fetch_all(MYSQLI_ASSOC), 5); - // Catchall - $this->debug->append("Failed to find blocks:" . $this->mysqli->error); - return false; + return $this->sqlError(); } + /** + * Get our last $limit blocks found by height + * @param limit int Last limit blocks + * @return array + **/ + public function getBlocksFoundHeight($iHeight=0, $limit=10) { + $this->debug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__ . $iHeight . $limit)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT + b.*, + a.username AS finder, + a.is_anonymous AS is_anonymous, + ROUND((difficulty * POW(2, 32 - " . $this->config['target_bits'] . ")) / POW(2, (" . $this->config['difficulty'] . " -16)), 0) AS estshares + FROM " . $this->block->getTableName() . " AS b + LEFT JOIN " . $this->user->getTableName() . " AS a + ON b.account_id = a.id + WHERE b.height <= ? + ORDER BY height DESC LIMIT ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $iHeight, $limit) && $stmt->execute() && $result = $stmt->get_result()) + return $this->memcache->setCache(__FUNCTION__ . $iHeight . $limit, $result->fetch_all(MYSQLI_ASSOC), 5); + return $this->sqlError(); + } + + /** + * Get SUM of blocks found and generated Coins for each Account + * @param limit int Last limit blocks + * @return array + **/ + public function getBlocksSolvedbyAccount($limit=25) { + $this->debug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__ . $limit)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT + b.*, + a.username AS finder, + a.is_anonymous AS is_anonymous, + COUNT(b.id) AS solvedblocks, + SUM(b.amount) AS generatedcoins + FROM " . $this->block->getTableName() . " AS b + LEFT JOIN " . $this->user->getTableName() . " AS a + ON b.account_id = a.id + WHERE confirmations > 0 + GROUP BY finder + ORDER BY solvedblocks DESC LIMIT ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) + return $this->memcache->setCache(__FUNCTION__ . $limit, $result->fetch_all(MYSQLI_ASSOC), 5); + return $this->sqlError(); + } + + /** + * Get SUM of blocks found and generated Coins for each worker + * @param limit int Last limit blocks + * @return array + **/ + public function getBlocksSolvedbyWorker($account_id, $limit=25) { + $this->debug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__ . $account_id . $limit)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT + worker_name AS finder, + COUNT(id) AS solvedblocks, + SUM(amount) AS generatedcoins + FROM " . $this->block->getTableName() . " + WHERE account_id = ? AND worker_name != 'unknown' + GROUP BY finder + ORDER BY solvedblocks DESC LIMIT ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $limit) && $stmt->execute() && $result = $stmt->get_result()) + return $this->memcache->setCache(__FUNCTION__ . $account_id . $limit, $result->fetch_all(MYSQLI_ASSOC), 5); + return $this->sqlError(); + } + /** * Currently the only function writing to the database * Stored per block user statistics of valid and invalid shares @@ -88,9 +198,41 @@ class Statistics { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, valid, invalid, block_id) VALUES (?, ?, ?, ?)"); if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $aStats['id'], $aStats['valid'], $aStats['invalid'], $iBlockId) && $stmt->execute()) return true; - // Catchall - $this->debug->append("Failed to update share stats: " . $this->mysqli->error); - return false; + return $this->sqlError(); + } + + /** + * update user statistics of valid and invalid pplns shares + **/ + public function updatePPLNSShareStatistics($aStats, $iBlockId) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare(" + UPDATE $this->table SET pplns_valid = ?, pplns_invalid = ? WHERE account_id = ? AND block_id = ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $aStats['valid'], $aStats['invalid'], $aStats['id'], $iBlockId) && $stmt->execute()) return true; + return $this->sqlError(); + } + + /** + * insert user statistics of valid and invalid pplns shares "rbpplns" + **/ + public function insertPPLNSShareStatistics($aStats, $iBlockId) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, valid, invalid, pplns_valid, pplns_invalid, block_id) VALUES (?, 0, 0, ?, ?, ?)"); + if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $aStats['id'], $aStats['valid'], $aStats['invalid'], $iBlockId) && $stmt->execute()) return true; + return $this->sqlError(); + } + + /** + * Fetch the share ID from stats for rbpplns + **/ + function getIdShareStatistics($aStats, $iBlockId) { + $stmt = $this->mysqli->prepare(" + SELECT id AS id FROM $this->table + WHERE account_id = ? AND block_id = ? + "); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aStats['id'], $iBlockId) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_object()->id; + return $this->sqlError(); } /** @@ -106,20 +248,20 @@ class Statistics { SELECT ( ( - SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate + SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0) AS hashrate FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL ? SECOND) + AND our_result = 'Y' ) + ( - SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate + SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0) AS hashrate FROM " . $this->share->getArchiveTableName() . " WHERE time > DATE_SUB(now(), INTERVAL ? SECOND) + AND our_result = 'Y' ) ) AS hashrate FROM DUAL"); - // Catchall if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $interval, $interval, $interval, $interval) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->hashrate); - $this->debug->append("Failed to get hashrate: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -137,17 +279,17 @@ class Statistics { SELECT ROUND(COUNT(id) / ?, 2) AS sharerate FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL ? SECOND) + AND our_result = 'Y' ) + ( SELECT ROUND(COUNT(id) / ?, 2) AS sharerate FROM " . $this->share->getArchiveTableName() . " WHERE time > DATE_SUB(now(), INTERVAL ? SECOND) + AND our_result = 'Y' ) ) AS sharerate FROM DUAL"); if ($this->checkStmt($stmt) && $stmt->bind_param('iiii', $interval, $interval, $interval, $interval) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->sharerate); - // Catchall - $this->debug->append("Failed to fetch share rate: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -179,9 +321,7 @@ class Statistics { WHERE UNIX_TIMESTAMP(time) > IFNULL((SELECT MAX(time) FROM " . $this->block->getTableName() . "), 0)"); if ( $this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(STATISTICS_ROUND_SHARES, $result->fetch_assoc()); - // Catchall - $this->debug->append("Failed to fetch round shares: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -229,9 +369,7 @@ class Statistics { $data['share_id'] = $this->share->getMaxShareId(); return $this->memcache->setCache(STATISTICS_ALL_USER_SHARES, $data); } - // Catchall - $this->debug->append("Unable to fetch all users round shares: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -261,18 +399,15 @@ class Statistics { AND u.id = ?"); if ($stmt && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result()) return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_assoc()); - // Catchall - $this->debug->append("Unable to fetch user round shares: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** * Admin panel specific query - * @return data array invlid and valid shares for all accounts + * @return data array User settings and shares **/ public function getAllUserStats($filter='%') { $this->debug->append("STA " . __METHOD__, 4); - if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $filter)) return $data; $stmt = $this->mysqli->prepare(" SELECT a.id AS id, @@ -281,22 +416,25 @@ class Statistics { a.no_fees as no_fees, a.username AS username, a.donate_percent AS donate_percent, - a.email AS email, - ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares + a.email AS email FROM " . $this->user->getTableName() . " AS a - LEFT JOIN " . $this->share->getTableName() . " AS s - ON a.username = SUBSTRING_INDEX( s.username, '.', 1 ) WHERE a.username LIKE ? GROUP BY username ORDER BY username"); if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) { - return $this->memcache->setCache(__FUNCTION__ . $filter, $result->fetch_all(MYSQLI_ASSOC)); + // Add our cached shares to the users + while ($row = $result->fetch_assoc()) { + $row['shares'] = $this->getUserShares($row['id']); + $aUsers[] = $row; + } + return $aUsers; } + return $this->sqlError(); } /** - * Same as getUserShares for Hashrate + * Fetch total user hashrate based on shares and archived shares * @param account_id integer User ID * @return data integer Current Hashrate in khash/s **/ @@ -305,27 +443,71 @@ class Statistics { if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; $stmt = $this->mysqli->prepare(" SELECT - ( - SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate - FROM " . $this->share->getTableName() . " AS s, - " . $this->user->getTableName() . " AS u - WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) - AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) - AND u.id = ? - ) + ( - SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / ? / 1000), 0) AS hashrate - FROM " . $this->share->getArchiveTableName() . " AS s, - " . $this->user->getTableName() . " AS u - WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) - AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) - AND u.id = ? - ) AS hashrate - FROM DUAL"); - if ($this->checkStmt($stmt) && $stmt->bind_param("iiiiii", $interval, $interval, $account_id, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() ) + IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate + FROM ( + SELECT + s.id, s.our_result, IF(s.difficulty = 0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty) AS difficulty + FROM + shares AS s, + accounts AS u + WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) + AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) + AND s.our_result = 'Y' + AND u.id = ? + UNION + SELECT + s.share_id, s.our_result, IF(s.difficulty = 0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty) AS difficulty + FROM + shares_archive AS s, + accounts AS u + WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) + AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) + AND s.our_result = 'Y' + AND u.id = ? + ) AS temp"); + if ($this->checkStmt($stmt) && $stmt->bind_param("iiiii", $interval, $interval, $account_id, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->hashrate); - // Catchall - $this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error); - return false; + return $this->sqlError(); + } + + public function getUserUnpaidPPSShares($account_id, $last_paid_pps_id) { + $this->debug->append("STA " . __METHOD__, 4); + if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT + ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS total + FROM " . $this->share->getTableName() . " AS s + JOIN " . $this->user->getTableName() . " AS a + ON a.username = SUBSTRING_INDEX( s.username, '.', 1 ) + AND a.id = ? + AND s.id > ? + WHERE our_result = 'Y'"); + if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $account_id, $last_paid_pps_id) && $stmt->execute() && $result = $stmt->get_result() ) + return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->total); + return $this->sqlError(); + } + + /** + * Get average share difficulty across all workers for user + * @param account_id int Account ID + * @param interval int Data interval in seconds + * @return double Share difficulty or 0 + **/ + public function getUserShareDifficulty($account_id, $interval=600) { + $this->debug->append("STA " . __METHOD__, 4); + if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT + IFNULL(AVG(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 0) AS avgsharediff, + COUNT(s.id) AS total + FROM " . $this->share->getTableName() . " AS s JOIN " . $this->user->getTableName() . " AS a + ON a.username = SUBSTRING_INDEX( s.username, '.', 1 ) + WHERE s.time > DATE_SUB(now(), INTERVAL ? SECOND) + AND our_result = 'Y' + AND a.id = ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param("ii", $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() ) + return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->avgsharediff); + return $this->sqlError(); } /** @@ -338,29 +520,31 @@ class Statistics { if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; $stmt = $this->mysqli->prepare(" SELECT - ( - ( - SELECT COUNT(s.id) / ? AS sharerate - FROM " . $this->share->getTableName() . " AS s, - " . $this->user->getTableName() . " AS u - WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) - AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) - AND u.id = ? - ) + ( - SELECT COUNT(s.id) / ? AS sharerate - FROM " . $this->share->getArchiveTableName() . " AS s, - " . $this->user->getTableName() . " AS u - WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) - AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) - AND u.id = ? - ) - ) AS sharerate - FROM DUAL"); - if ($this->checkStmt($stmt) && $stmt->bind_param("iiiiii", $interval, $interval, $account_id, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() ) + IFNULL(COUNT(*) / ?, 0) AS sharerate + FROM ( + SELECT + s.id + FROM + shares AS s, + accounts AS u + WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) + AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) + AND s.our_result = 'Y' + AND u.id = ? + UNION + SELECT + s.share_id + FROM + shares_archive AS s, + accounts AS u + WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) + AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) + AND s.our_result = 'Y' + AND u.id = ? + ) AS temp"); + if ($this->checkStmt($stmt) && $stmt->bind_param("iiiii", $interval, $interval, $account_id, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__ . $account_id, $result->fetch_object()->sharerate); - // Catchall - $this->debug->append("Failed to fetch sharerate: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -372,17 +556,16 @@ class Statistics { $this->debug->append("STA " . __METHOD__, 4); if ($data = $this->memcache->get(__FUNCTION__ . $worker_id)) return $data; $stmt = $this->mysqli->prepare(" - SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536 / 600 / 1000), 0) AS hashrate + SELECT IFNULL(ROUND(SUM(IF(difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 600 / 1000), 0) AS hashrate FROM " . $this->share->getTableName() . " AS s, " . $this->user->getTableName() . " AS u WHERE u.username = SUBSTRING_INDEX( s.username, '.', 1 ) + AND our_result = 'Y' AND s.time > DATE_SUB(now(), INTERVAL 600 SECOND) AND u.id = ?"); if ($this->checkStmt($stmt) && $stmt->bind_param("i", $account_id) && $stmt->execute() && $result = $stmt->get_result() ) return $this->memcache->setCache(__FUNCTION__ . $worker_id, $result->fetch_object()->hashrate); - // Catchall - $this->debug->append("Failed to fetch hashrate: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -397,28 +580,32 @@ class Statistics { switch ($type) { case 'shares': if ($data = $this->memcache->get(STATISTICS_ALL_USER_SHARES)) { - // Use global cache to build data - $max = 0; - foreach($data['data'] as $key => $aUser) { - $shares[$key] = $aUser['valid']; - $username[$key] = $aUser['username']; + // Use global cache to build data, if we have any data there + if (!empty($data['data']) && is_array($data['data'])) { + foreach($data['data'] as $key => $aUser) { + $shares[$key] = $aUser['valid']; + $username[$key] = $aUser['username']; + } + array_multisort($shares, SORT_DESC, $username, SORT_ASC, $data['data']); + $count = 0; + foreach ($data['data'] as $key => $aUser) { + if ($count == $limit) break; + $count++; + $data_new[$key]['shares'] = $aUser['valid']; + $data_new[$key]['account'] = $aUser['username']; + $data_new[$key]['donate_percent'] = $aUser['donate_percent']; + $data_new[$key]['is_anonymous'] = $aUser['is_anonymous']; + } + return $data_new; } - array_multisort($shares, SORT_DESC, $username, SORT_ASC, $data['data']); - foreach ($data['data'] as $key => $aUser) { - $data_new[$key]['shares'] = $aUser['valid']; - $data_new[$key]['account'] = $aUser['username']; - $data_new[$key]['donate_percent'] = $aUser['donate_percent']; - $data_new[$key]['is_anonymous'] = $aUser['is_anonymous']; - } - return $data_new; } // No cached data, fallback to SQL and cache in local cache $stmt = $this->mysqli->prepare(" SELECT + a.username AS account, a.donate_percent AS donate_percent, a.is_anonymous AS is_anonymous, - ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares, - SUBSTRING_INDEX( s.username, '.', 1 ) AS account + ROUND(IFNULL(SUM(IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)), 0) / POW(2, (" . $this->config['difficulty'] . " - 16)), 0) AS shares FROM " . $this->share->getTableName() . " AS s LEFT JOIN " . $this->user->getTableName() . " AS a ON SUBSTRING_INDEX( s.username, '.', 1 ) = a.username @@ -428,22 +615,21 @@ class Statistics { LIMIT ?"); if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC)); - $this->debug->append("Fetching shares failed: "); - return false; + return $this->sqlError(); break; case 'hashes': $stmt = $this->mysqli->prepare(" SELECT + a.username AS account, a.donate_percent AS donate_percent, a.is_anonymous AS is_anonymous, - IFNULL(ROUND(SUM(t1.difficulty) * 65536/600/1000, 2), 0) AS hashrate, - SUBSTRING_INDEX( t1.username, '.', 1 ) AS account + IFNULL(ROUND(SUM(t1.difficulty) * POW(2, " . $this->config['target_bits'] . ") / 600 / 1000, 2), 0) AS hashrate FROM ( - SELECT IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y' - UNION ALL - SELECT IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getArchiveTableName() ." WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y' + SELECT id, IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y' + UNION + SELECT share_id, IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, username FROM " . $this->share->getArchiveTableName() ." WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE) AND our_result = 'Y' ) AS t1 LEFT JOIN " . $this->user->getTableName() . " AS a ON SUBSTRING_INDEX( t1.username, '.', 1 ) = a.username @@ -451,8 +637,7 @@ class Statistics { ORDER BY hashrate DESC LIMIT ?"); if ($this->checkStmt($stmt) && $stmt->bind_param("i", $limit) && $stmt->execute() && $result = $stmt->get_result()) return $this->memcache->setCache(__FUNCTION__ . $type . $limit, $result->fetch_all(MYSQLI_ASSOC)); - $this->debug->append("Fetching shares failed: " . $this->mysqli->error); - return false; + return $this->sqlError(); break; } } @@ -467,20 +652,24 @@ class Statistics { if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; $stmt = $this->mysqli->prepare(" SELECT - IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate, + a.id, + IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate, HOUR(s.time) AS hour FROM " . $this->share->getTableName() . " AS s, accounts AS a WHERE time < NOW() - INTERVAL 1 HOUR + AND our_result = 'Y' AND time > NOW() - INTERVAL 25 HOUR AND a.username = SUBSTRING_INDEX( s.username, '.', 1 ) AND a.id = ? GROUP BY HOUR(time) - UNION ALL + UNION SELECT - IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate, + share_id, + IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate, HOUR(s.time) AS hour FROM " . $this->share->getArchiveTableName() . " AS s, accounts AS a WHERE time < NOW() - INTERVAL 1 HOUR + AND our_result = 'Y' AND time > NOW() - INTERVAL 25 HOUR AND a.username = SUBSTRING_INDEX( s.username, '.', 1 ) AND a.id = ? @@ -493,9 +682,7 @@ class Statistics { while ($row = $result->fetch_assoc()) $aData[$row['hour']] = $row['hashrate']; return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData); } - // Catchall - $this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -508,19 +695,23 @@ class Statistics { if ($this->getGetCache() && $data = $this->memcache->get(__FUNCTION__)) return $data; $stmt = $this->mysqli->prepare(" SELECT - IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate, + id, + IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate, HOUR(s.time) AS hour FROM " . $this->share->getTableName() . " AS s WHERE time < NOW() - INTERVAL 1 HOUR AND time > NOW() - INTERVAL 25 HOUR + AND our_result = 'Y' GROUP BY HOUR(time) - UNION ALL + UNION SELECT - IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * 65536/3600/1000), 0) AS hashrate, + share_id, + IFNULL(ROUND(SUM(IF(s.difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty)) * POW(2, " . $this->config['target_bits'] . ") / 3600 / 1000), 0) AS hashrate, HOUR(s.time) AS hour FROM " . $this->share->getArchiveTableName() . " AS s WHERE time < NOW() - INTERVAL 1 HOUR AND time > NOW() - INTERVAL 25 HOUR + AND our_result = 'Y' GROUP BY HOUR(time)"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) { $iStartHour = date('G'); @@ -530,35 +721,95 @@ class Statistics { while ($row = $result->fetch_assoc()) $aData[$row['hour']] = (int) $row['hashrate']; return $this->memcache->setCache(__FUNCTION__, $aData); } - // Catchall - $this->debug->append("Failed to fetch hourly hashrate: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** * get user estimated payouts based on share counts - * @param aRoundShares array Round shares - * @param aUserShares array User shares + * @param value1 mixed Round shares OR share rate + * @param value2 mixed User shares OR share difficulty * @param dDonate double User donation setting * @param bNoFees bool User no-fees option setting * @return aEstimates array User estimations **/ - public function getUserEstimates($aRoundShares, $aUserShares, $dDonate, $bNoFees) { + public function getUserEstimates($value1, $value2, $dDonate, $bNoFees, $ppsvalue=0) { $this->debug->append("STA " . __METHOD__, 4); - // Fetch some user information that we need - if (@$aRoundShares['valid'] > 0 && @$aUserShares['valid'] > 0) { - $aEstimates['block'] = round(( (int)$aUserShares['valid'] / (int)$aRoundShares['valid'] ) * (float)$this->config['reward'], 8); - $bNoFees == 0 ? $aEstimates['fee'] = round(((float)$this->config['fees'] / 100) * (float)$aEstimates['block'], 8) : $aEstimates['fee'] = 0; - $aEstimates['donation'] = round((( (float)$dDonate / 100) * ((float)$aEstimates['block'] - (float)$aEstimates['fee'])), 8); - $aEstimates['payout'] = round((float)$aEstimates['block'] - (float)$aEstimates['donation'] - (float)$aEstimates['fee'], 8); + if ($this->config['payout_system'] != 'pps') { + if (@$value1['valid'] > 0 && @$value2['valid'] > 0) { + $aEstimates['block'] = round(( (int)$value2['valid'] / (int)$value1['valid'] ) * (float)$this->config['reward'], 8); + $bNoFees == 0 ? $aEstimates['fee'] = round(((float)$this->config['fees'] / 100) * (float)$aEstimates['block'], 8) : $aEstimates['fee'] = 0; + $aEstimates['donation'] = round((( (float)$dDonate / 100) * ((float)$aEstimates['block'] - (float)$aEstimates['fee'])), 8); + $aEstimates['payout'] = round((float)$aEstimates['block'] - (float)$aEstimates['donation'] - (float)$aEstimates['fee'], 8); + } else { + $aEstimates['block'] = 0; + $aEstimates['fee'] = 0; + $aEstimates['donation'] = 0; + $aEstimates['payout'] = 0; + } } else { - $aEstimates['block'] = 0; - $aEstimates['fee'] = 0; - $aEstimates['donation'] = 0; - $aEstimates['payout'] = 0; + // Hack so we can use this method for PPS estimates too + // value1 = shares/s + // value2 = avg share difficulty + if (@$value1 > 0 && @$value2 > 0) { + $hour = 60 * 60; + $pps = $value1 * $value2 * $ppsvalue; + $aEstimates['hours1'] = $pps * $hour; + $aEstimates['hours24'] = $pps * 24 * $hour; + $aEstimates['days7'] = $pps * 24 * 7 * $hour; + $aEstimates['days14'] = $pps * 14 * 24 * $hour; + $aEstimates['days30'] = $pps * 30 * 24 * $hour; + } else { + $aEstimates['hours1'] = 0; + $aEstimates['hours24'] = 0; + $aEstimates['days7'] = 0; + $aEstimates['days14'] = 0; + $aEstimates['days30'] = 0; + } } return $aEstimates; } + + /** + * Get pool stats last 24 hours + * @param limit int Last number of hours + * @return array + **/ + public function getPoolStatsHours($hour=24) { + $this->debug->append("STA " . __METHOD__, 4); + if ($data = $this->memcache->get(__FUNCTION__ . $hour)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT + IFNULL(COUNT(id), 0) as count, + IFNULL(AVG(difficulty), 0) as average, + IFNULL(ROUND(SUM((POW(2, ( 32 - " . $this->config['target_bits'] . " )) * difficulty) / POW(2, (" . $this->config['difficulty'] . " -16))), 0), 0) AS expected, + IFNULL(ROUND(SUM(shares)), 0) as shares, + IFNULL(SUM(amount), 0) as rewards + FROM " . $this->block->getTableName() . " + WHERE FROM_UNIXTIME(time) > DATE_SUB(now(), INTERVAL ? HOUR) + AND confirmations >= 1"); + if ($this->checkStmt($stmt) && $stmt->bind_param("i", $hour) && $stmt->execute() && $result = $stmt->get_result()) + return $this->memcache->setCache(__FUNCTION__ . $hour, $result->fetch_assoc()); + return $this->sqlError(); + } + + /** + * Caclulate estimated shares based on network difficulty and pool difficulty + * @param dDiff double Network difficulty + * @return shares integer Share count + **/ + public function getEstimatedShares($dDiff) { + return round((POW(2, (32 - $this->config['target_bits'])) * $dDiff) / pow(2, ($this->config['difficulty'] - 16))); + } } -$statistics = new Statistics($debug, $mysqli, $config, $share, $user, $block, $memcache); +$statistics = new Statistics(); +$statistics->setDebug($debug); +$statistics->setMysql($mysqli); +$statistics->setShare($share); +$statistics->setUser($user); +$statistics->setBlock($block); +$statistics->setMemcache($memcache); +$statistics->setConfig($config); +$statistics->setErrorCodes($aErrorCodes); + +?> diff --git a/public/include/classes/token.class.php b/public/include/classes/token.class.php index c65da13c..42b07a2c 100644 --- a/public/include/classes/token.class.php +++ b/public/include/classes/token.class.php @@ -4,7 +4,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); class Token Extends Base { - var $table = 'tokens'; + protected $table = 'tokens'; /** * Fetch a token from our table @@ -15,7 +15,7 @@ class Token Extends Base { $stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE token = ? LIMIT 1"); if ($stmt && $stmt->bind_param('s', $strToken) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_assoc(); - return false; + return $this->sqlError(); } /** @@ -36,9 +36,7 @@ class Token Extends Base { "); if ($stmt && $stmt->bind_param('sii', $strToken, $iToken_id, $account_id) && $stmt->execute()) return $strToken; - $this->setErrorMessage('Unable to create new token'); - $this->debug->append('Failed to create new token in database: ' . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -50,7 +48,7 @@ class Token Extends Base { $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE token = ? LIMIT 1"); if ($stmt && $stmt->bind_param('s', $token) && $stmt->execute()) return true; - return false; + return $this->sqlError(); } } @@ -58,3 +56,4 @@ $oToken = new Token(); $oToken->setDebug($debug); $oToken->setMysql($mysqli); $oToken->setTokenType($tokentype); +$oToken->setErrorCodes($aErrorCodes); diff --git a/public/include/classes/tokentype.class.php b/public/include/classes/tokentype.class.php index d33356cb..f4f238ee 100644 --- a/public/include/classes/tokentype.class.php +++ b/public/include/classes/tokentype.class.php @@ -5,7 +5,8 @@ if (!defined('SECURITY')) die('Hacking attempt'); class Token_Type Extends Base { - var $table = 'token_types'; + protected $table = 'token_types'; + /** * Return ID for specific token * @param strName string Token Name @@ -19,3 +20,4 @@ class Token_Type Extends Base { $tokentype = new Token_Type(); $tokentype->setDebug($debug); $tokentype->setMysql($mysqli); +$tokentype->setErrorCodes($aErrorCodes); diff --git a/public/include/classes/tools.class.php b/public/include/classes/tools.class.php index dcfa1959..1cf4f875 100644 --- a/public/include/classes/tools.class.php +++ b/public/include/classes/tools.class.php @@ -17,7 +17,7 @@ class Tools extends Base { * @param auth array Optional authentication data to be sent with * @return dec array JSON decoded PHP array **/ - private function getApi($url, $target, $auth=NULL) { + public function getApi($url, $target, $auth=NULL) { static $ch = null; static $ch = null; if (is_null($ch)) { @@ -32,9 +32,15 @@ class Tools extends Base { // run the query $res = curl_exec($ch); - if ($res === false) throw new Exception('Could not get reply: '.curl_error($ch)); + if ($res === false) { + $this->setErrorMessage('Could not get reply: '.curl_error($ch)); + return false; + } $dec = json_decode($res, true); - if (!$dec) throw new Exception('Invalid data received, please make sure connection is working and requested API exists'); + if (!$dec) { + $this->setErrorMessage('Invalid data received, please make sure connection is working and requested API exists'); + return false; + } return $dec; } diff --git a/public/include/classes/transaction.class.php b/public/include/classes/transaction.class.php index 53d111e3..432dee46 100644 --- a/public/include/classes/transaction.class.php +++ b/public/include/classes/transaction.class.php @@ -5,7 +5,7 @@ if (!defined('SECURITY')) die('Hacking attempt'); class Transaction extends Base { - private $sError = '', $table = 'transactions'; + protected $table = 'transactions'; public $num_rows = 0, $insert_id = 0; /** @@ -18,14 +18,13 @@ class Transaction extends Base { * @param coin_address string Coin address for this transaction [optional] * @return bool **/ - public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL) { - $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address) VALUES (?, ?, ?, ?, ?)"); - if ($this->checkStmt($stmt) && $stmt->bind_param("idiss", $account_id, $amount, $block_id, $type, $coin_address) && $stmt->execute()) { + public function addTransaction($account_id, $amount, $type='Credit', $block_id=NULL, $coin_address=NULL, $txid=NULL) { + $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, amount, block_id, type, coin_address, txid) VALUES (?, ?, ?, ?, ?, ?)"); + if ($this->checkStmt($stmt) && $stmt->bind_param("idisss", $account_id, $amount, $block_id, $type, $coin_address, $txid) && $stmt->execute()) { $this->insert_id = $stmt->insert_id; return true; } - $this->setErrorMessage("Failed to store transaction"); - return false; + return $this->sqlError(); } /* @@ -35,16 +34,20 @@ class Transaction extends Base { * @param bool boolean True or False **/ public function setArchived($account_id, $txid) { + // Update all paid out transactions as archived $stmt = $this->mysqli->prepare(" UPDATE $this->table AS t LEFT JOIN " . $this->block->getTableName() . " AS b ON b.id = t.block_id SET t.archived = 1 - WHERE ( t.account_id = ? AND t.id <= ? AND b.confirmations >= ? ) - OR ( t.account_id = ? AND t.id <= ? AND t.type IN ( 'Credit_PPS', 'Donation_PPS', 'Fee_PPS', 'TXFee', 'Debit_MP', 'Debit_AP' ) )"); - if ($this->checkStmt($stmt) && $stmt->bind_param('iiiii', $account_id, $txid, $this->config['confirmations'], $account_id, $txid) && $stmt->execute()) + WHERE t.archived = 0 + AND ( + ( t.account_id = ? AND t.id <= ? AND b.confirmations >= ? ) + OR ( t.account_id = ? AND t.id <= ? AND t.type IN ( 'Credit_PPS', 'Donation_PPS', 'Fee_PPS', 'TXFee', 'Debit_MP', 'Debit_AP' ) ) + )"); + if ($this->checkStmt($stmt) && $stmt->bind_param('iiiii', $account_id, $txid, $this->config['confirmations'], $account_id, $txid) && $stmt->execute()) return true; - return false; + return $this->sqlError(); } /** @@ -53,9 +56,16 @@ class Transaction extends Base { * @return data array type and total **/ public function getTransactionSummary($account_id=NULL) { - $sql = "SELECT SUM(t.amount) AS total, t.type AS type FROM $this->table AS t"; + if ($data = $this->memcache->get(__FUNCTION__ . $account_id)) return $data; + $sql = " + SELECT + SUM(t.amount) AS total, t.type AS type + FROM transactions AS t + LEFT OUTER JOIN blocks AS b + ON b.id = t.block_id + WHERE ( b.confirmations > 0 OR b.id IS NULL )"; if (!empty($account_id)) { - $sql .= " WHERE t.account_id = ? "; + $sql .= " AND t.account_id = ? "; $this->addParam('i', $account_id); } $sql .= " GROUP BY t.type"; @@ -74,9 +84,10 @@ class Transaction extends Base { while ($row = $result->fetch_assoc()) { $aData[$row['type']] = $row['total']; } - return $aData; + // Cache data for a while, query takes long on many rows + return $this->memcache->setCache(__FUNCTION__ . $account_id, $aData, 60); } - return false; + return $this->sqlError(); } /** @@ -98,6 +109,7 @@ class Transaction extends Base { t.amount AS amount, t.coin_address AS coin_address, t.timestamp AS timestamp, + t.txid AS txid, b.height AS height, b.blockhash AS blockhash, b.confirmations AS confirmations @@ -144,7 +156,7 @@ class Transaction extends Base { } } if (!empty($aFilter)) { - empty($account_id) ? $sql .= " WHERE " : $sql .= " AND "; + empty($account_id) ? $sql .= " WHERE " : $sql .= " AND "; $sql .= implode(' AND ', $aFilter); } } @@ -163,8 +175,7 @@ class Transaction extends Base { $this->num_rows = $row_count; return $result->fetch_all(MYSQLI_ASSOC); } - $this->debug->append('Failed to fetch transactions: ' . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -180,8 +191,7 @@ class Transaction extends Base { } return $aData; } - $this->debug->append('Failed to fetch transaction types: ' . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -208,11 +218,11 @@ class Transaction extends Base { t.type = 'Donation_PPS' ) GROUP BY a.username + ORDER BY donation DESC "); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - $this->debug->append("Failed to fetch website donors: " . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -236,10 +246,7 @@ class Transaction extends Base { WHERE archived = 0"); if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['confirmations'], $this->config['confirmations']) && $stmt->execute() && $stmt->bind_result($dBalance) && $stmt->fetch()) return $dBalance; - // Catchall - $this->setErrorMessage('Unable to find locked credits for all users'); - $this->debug->append('MySQL query failed : ' . $this->mysqli->error); - return false; + return $this->sqlError(); } /** @@ -272,14 +279,17 @@ class Transaction extends Base { "); if ($this->checkStmt($stmt) && $stmt->bind_param("iiiii", $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $this->config['confirmations'], $account_id) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_assoc(); - $this->debug->append('Failed to fetch users balance: ' . $this->mysqli->error); - return false; + return $this->sqlError(); } } $transaction = new Transaction(); +$transaction->setMemcache($memcache); $transaction->setDebug($debug); $transaction->setMysql($mysqli); $transaction->setConfig($config); $transaction->setBlock($block); $transaction->setUser($user); +$transaction->setErrorCodes($aErrorCodes); + +?> diff --git a/public/include/classes/user.class.php b/public/include/classes/user.class.php index df8969c1..e68ae854 100644 --- a/public/include/classes/user.class.php +++ b/public/include/classes/user.class.php @@ -4,39 +4,12 @@ if (!defined('SECURITY')) die('Hacking attempt'); -class User { - private $sError = ''; +class User extends Base { + protected $table = 'accounts'; private $userID = false; - private $table = 'accounts'; private $user = array(); - public function __construct($debug, $mysqli, $salt, $config) { - $this->debug = $debug; - $this->mysqli = $mysqli; - $this->salt = $salt; - $this->config = $config; - $this->debug->append("Instantiated User class", 2); - } - // get and set methods - public function setMail($mail) { - $this->mail = $mail; - } - public function setToken($token) { - $this->token = $token; - } - public function setBitcoin($bitcoin) { - $this->bitcoin = $bitcoin; - } - public function setSetting($setting) { - $this->setting = $setting; - } - private function setErrorMessage($msg) { - $this->sError = $msg; - } - public function getError() { - return $this->sError; - } private function getHash($string) { return hash('sha256', $string.$this->salt); } @@ -174,31 +147,6 @@ class User { return $pin_hash === $row_pin; } - /** - * Get a single row from the table - * @param value string Value to search for - * @param search Return column to search for - * @param field string Search column - * @param type string Type of value - * @return array Return result - **/ - private function getSingle($value, $search='id', $field='id', $type="i", $lower=false) { - $this->debug->append("STA " . __METHOD__, 4); - $sql = "SELECT $search FROM $this->table WHERE"; - $lower ? $sql .= " LOWER($field) = LOWER(?)" : $sql .= " $field = ?"; - $sql .= " LIMIT 1"; - $stmt = $this->mysqli->prepare($sql); - if ($this->checkStmt($stmt)) { - $stmt->bind_param($type, $value); - $stmt->execute(); - $stmt->bind_result($retval); - $stmt->fetch(); - $stmt->close(); - return $retval; - } - return false; - } - /** * Get all users that have auto payout setup * @param none @@ -243,31 +191,6 @@ class User { return $dPercent; } - /** - * Update a single row in a table - * @param userID int Account ID - * @param field string Field to update - * @return bool - **/ - private function updateSingle($id, $field) { - $this->debug->append("STA " . __METHOD__, 4); - $stmt = $this->mysqli->prepare("UPDATE $this->table SET `" . $field['name'] . "` = ? WHERE id = ? LIMIT 1"); - if ($this->checkStmt($stmt) && $stmt->bind_param($field['type'].'i', $field['value'], $id) && $stmt->execute()) - return true; - $this->debug->append("Unable to update " . $field['name'] . " with " . $field['value'] . " for ID $id"); - return false; - } - - private function checkStmt($bState) { - $this->debug->append("STA " . __METHOD__, 4); - if ($bState ===! true) { - $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); - $this->setErrorMessage('Internal application Error'); - return false; - } - return true; - } - /** * Update the accounts password * @param userID int User ID @@ -438,12 +361,34 @@ class User { // Enforce generation of a new Session ID and delete the old session_regenerate_id(true); // Enforce a page reload and point towards login with referrer included, if supplied - $location = @$_SERVER['HTTPS'] ? 'https' . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] : 'http' . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF']; + $port = ($_SERVER["SERVER_PORT"] == "80" or $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]); + $location = @$_SERVER['HTTPS'] ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $_SERVER['PHP_SELF'] : 'http://' . $_SERVER['SERVER_NAME'] . $port . $_SERVER['PHP_SELF']; if (!empty($from)) $location .= '?page=login&to=' . urlencode($from); // if (!headers_sent()) header('Location: ' . $location); exit(''); } + /** + * Get all users for admin panel + **/ + public function getAllUsers($filter='%') { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare(" + SELECT + a.id AS id, + a.username AS username + FROM " . $this->getTableName() . " AS a + WHERE a.username LIKE ? + GROUP BY username"); + if ($this->checkStmt($stmt) && $stmt->bind_param('s', $filter) && $stmt->execute() && $result = $stmt->get_result()) { + while ($row = $result->fetch_assoc()) { + $aData[$row['id']] = $row['username']; + } + return $aData; + } + return false; + } + /** * Fetch this classes table name * @return table string This classes table name @@ -493,7 +438,7 @@ class User { **/ public function register($username, $password1, $password2, $pin, $email1='', $email2='', $strToken='') { $this->debug->append("STA " . __METHOD__, 4); - if (strlen($username > 40)) { + if (strlen($username) > 40) { $this->setErrorMessage('Username exceeding character limit'); return false; } @@ -687,7 +632,11 @@ class User { } // Make our class available automatically -$user = new User($debug, $mysqli, SALT, $config); +$user = new User(); +$user->setDebug($debug); +$user->setMysql($mysqli); +$user->setSalt(SALT); +$user->setConfig($config); $user->setMail($mail); $user->setToken($oToken); $user->setBitcoin($bitcoin); diff --git a/public/include/classes/worker.class.php b/public/include/classes/worker.class.php index 122d287d..0caa9f4b 100644 --- a/public/include/classes/worker.class.php +++ b/public/include/classes/worker.class.php @@ -1,38 +1,10 @@ debug = $debug; - $this->mysqli = $mysqli; - $this->user = $user; - $this->share = $share; - $this->config = $config; - $this->debug->append("Instantiated Worker class", 2); - } - - // get and set methods - private function setErrorMessage($msg) { - $this->sError = $msg; - } - public function getError() { - return $this->sError; - } - - private function checkStmt($bState) { - if ($bState ===! true) { - $this->debug->append("Failed to prepare statement: " . $this->mysqli->error); - $this->setErrorMessage('Internal application Error'); - return false; - } - return true; - } +class Worker extends Base { + protected $table = 'pool_worker'; /** * Update worker list for a user @@ -61,9 +33,7 @@ class Worker { } if ($iFailed == 0) return true; - // Catchall - $this->setErrorMessage('Failed to update ' . $iFailed . ' worker.'); - return false; + return $this->sqlError('E0053', $iFailed); } /** @@ -71,20 +41,21 @@ class Worker { * @param none * @return data array Workers in IDLE state and monitoring enabled **/ - public function getAllIdleWorkers() { + public function getAllIdleWorkers($interval=600) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare(" - SELECT account_id, id, username - FROM " . $this->table . " AS w - WHERE monitor = 1 - AND ( - SELECT IFNULL(SUM(IF(our_result = 'Y', 1, 0)), 0) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) - ) = 0"); - if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) + SELECT w.account_id AS account_id, w.id AS id, w.username AS username + FROM " . $this->share->getTableName() . " AS s + RIGHT JOIN " . $this->getTableName() . " AS w + ON w.username = s.username + AND s.time > DATE_SUB(now(), INTERVAL ? SECOND) + AND our_result = 'Y' + WHERE w.monitor = 1 + AND s.id IS NULL + "); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $interval) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - // Catchall - $this->setErrorMessage("Unable to fetch IDLE, monitored workers"); - return false; + return $this->sqlError('E0054'); } /** @@ -92,7 +63,7 @@ class Worker { * @param id int Worker ID * @return mixed array Worker details **/ - public function getWorker($id) { + public function getWorker($id, $interval=600) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare(" SELECT id, username, password, monitor, @@ -100,35 +71,34 @@ class Worker { ( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive, ( SELECT - IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate + IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate FROM " . $this->share->getTableName() . " WHERE username = w.username - AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) + AND time > DATE_SUB(now(), INTERVAL ? SECOND) ) + ( SELECT - IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate + IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username - AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) + AND time > DATE_SUB(now(), INTERVAL ? SECOND) ) AS hashrate, ( SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0) FROM " . $this->share->getTableName() . " - WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) + WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) ) + ( SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0) FROM " . $this->share->getArchiveTableName() . " - WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) + WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) ) AS difficulty FROM $this->table AS w WHERE id = ? "); - if ($this->checkStmt($stmt) && $stmt->bind_param('i', $id) && $stmt->execute() && $result = $stmt->get_result()) + if ($this->checkStmt($stmt) && $stmt->bind_param('iiiiiii', $interval, $interval, $interval, $interval, $interval, $interval, $id) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_assoc(); - // Catchall - return false; + return $this->sqlError('E0055'); } /** @@ -136,44 +106,90 @@ class Worker { * @param account_id int User ID * @return mixed array Workers and their settings or false **/ - public function getWorkers($account_id) { + public function getWorkers($account_id, $interval=600) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare(" SELECT id, username, password, monitor, - ( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all, - ( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)) AS count_all_archive, + ( SELECT COUNT(id) FROM " . $this->share->getTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)) AS count_all, + ( SELECT COUNT(id) FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND)) AS count_all_archive, ( SELECT - IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate + IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate FROM " . $this->share->getTableName() . " WHERE username = w.username - AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) + AND time > DATE_SUB(now(), INTERVAL ? SECOND) ) + ( SELECT - IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * 65536/600/1000), 0), 0) AS hashrate + IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate FROM " . $this->share->getArchiveTableName() . " WHERE username = w.username - AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) + AND time > DATE_SUB(now(), INTERVAL ? SECOND) ) AS hashrate, ( SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all, 2), 0) FROM " . $this->share->getTableName() . " - WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) + WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) ) + ( SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) / count_all_archive, 2), 0) FROM " . $this->share->getArchiveTableName() . " - WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL 10 MINUTE) + WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) ) AS difficulty FROM $this->table AS w WHERE account_id = ?"); - if ($this->checkStmt($stmt) && $stmt->bind_param('i', $account_id) && $stmt->execute() && $result = $stmt->get_result()) + if ($this->checkStmt($stmt) && $stmt->bind_param('iiiiiiiii', $interval, $interval, $interval, $interval, $interval, $interval, $interval, $interval, $account_id) && $stmt->execute() && $result = $stmt->get_result()) return $result->fetch_all(MYSQLI_ASSOC); - // Catchall - $this->setErrorMessage('Failed to fetch workers for your account'); - $this->debug->append('Fetching workers failed: ' . $this->mysqli->error); - return false; + return $this->sqlError('E0056'); + } + + /** + * Fetch all workers for admin panel + * @param limit int + * @return mixed array Workers and their settings or false + **/ + public function getAllWorkers($iLimit=0, $interval=600) { + $this->debug->append("STA " . __METHOD__, 4); + $stmt = $this->mysqli->prepare(" + SELECT id, username, password, monitor, + IFNULL(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty), 0) AS difficulty, + ( + SELECT + IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate + FROM " . $this->share->getTableName() . " + WHERE + username = w.username + AND time > DATE_SUB(now(), INTERVAL ? SECOND) + ) + ( + SELECT + IFNULL(IF(our_result='Y', ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)) * POW(2, " . $this->config['target_bits'] . ") / ? / 1000), 0), 0) AS hashrate + FROM " . $this->share->getArchiveTableName() . " + WHERE + username = w.username + AND time > DATE_SUB(now(), INTERVAL ? SECOND) + ) AS hashrate, + (( + SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 2), 0) + FROM " . $this->share->getTableName() . " + WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) + ) + ( + SELECT IFNULL(ROUND(SUM(IF(difficulty=0, pow(2, (" . $this->config['difficulty'] . " - 16)), difficulty)), 2), 0) + FROM " . $this->share->getArchiveTableName() . " + WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) + )) / (( + SELECT COUNT(id) + FROM " . $this->share->getTableName() . " + WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) + ) + ( + SELECT COUNT(id) + FROM " . $this->share->getArchiveTableName() . " + WHERE username = w.username AND time > DATE_SUB(now(), INTERVAL ? SECOND) + )) AS avg_difficulty + FROM $this->table AS w + ORDER BY hashrate DESC LIMIT ?"); + if ($this->checkStmt($stmt) && $stmt->bind_param('iiiiiiiii', $interval, $interval, $interval, $interval, $interval, $interval, $interval, $interval, $iLimit) && $stmt->execute() && $result = $stmt->get_result()) + return $result->fetch_all(MYSQLI_ASSOC); + return $this->sqlError('E0057'); } /** @@ -183,10 +199,15 @@ class Worker { **/ public function getCountAllActiveWorkers() { $this->debug->append("STA " . __METHOD__, 4); - $stmt = $this->mysqli->prepare("SELECT IFNULL(IF(our_result='Y', COUNT(DISTINCT username), 0), 0) AS total FROM " . $this->share->getTableName() . " WHERE time > DATE_SUB(now(), INTERVAL 10 MINUTE)"); + if ($data = $this->memcache->get(__FUNCTION__)) return $data; + $stmt = $this->mysqli->prepare(" + SELECT COUNT(DISTINCT(username)) AS total + FROM " . $this->share->getTableName() . " + WHERE our_result = 'Y' + AND time > DATE_SUB(now(), INTERVAL 10 MINUTE)"); if ($this->checkStmt($stmt) && $stmt->execute() && $result = $stmt->get_result()) - return $result->fetch_object()->total; - return false; + return $this->memcache->setCache(__FUNCTION__, $result->fetch_object()->total); + return $this->sqlError(); } /** @@ -201,22 +222,20 @@ class Worker { public function addWorker($account_id, $workerName, $workerPassword) { $this->debug->append("STA " . __METHOD__, 4); if ('' === $workerName || '' === $workerPassword) { - $this->setErrorMessage('Worker name and/or password may not be empty'); + $this->setErrorMessage($this->getErrorMsg('E0058')); return false; } $username = $this->user->getUserName($account_id); $workerName = "$username.$workerName"; $stmt = $this->mysqli->prepare("INSERT INTO $this->table (account_id, username, password) VALUES(?, ?, ?)"); - if ($this->checkStmt($stmt)) { - $stmt->bind_param('iss', $account_id, $workerName, $workerPassword); + if ($this->checkStmt($stmt) && $stmt->bind_param('iss', $account_id, $workerName, $workerPassword)) { if (!$stmt->execute()) { - $this->setErrorMessage( 'Failed to add worker' ); - if ($stmt->sqlstate == '23000') $this->setErrorMessage( 'Worker already exists' ); - return false; + if ($stmt->sqlstate == '23000') return $this->sqlError('E0059'); + } else { + return true; } - return true; } - return false; + return $this->sqlError('E0060'); } /** @@ -228,16 +247,19 @@ class Worker { public function deleteWorker($account_id, $id) { $this->debug->append("STA " . __METHOD__, 4); $stmt = $this->mysqli->prepare("DELETE FROM $this->table WHERE account_id = ? AND id = ?"); - if ($this->checkStmt($stmt)) { - $stmt->bind_param('ii', $account_id, $id); - if ($stmt->execute() && $stmt->affected_rows == 1) { + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $account_id, $id) && $stmt->execute() && $stmt->affected_rows == 1) return true; - } else { - $this->setErrorMessage( 'Unable to delete worker' ); - } - } - return false; + return $this->sqlError('E0061'); } } -$worker = new Worker($debug, $mysqli, $user, $share, $config); +$worker = new Worker(); +$worker->setDebug($debug); +$worker->setMysql($mysqli); +$worker->setMemcache($memcache); +$worker->setShare($share); +$worker->setConfig($config); +$worker->setUser($user); +$worker->setErrorCodes($aErrorCodes); + +?> diff --git a/public/include/config/admin_settings.inc.php b/public/include/config/admin_settings.inc.php index e85b9480..731e4ac6 100644 --- a/public/include/config/admin_settings.inc.php +++ b/public/include/config/admin_settings.inc.php @@ -123,6 +123,13 @@ $aSettings['statistics'][] = array( 'name' => 'statistics_block_count', 'value' => $setting->getValue('statistics_block_count'), 'tooltip' => 'Blocks to fetch for the block statistics page.' ); +$aSettings['statistics'][] = array( + 'display' => 'Show block average', 'type' => 'select', + 'options' => array( 0 => 'No', 1 => 'Yes' ), + 'default' => 0, + 'name' => 'statistics_show_block_average', 'value' => $setting->getValue('statistics_show_block_average'), + 'tooltip' => 'Show block average in block statistics page.' +); $aSettings['statistics'][] = array( 'display' => 'Pool Hashrate Modifier', 'type' => 'select', 'options' => array( '1' => 'KH/s', '0.001' => 'MH/s', '0.000001' => 'GH/s' ), @@ -144,6 +151,28 @@ $aSettings['statistics'][] = array( 'name' => 'statistics_personal_hashrate_modifier', 'value' => $setting->getValue('statistics_personal_hashrate_modifier'), 'tooltip' => 'Auto-adjust displayed personal hashrates to a certain limit.' ); +$aSettings['statistics'][] = array( + 'display' => 'Enable Google analytics', 'type' => 'select', + 'options' => array( 0 => 'No', 1 => 'Yes' ), + 'default' => 0, + 'name' => 'statistics_analytics_enabled', 'value' => $setting->getValue('statistics_analytics_enabled'), + 'tooltip' => 'Enable or Disable Google Analytics.' +); +$aSettings['statistics'][] = array( + 'display' => 'Google Analytics Code', 'type' => 'textarea', + 'size' => 20, + 'height' => 12, + 'default' => 'Code from Google Analytics', + 'name' => 'statistics_analytics_code', 'value' => $setting->getValue('statistics_analytics_code'), + 'tooltip' => '.' +); +$aSettings['acl'][] = array( + 'display' => 'Hide news post author', 'type' => 'select', + 'options' => array( 0 => 'No', 1 => 'Yes' ), + 'default' => 0, + 'name' => 'acl_hide_news_author', 'value' => $setting->getValue('acl_hide_news_author'), + 'tooltip' => 'Should the news author username be hidden.' +); $aSettings['acl'][] = array( 'display' => 'Pool Statistics', 'type' => 'select', 'options' => array( 0 => 'Private', 1 => 'Public'), @@ -166,11 +195,25 @@ $aSettings['acl'][] = array( 'tooltip' => 'Make the round statistics page private (users only) or public.' ); $aSettings['acl'][] = array( - 'display' => 'Round Transactions', 'type' => 'select', - 'options' => array( 0 => 'Admins', 1 => 'Public'), - 'default' => 0, - 'name' => 'acl_round_transactions', 'value' => $setting->getValue('acl_round_transactions'), - 'tooltip' => 'Display all transactions regardless of admin status.' + 'display' => 'Block Finder Statistics', 'type' => 'select', + 'options' => array( 0 => 'Private', 1 => 'Public'), + 'default' => 1, + 'name' => 'acl_blockfinder_statistics', 'value' => $setting->getValue('acl_blockfinder_statistics'), + 'tooltip' => 'Make the Block Finder Statistics page private (users only) or public.' +); +$aSettings['acl'][] = array( + 'display' => 'Uptime Statistics', 'type' => 'select', + 'options' => array( 0 => 'Private', 1 => 'Public'), + 'default' => 1, + 'name' => 'acl_uptime_statistics', 'value' => $setting->getValue('acl_uptime_statistics'), + 'tooltip' => 'Make the uptime statistics page private (users only) or public.' +); +$aSettings['system'][] = array( + 'display' => 'E-mail address for system error notifications', 'type' => 'text', + 'size' => 25, + 'default' => 'test@example.com', + 'name' => 'system_error_email', 'value' => $setting->getValue('system_error_email'), + 'tooltip' => 'The email address for system errors notifications, like cronjobs failures.' ); $aSettings['system'][] = array( 'display' => 'Disable e-mail confirmations', 'type' => 'select', @@ -228,6 +271,34 @@ $aSettings['system'][] = array( 'name' => 'disable_contactform', 'value' => $setting->getValue('disable_contactform'), 'tooltip' => 'Enable or Disable Contactform. Users will not be able to use the contact form.' ); +$aSettings['system'][] = array( + 'display' => 'Disable Donors Page', 'type' => 'select', + 'options' => array( 0 => 'No', 1 => 'Yes'), + 'default' => 1, + 'name' => 'disable_donors', 'value' => $setting->getValue('disable_donors'), + 'tooltip' => 'Showing Donors page in Navigation.' +); +$aSettings['system'][] = array( + 'display' => 'Disable About Page', 'type' => 'select', + 'options' => array( 0 => 'No', 1 => 'Yes'), + 'default' => 1, + 'name' => 'disable_about', 'value' => $setting->getValue('disable_about'), + 'tooltip' => 'Showing About page in Navigation.' +); +$aSettings['system'][] = array( + 'display' => 'Disable Live Dashboard', 'type' => 'select', + 'options' => array( 0 => 'No', 1 => 'Yes'), + 'default' => 0, + 'name' => 'disable_dashboard', 'value' => $setting->getValue('disable_dashboard'), + 'tooltip' => 'Disable live updates on the dashboard to reduce server load.' +); +$aSettings['system'][] = array( + 'display' => 'Disable Dashboard API', 'type' => 'select', + 'options' => array( 0 => 'No', 1 => 'Yes'), + 'default' => 0, + 'name' => 'disable_dashboard_api', 'value' => $setting->getValue('disable_dashboard_api'), + 'tooltip' => 'Disable dashboard API entirely to reduce server load.' +); $aSettings['recaptcha'][] = array( 'display' => 'Enable re-Captcha', 'type' => 'select', 'options' => array( 0 => 'No', 1 => 'Yes' ), @@ -249,5 +320,12 @@ $aSettings['recaptcha'][] = array( 'name' => 'recaptcha_public_key', 'value' => $setting->getValue('recaptcha_public_key'), 'tooltip' => 'Your public key as given by your re-Captcha account.' ); +$aSettings['monitoring'][] = array( + 'display' => 'Uptime Robot Private API Key', 'type' => 'text', + 'size' => 100, + 'default' => '|,|, ...', + 'name' => 'monitoring_uptimerobot_api_keys', 'value' => $setting->getValue('monitoring_uptimerobot_api_keys'), + 'tooltip' => 'Create per-monitor API keys and save them here to propagate your uptime statistics.' +); ?> diff --git a/public/include/config/error_codes.inc.php b/public/include/config/error_codes.inc.php new file mode 100644 index 00000000..514f9be2 --- /dev/null +++ b/public/include/config/error_codes.inc.php @@ -0,0 +1,71 @@ + diff --git a/public/include/config/global.inc.dist.php b/public/include/config/global.inc.dist.php index 941f57b0..28a7bf96 100644 --- a/public/include/config/global.inc.dist.php +++ b/public/include/config/global.inc.dist.php @@ -20,6 +20,17 @@ define('DEBUG', 0); // SALT used to hash passwords define('SALT', 'PLEASEMAKEMESOMETHINGRANDOM'); +/** + * Underlying coin algorithm that you are mining on. Set this to whatever your coin needs: + * + * Options: + * sha256d : SHA coins like Bitcoin + * scrypt : Scrypt based coins like Litecoin + * Default: + * scrypt : Scrypt is default + **/ +$config['algorithm'] = 'scrypt'; + /** * Database configuration * @@ -58,6 +69,18 @@ $config['wallet']['host'] = 'localhost:19334'; $config['wallet']['username'] = 'testnet'; $config['wallet']['password'] = 'testnet'; +/** + * Getting Started Config + * + * This is displayed on GettingStarted Page + * to make it more dynamic + * + * + **/ +$config['gettingstarted']['coinname'] = 'Litecoin'; +$config['gettingstarted']['coinurl'] = 'http://www.litecoin.org'; +$config['gettingstarted']['stratumport'] = '3333'; + /** * API configuration to fetch prices for set currency * @@ -86,7 +109,6 @@ $config['price']['url'] = 'https://btc-e.com'; $config['price']['target'] = '/api/2/ltc_usd/ticker'; $config['price']['currency'] = 'USD'; - /** * Automatic payout thresholds * @@ -123,6 +145,20 @@ $config['accounts']['invitations']['count'] = 5; // Currency system used in this pool, default: `LTC` $config['currency'] = 'LTC'; +/** + * Coin Target in seconds + * + * Explanation + * Target time for coins to be generated + * + * Fastcoin: 12 seconds + * Litecoin: 2,5 minutes = 150 seconds + * Feathercoin: 2,5 minutes = 150 seconds + * Bitcoin: 10 minutes = 600 seconds + * + **/ +$config['cointarget'] = '150'; + /** * Default transaction fee to apply to user transactions * @@ -231,9 +267,20 @@ $config['fees'] = 0; * type = `blockavg` * blockcount = 10 **/ +/** + * $config['pplns']['shares']['type'] = 'dynamic'; + * Dynamic target adjustment allows the blockavg target to adjust faster to share counts + * while still tracking round share averages by using a percentage of the current round shares + * to alter the pplns blockavg target this is useful with the nature of many alt coins low and fast + * adjusting difficulties and quick round times + * reverse_payout is useful to even out payouts for fast round times when even steady miners + * are missing share submissions for the current round +**/ $config['pplns']['shares']['default'] = 4000000; $config['pplns']['shares']['type'] = 'blockavg'; $config['pplns']['blockavg']['blockcount'] = 10; +$config['pplns']['reverse_payout'] = false; // add user shares from archive even if user not in current round +$config['pplns']['dynamic']['percent'] = 30; // percentage of round shares factored into block average when using dynamic type // Pool target difficulty as set in pushpoold configuration file // Please also read this for stratum: https://github.com/TheSerapher/php-mpos/wiki/FAQ @@ -297,10 +344,6 @@ $config['pps']['reward']['default'] = 50; $config['pps']['reward']['type'] = 'blockavg'; $config['pps']['blockavg']['blockcount'] = 10; -// pps base payout target, default 16 = difficulty 1 shares for vardiff -// (1/(65536 * difficulty) * reward) = (reward / (pow(2,32) * difficulty) * pow(2, 16)) -$config['pps_target'] = 16; // do not change unless you know what it does - /** * Memcache configuration * @@ -413,4 +456,19 @@ $config['cookie']['secure'] = false; **/ $config['smarty']['cache'] = 0; $config['smarty']['cache_lifetime'] = 30; + +/** + * System load setting + * + * This will disable loading of some API calls in case the system + * loads exceeds the defined max setting. Useful to temporaily suspend + * live statistics on a server that is too busy to deal with requests. + * + * Options + * max = float, maximum system load + * + * Defaults: + * max = 10.0 + **/ +$config['system']['load']['max'] = 10.0; ?> diff --git a/public/include/pages/about/donors.inc.php b/public/include/pages/about/donors.inc.php index e4ff8753..0d4213f7 100644 --- a/public/include/pages/about/donors.inc.php +++ b/public/include/pages/about/donors.inc.php @@ -3,9 +3,15 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -$aDonors = $transaction->getDonations(); +if ($setting->getValue('disable_donors')) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Donors are currently disabled. Please try again later.', 'TYPE' => 'errormsg'); + $smarty->assign("CONTENT", "disabled.tpl"); +} else { + $aDonors = $transaction->getDonations(); + + // Tempalte specifics + $smarty->assign("DONORS", $aDonors); + $smarty->assign("CONTENT", "default.tpl"); +} -// Tempalte specifics -$smarty->assign("DONORS", $aDonors); -$smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/include/pages/about/pool.inc.php b/public/include/pages/about/pool.inc.php index aecab054..20c860f0 100644 --- a/public/include/pages/about/pool.inc.php +++ b/public/include/pages/about/pool.inc.php @@ -4,6 +4,12 @@ if (!defined('SECURITY')) die('Hacking attempt'); -// Tempalte specifics -$smarty->assign("CONTENT", "default.tpl"); +if ($setting->getValue('disable_about')) { + $_SESSION['POPUP'][] = array('CONTENT' => 'Donors are currently disabled. Please try again later.', 'TYPE' => 'errormsg'); + $smarty->assign("CONTENT", "disabled.tpl"); +} else { + // Tempalte specifics + $smarty->assign("CONTENT", "default.tpl"); +} + ?> diff --git a/public/include/pages/account/notifications.inc.php b/public/include/pages/account/notifications.inc.php index a454fe12..d2347812 100644 --- a/public/include/pages/account/notifications.inc.php +++ b/public/include/pages/account/notifications.inc.php @@ -11,7 +11,7 @@ if ($user->isAuthenticated()) { if ($notification->updateSettings($_SESSION['USERDATA']['id'], $_REQUEST['data'])) { $_SESSION['POPUP'][] = array('CONTENT' => 'Updated notification settings'); } else { - $_SESSION['POPUP'][] = array('CONTENT' => 'Failed to update settings', 'TYPE' => 'errormsg'); + $_SESSION['POPUP'][] = array('CONTENT' => $notification->getError(), 'TYPE' => 'errormsg'); } } diff --git a/public/include/pages/admin/monitoring.inc.php b/public/include/pages/admin/monitoring.inc.php index b46908db..c57acb2c 100644 --- a/public/include/pages/admin/monitoring.inc.php +++ b/public/include/pages/admin/monitoring.inc.php @@ -28,6 +28,7 @@ case 'prop': // Data array for template foreach ($aCrons as $strCron) { $aCronStatus[$strCron] = array( + 'disabled' => $monitoring->getStatus($strCron . '_disabled'), 'exit' => $monitoring->getStatus($strCron . '_status'), 'active' => $monitoring->getStatus($strCron . '_active'), 'runtime' => $monitoring->getStatus($strCron . '_runtime'), diff --git a/public/include/pages/admin/poolworkers.inc.php b/public/include/pages/admin/poolworkers.inc.php new file mode 100644 index 00000000..54ea4e3c --- /dev/null +++ b/public/include/pages/admin/poolworkers.inc.php @@ -0,0 +1,18 @@ +isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { + header("HTTP/1.1 404 Page not found"); + die("404 Page not found"); +} + + $iActiveWorkers = $worker->getCountAllActiveWorkers(); + $aWorkers = $worker->getAllWorkers($iActiveWorkers); + + $smarty->assign('WORKERS', $aWorkers); + +$smarty->assign('CONTENT', 'default.tpl'); + +?> diff --git a/public/include/pages/admin/reports.inc.php b/public/include/pages/admin/reports.inc.php new file mode 100644 index 00000000..5225461c --- /dev/null +++ b/public/include/pages/admin/reports.inc.php @@ -0,0 +1,86 @@ +isAuthenticated() || !$user->isAdmin($_SESSION['USERDATA']['id'])) { + header("HTTP/1.1 404 Page not found"); + die("404 Page not found"); +} + +if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { + $debug->append('No cached version available, fetching from backend', 3); + + $aUserList = $user->getAllUsers('%'); + + $iHeight = 0; + $iUserId = 0; + $filter = 0; + $userName = 'None'; + if (@$_REQUEST['id']) { + $iUserId = $_REQUEST['id']; + $userName = $user->getUserName($_REQUEST['id']); + } + + $setting->getValue('statistics_block_count') ? $iLimit = $setting->getValue('statistics_block_count') : $iLimit = 20; + if (@$_REQUEST['limit']) { + $iLimit = $_REQUEST['limit']; + if ( $iLimit > 1000 ) + $iLimit = 1000; + } + + if (@$_REQUEST['next'] && !empty($_REQUEST['height'])) { + $iHeight = @$roundstats->getNextBlockForStats($_REQUEST['height'], $iLimit); + if (!$iHeight) { + $aBlock = $block->getLast(); + $iHeight = $aBlock['height']; + } + } else if (@$_REQUEST['prev'] && !empty($_REQUEST['height'])) { + $iHeight = $_REQUEST['height']; + } else if (!empty($_REQUEST['height']) && is_numeric($_REQUEST['height'])) { + $iHeight = $_REQUEST['height']; + } else { + $aBlock = $block->getLast(); + $iHeight = $aBlock['height']; + } + + if (@$_REQUEST['search']) { + $iHeight = $roundstats->searchForBlockHeight($_REQUEST['search']); + } + + if (@$_REQUEST['filter']) { + $filter = $_REQUEST['filter']; + } + + $aBlocksData = array(); + if ( $iUserId ) { + if ($filter) { + $aBlocksData = $roundstats->getAllReportBlocksFoundHeight($iHeight, $iLimit); + } else { + $aBlocksData = $roundstats->getUserReportBlocksFoundHeight($iHeight, $iLimit, $iUserId); + } + foreach($aBlocksData as $key => $aData) { + $aBlocksData[$key]['pplns_shares'] = @$roundstats->getPPLNSRoundShares($aData['height']); + $aBlocksData[$key]['user'] = @$roundstats->getRoundStatsForUser($aData['height'], $iUserId); + $aBlocksData[$key]['user_credit'] = @$roundstats->getUserRoundTransHeight($aData['height'], $iUserId); + } + } + + $smarty->assign('REPORTDATA', $aBlocksData); + $smarty->assign("USERLIST", $aUserList); + $smarty->assign("USERNAME", $userName); + $smarty->assign("USERID", $iUserId); + $smarty->assign("BLOCKLIMIT", $iLimit); + $smarty->assign("HEIGHT", $iHeight); + $smarty->assign("FILTER", $filter); +} else { + $debug->append('Using cached page', 3); +} + +if ($user->isAuthenticated(false)) { + $smarty->assign("CONTENT", "default.tpl"); +} else { + $smarty->assign("CONTENT", "empty"); +} +?> diff --git a/public/include/pages/admin/user.inc.php b/public/include/pages/admin/user.inc.php index bc562bbf..5ad323d5 100644 --- a/public/include/pages/admin/user.inc.php +++ b/public/include/pages/admin/user.inc.php @@ -31,23 +31,11 @@ if (@$_POST['query']) { $aUsers = $statistics->getAllUserStats($_POST['query']); // Add additional stats to each user - // This is not optimized yet, best is a proper SQL - // Query against the stats table? Currently cached though. foreach ($aUsers as $iKey => $aUser) { $aBalance = $transaction->getBalance($aUser['id']); $aUser['balance'] = $aBalance['confirmed']; $aUser['hashrate'] = $statistics->getUserHashrate($aUser['id']); - if ($aUser['shares'] > 0) { - $aUser['payout']['est_block'] = round(( (int)$aUser['shares'] / (int)$aRoundShares['valid'] ) * (int)$config['reward'], 3); - $aUser['payout']['est_fee'] = round(($config['fees'] / 100) * $aUser['payout']['est_block'], 3); - $aUser['payout']['est_donation'] = round((( $aUser['donate_percent'] / 100) * ($aUser['payout']['est_block'] - $aUser['payout']['est_fee'])), 3); - $aUser['payout']['est_payout'] = round($aUser['payout']['est_block'] - $aUser['payout']['est_donation'] - $aUser['payout']['est_fee'], 3); - } else { - $aUser['payout']['est_block'] = 0; - $aUser['payout']['est_fee'] = 0; - $aUser['payout']['est_donation'] = 0; - $aUser['payout']['est_payout'] = 0; - } + $aUser['estimates'] = $statistics->getUserEstimates($aRoundShares, $aUser['shares'], $aUser['donate_percent'], $aUser['no_fees']); $aUsers[$iKey] = $aUser; } // Assign our variables diff --git a/public/include/pages/admin/wallet.inc.php b/public/include/pages/admin/wallet.inc.php index 1e2d2659..b057b4d1 100644 --- a/public/include/pages/admin/wallet.inc.php +++ b/public/include/pages/admin/wallet.inc.php @@ -13,13 +13,14 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); if ($bitcoin->can_connect() === true){ $dBalance = $bitcoin->query('getbalance'); - $dGetInfo = $bitcoin->query('getinfo'); - if (is_array($dGetInfo) && array_key_exists('newmint', $dGetInfo)) { - $dNewmint = $dGetInfo['newmint']; + $aGetInfo = $bitcoin->query('getinfo'); + if (is_array($aGetInfo) && array_key_exists('newmint', $aGetInfo)) { + $dNewmint = $aGetInfo['newmint']; } else { $dNewmint = -1; } } else { + $aGetInfo = array('errors' => 'Unable to connect'); $dBalance = 0; $dNewmint = -1; $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to connect to wallet RPC service: ' . $bitcoin->can_connect(), 'TYPE' => 'errormsg'); @@ -45,6 +46,7 @@ $smarty->assign("BALANCE", $dBalance); $smarty->assign("COLDCOINS", $dColdCoins); $smarty->assign("LOCKED", $dLockedBalance); $smarty->assign("NEWMINT", $dNewmint); +$smarty->assign("COININFO", $aGetInfo); // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); diff --git a/public/include/pages/api/getdashboarddata.inc.php b/public/include/pages/api/getdashboarddata.inc.php index e16d89cd..efc9d743 100644 --- a/public/include/pages/api/getdashboarddata.inc.php +++ b/public/include/pages/api/getdashboarddata.inc.php @@ -3,8 +3,22 @@ // Make sure we are called from index.php if (!defined('SECURITY')) die('Hacking attempt'); -// Check if the API is activated -$api->isActive(); +// Check if the system is enabled +if ($setting->getValue('disable_dashboard_api')) { + echo $api->get_json(array('error' => 'disabled')); + die(); +} + +// System load check +if ($load = @sys_getloadavg()) { + if (isset($config['system']['load']['max']) && $load[0] > $config['system']['load']['max']) { + header('HTTP/1.1 503 Too busy, try again later'); + die('Server too busy. Please try again later.'); + } +} + +// Supress master template +$supress_master = 1; // Check user token and access level permissions $user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']); @@ -32,27 +46,85 @@ $dPoolHashrate = $statistics->getCurrentHashrate($interval); if ($dPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $dPoolHashrate; $dPersonalHashrate = $statistics->getUserHashrate($user_id, $interval); $dPersonalSharerate = $statistics->getUserSharerate($user_id, $interval); +$dPersonalShareDifficulty = $statistics->getUserShareDifficulty($user_id, $interval); $statistics->setGetCache(true); // Use caches for this one $aUserRoundShares = $statistics->getUserShares($user_id); $aRoundShares = $statistics->getRoundShares(); -$aEstimates = $statistics->getUserEstimates($aRoundShares, $aUserRoundShares, $user->getUserDonatePercent($user_id), $user->getUserNoFee($user_id)); + +if ($config['payout_system'] != 'pps') { + $aEstimates = $statistics->getUserEstimates($aRoundShares, $aUserRoundShares, $user->getUserDonatePercent($user_id), $user->getUserNoFee($user_id)); + $dUnpaidShares = 0; +} else { + $dUnpaidShares = $statistics->getUserUnpaidPPSShares($user_id, $setting->getValue('pps_last_share_id')); + if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) { + $pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount'])); + } else { + if ($config['pps']['reward']['type'] == 'block') { + if ($aLastBlock = $block->getLast()) { + $pps_reward = $aLastBlock['amount']; + } else { + $pps_reward = $config['pps']['reward']['default']; + } + } else { + $pps_reward = $config['pps']['reward']['default']; + } + } + + $ppsvalue = round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['target_bits']), 12); + $aEstimates = $statistics->getUserEstimates($dPersonalSharerate, $dPersonalShareDifficulty, $user->getUserDonatePercent($user_id), $user->getUserNoFee($user_id), $ppsvalue); +} + +$iTotalRoundShares = $aRoundShares['valid'] + $aRoundShares['invalid']; +if ($iTotalRoundShares > 0) { + $dUserInvalidPercent = round($aUserRoundShares['invalid'] / $iTotalRoundShares * 100, 2); + $dPoolInvalidPercent = round($aRoundShares['invalid'] / $iTotalRoundShares * 100, 2); +} else { + $dUserInvalidPercent = 0; + $dPoolInvalidPercent = 0; +} // Apply pool modifiers $dPersonalHashrateAdjusted = $dPersonalHashrate * $dPersonalHashrateModifier; $dPoolHashrateAdjusted = $dPoolHashrate * $dPoolHashrateModifier; $dNetworkHashrateAdjusted = $dNetworkHashrate / 1000 * $dNetworkHashrateModifier; +// Worker information +$aWorkers = $worker->getWorkers($user_id, $interval); + +// Coin price +$aPrice = $setting->getValue('price'); + +// Round progress +$iEstShares = $statistics->getEstimatedShares($dDifficulty); +if ($iEstShares > 0 && $aRoundShares['valid'] > 0) { + $dEstPercent = round(100 / $iEstShares * $aRoundShares['valid'], 2); +} else { + $dEstPercent = 0; +} + // Output JSON format $data = array( 'raw' => array( 'personal' => array( 'hashrate' => $dPersonalHashrate ), 'pool' => array( 'hashrate' => $dPoolHashrate ), 'network' => array( 'hashrate' => $dNetworkHashrate / 1000 ) ), - 'personal' => array ( 'hashrate' => $dPersonalHashrateAdjusted, 'sharerate' => $dPersonalSharerate, 'shares' => $aUserRoundShares, 'balance' => $transaction->getBalance($user_id), 'estimates' => $aEstimates), - 'pool' => array( 'hashrate' => $dPoolHashrateAdjusted, 'shares' => $aRoundShares ), + 'personal' => array ( + 'hashrate' => $dPersonalHashrateAdjusted, 'sharerate' => $dPersonalSharerate, 'sharedifficulty' => $dPersonalShareDifficulty, + 'shares' => array('valid' => $aUserRoundShares['valid'], 'invalid' => $aUserRoundShares['invalid'], 'invalid_percent' => $dUserInvalidPercent, 'unpaid' => $dUnpaidShares ), + 'balance' => $transaction->getBalance($user_id), 'estimates' => $aEstimates, 'workers' => $aWorkers ), + 'pool' => array( + 'info' => array( + 'name' => $setting->getValue('website_name'), + 'currency' => $config['currency'] + ), + 'workers' => $worker->getCountAllActiveWorkers(), 'hashrate' => $dPoolHashrateAdjusted, + 'shares' => array( 'valid' => $aRoundShares['valid'], 'invalid' => $aRoundShares['invalid'], 'invalid_percent' => $dPoolInvalidPercent, 'estimated' => $iEstShares, 'progress' => $dEstPercent ), + 'price' => $aPrice, + 'difficulty' => pow(2, $config['difficulty'] - 16), + 'target_bits' => $config['difficulty'] + ), + 'system' => array( 'load' => sys_getloadavg() ), 'network' => array( 'hashrate' => $dNetworkHashrateAdjusted, 'difficulty' => $dDifficulty, 'block' => $iBlock ), ); -echo $api->get_json($data); -// Supress master template -$supress_master = 1; +echo $api->get_json($data); ?> diff --git a/public/include/pages/api/getnavbardata.inc.php b/public/include/pages/api/getnavbardata.inc.php new file mode 100644 index 00000000..3f9615a7 --- /dev/null +++ b/public/include/pages/api/getnavbardata.inc.php @@ -0,0 +1,64 @@ +isActive(); + +// Fetch RPC information +if ($bitcoin->can_connect() === true) { + $dNetworkHashrate = $bitcoin->getnetworkhashps(); + $dDifficulty = $bitcoin->getdifficulty(); + $iBlock = $bitcoin->getblockcount(); +} else { + $dNetworkHashrate = 0; + $dDifficulty = 1; + $iBlock = 0; +} + +// Some settings +if ( ! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300; +if ( ! $dPoolHashrateModifier = $setting->getValue('statistics_pool_hashrate_modifier') ) $dPoolHashrateModifier = 1; +if ( ! $dNetworkHashrateModifier = $setting->getValue('statistics_network_hashrate_modifier') ) $dNetworkHashrateModifier = 1; + +// Fetch raw data +$statistics->setGetCache(false); +$dPoolHashrate = $statistics->getCurrentHashrate($interval); +if ($dPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $dPoolHashrate; +$statistics->setGetCache(true); + +// Apply pool modifiers +$dPoolHashrateAdjusted = $dPoolHashrate * $dPoolHashrateModifier; +$dNetworkHashrateAdjusted = $dNetworkHashrate / 1000 * $dNetworkHashrateModifier; + +// Use caches for this one +$aRoundShares = $statistics->getRoundShares(); + +$iTotalRoundShares = $aRoundShares['valid'] + $aRoundShares['invalid']; +if ($iTotalRoundShares > 0) { + $dPoolInvalidPercent = round($aRoundShares['invalid'] / $iTotalRoundShares * 100, 2); +} else { + $dUserInvalidPercent = 0; + $dPoolInvalidPercent = 0; +} + +// Round progress +$iEstShares = $statistics->getEstimatedShares($dDifficulty); +if ($iEstShares > 0 && $aRoundShares['valid'] > 0) { + $dEstPercent = round(100 / $iEstShares * $aRoundShares['valid'], 2); +} else { + $dEstPercent = 0; +} + +// Output JSON format +$data = array( + 'raw' => array( 'workers' => $worker->getCountAllActiveWorkers(), 'pool' => array( 'hashrate' => $dPoolHashrate ) ), + 'pool' => array( 'workers' => $worker->getCountAllActiveWorkers(), 'hashrate' => $dPoolHashrateAdjusted, 'estimated' => $iEstShares, 'progress' => $dEstPercent ), + 'network' => array( 'hashrate' => $dNetworkHashrateAdjusted, 'difficulty' => $dDifficulty, 'block' => $iBlock ), +); +echo $api->get_json($data); + +// Supress master template +$supress_master = 1; +?> diff --git a/public/include/pages/api/getpoolstatus.inc.php b/public/include/pages/api/getpoolstatus.inc.php index 00aab39c..ea053dcb 100644 --- a/public/include/pages/api/getpoolstatus.inc.php +++ b/public/include/pages/api/getpoolstatus.inc.php @@ -47,6 +47,7 @@ if (!empty($aLastBlock)) { // Output JSON format $data = array( + 'pool_name' => $setting->getValue('website_name'), 'hashrate' => $iCurrentPoolHashrate, 'efficiency' => $dEfficiency, 'workers' => $worker->getCountAllActiveWorkers(), diff --git a/public/include/pages/api/gettopcontributors.inc.php b/public/include/pages/api/gettopcontributors.inc.php new file mode 100644 index 00000000..0b22faee --- /dev/null +++ b/public/include/pages/api/gettopcontributors.inc.php @@ -0,0 +1,65 @@ +checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']); + +// Some settings +if ( ! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300; + +// Fetch raw data +$aContributorsHashes = $statistics->getTopContributors('hashes', 15); +$aContributorsShares = $statistics->getTopContributors('shares', 15); + +// Honor the anonymous flag +foreach ($aContributorsHashes as $iKey => $aData) { + if ($user->isAdmin($user_id)) { + $aContributorsHashes = array( + 'account' => $aData['account'], + 'hashrate' => $aData['hashrate'] + ); + } else if ($aData['is_anonymous'] == 1) { + $aContributorsHashes = array( + 'account' => 'anonymous', + 'hashrate' => $aData['hashrate'] + ); + } else { + $aContributorsHashes = array( + 'account' => $aData['account'], + 'hashrate' => $aData['hashrate'] + ); + } +} + +// Honor the anonymous flag +foreach ($aContributorsShares as $iKey => $aData) { + if ($user->isAdmin($user_id)) { + $aContributorsShares[$iKey] = array( + 'account' => $aData['account'], + 'shares' => $aData['shares'] + ); + } else if ($aData['is_anonymous'] == 1) { + $aContributorsShares[$iKey] = array( + 'account' => 'anonymous', + 'shares' => $aData['shares'] + ); + } else { + $aContributorsShares[$iKey] = array( + 'account' => $aData['account'], + 'shares' => $aData['shares'] + ); + } +} + +// Output JSON format +$data = array( + 'hashes' => $aContributorsHashes, + 'shares' => $aContributorsShares +); +echo $api->get_json($data); + +// Supress master template +$supress_master = 1; +?> diff --git a/public/include/pages/api/getuserstatus.inc.php b/public/include/pages/api/getuserstatus.inc.php index 8e0ef7ec..0cff1bf1 100644 --- a/public/include/pages/api/getuserstatus.inc.php +++ b/public/include/pages/api/getuserstatus.inc.php @@ -9,12 +9,16 @@ $api->isActive(); // Check user token $user_id = $api->checkAccess($user->checkApiKey($_REQUEST['api_key']), @$_REQUEST['id']); +// Fetch transaction summary +$aTransactionSummary = $transaction->getTransactionSummary($user_id); + // Output JSON format $data = array( 'username' => $user->getUsername($user_id), 'shares' => $statistics->getUserShares($user_id), 'hashrate' => $statistics->getUserHashrate($user_id), - 'sharerate' => $statistics->getUserSharerate($user_id) + 'sharerate' => $statistics->getUserSharerate($user_id), + 'transactions' => $aTransactionSummary ); echo $api->get_json($data); diff --git a/public/include/pages/dashboard.inc.php b/public/include/pages/dashboard.inc.php index 22a4edec..06b4f3b3 100644 --- a/public/include/pages/dashboard.inc.php +++ b/public/include/pages/dashboard.inc.php @@ -6,35 +6,28 @@ if (!defined('SECURITY')) die('Hacking attempt'); if ($user->isAuthenticated()) { if (! $interval = $setting->getValue('statistics_ajax_data_interval')) $interval = 300; // Defaults to get rid of PHP Notice warnings + $dNetworkHashrate = 0; $dDifficulty = 1; $aRoundShares = 1; - // Only run these if the user is logged in $aRoundShares = $statistics->getRoundShares(); + $dDifficulty = 1; + $dNetworkHashrate = 1; + $iBlock = 0; if ($bitcoin->can_connect() === true) { - $dDifficulty = $bitcoin->query('getdifficulty'); - if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) - $dDifficulty = $dDifficulty['proof-of-work']; - } - - // Always fetch this since we need for ministats header - $aRoundShares = $statistics->getRoundShares(); - if ($bitcoin->can_connect() === true) { - $dDifficulty = $bitcoin->query('getdifficulty'); - if (is_array($dDifficulty) && array_key_exists('proof-of-work', $dDifficulty)) - $dDifficulty = $dDifficulty['proof-of-work']; - try { $dNetworkHashrate = $bitcoin->query('getnetworkhashps') / 1000; } catch (Exception $e) { - // Maybe we are SHA - try { $dNetworkHashrate = $bitcoin->query('gethashespersec') / 1000; } catch (Exception $e) { - $dNetworkHashrate = 0; - } - $dNetworkHashrate = 0; - } - } else { - $dNetworkHashrate = 0; + $dDifficulty = $bitcoin->getdifficulty(); + $dNetworkHashrate = $bitcoin->getnetworkhashps(); + $iBlock = $bitcoin->getblockcount(); } // Fetch some data + // Round progress + $iEstShares = $statistics->getEstimatedShares($dDifficulty); + if ($iEstShares > 0 && $aRoundShares['valid'] > 0) { + $dEstPercent = round(100 / $iEstShares * $aRoundShares['valid'], 2); + } else { + $dEstPercent = 0; + } if (!$iCurrentActiveWorkers = $worker->getCountAllActiveWorkers()) $iCurrentActiveWorkers = 0; $iCurrentPoolHashrate = $statistics->getCurrentHashrate(); $iCurrentPoolShareRate = $statistics->getCurrentShareRate(); @@ -43,6 +36,10 @@ if ($user->isAuthenticated()) { if ($iCurrentPoolHashrate > $dNetworkHashrate) $dNetworkHashrate = $iCurrentPoolHashrate; // Make it available in Smarty + $smarty->assign('DISABLED_DASHBOARD', $setting->getValue('disable_dashboard')); + $smarty->assign('DISABLED_DASHBOARD_API', $setting->getValue('disable_dashboard_api')); + $smarty->assign('ESTIMATES', array('shares' => $iEstShares, 'percent' => $dEstPercent)); + $smarty->assign('NETWORK', array('difficulty' => $dDifficulty, 'block' => $iBlock)); $smarty->assign('INTERVAL', $interval / 60); $smarty->assign('CONTENT', 'default.tpl'); } diff --git a/public/include/pages/error.inc.php b/public/include/pages/error.inc.php new file mode 100644 index 00000000..aecab054 --- /dev/null +++ b/public/include/pages/error.inc.php @@ -0,0 +1,9 @@ +assign("CONTENT", "default.tpl"); +?> diff --git a/public/include/pages/error/404.inc.php b/public/include/pages/error/404.inc.php new file mode 100644 index 00000000..aecab054 --- /dev/null +++ b/public/include/pages/error/404.inc.php @@ -0,0 +1,9 @@ +assign("CONTENT", "default.tpl"); +?> diff --git a/public/include/pages/gettingstarted.inc.php b/public/include/pages/gettingstarted.inc.php index aecab054..2f30151a 100644 --- a/public/include/pages/gettingstarted.inc.php +++ b/public/include/pages/gettingstarted.inc.php @@ -4,6 +4,10 @@ if (!defined('SECURITY')) die('Hacking attempt'); +$smarty->assign("SITESTRATUMPORT", $config['gettingstarted']['stratumport']); +$smarty->assign("SITECOINNAME", $config['gettingstarted']['coinname']); +$smarty->assign("SITECOINURL", $config['gettingstarted']['coinurl']); + // Tempalte specifics $smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/include/pages/home.inc.php b/public/include/pages/home.inc.php index 349b197d..ea85e43b 100644 --- a/public/include/pages/home.inc.php +++ b/public/include/pages/home.inc.php @@ -16,6 +16,8 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $aNews[$key]['content'] = Markdown::defaultTransform($aData['content']); } } + + $smarty->assign("HIDEAUTHOR", $setting->getValue('acl_hide_news_author')); $smarty->assign("NEWS", $aNews); } else { $debug->append('Using cached page', 3); diff --git a/public/include/pages/login.inc.php b/public/include/pages/login.inc.php index 0dcf6774..32f1b2b4 100644 --- a/public/include/pages/login.inc.php +++ b/public/include/pages/login.inc.php @@ -7,7 +7,8 @@ if ($setting->getValue('maintenance') && !$user->isAdmin($user->getUserId($_POST $_SESSION['POPUP'][] = array('CONTENT' => 'You are not allowed to login during maintenace.', 'TYPE' => 'info'); } else if ($user->checkLogin(@$_POST['username'], @$_POST['password']) ) { empty($_POST['to']) ? $to = $_SERVER['PHP_SELF'] : $to = $_POST['to']; - $location = @$_SERVER['HTTPS'] === true ? 'https' : 'http' . '://' . $_SERVER['SERVER_NAME'] . $to; + $port = ($_SERVER["SERVER_PORT"] == "80" or $_SERVER["SERVER_PORT"] == "443") ? "" : (":".$_SERVER["SERVER_PORT"]); + $location = @$_SERVER['HTTPS'] === true ? 'https://' . $_SERVER['SERVER_NAME'] . $port . $to : 'http://' . $_SERVER['SERVER_NAME'] . $port . $to; if (!headers_sent()) header('Location: ' . $location); exit(''); } else if (@$_POST['username'] && @$_POST['password']) { diff --git a/public/include/pages/news.inc.php b/public/include/pages/news.inc.php index 127b1775..6e7a90c7 100644 --- a/public/include/pages/news.inc.php +++ b/public/include/pages/news.inc.php @@ -16,6 +16,7 @@ if (is_array($aNews)) { } // Tempalte specifics +$smarty->assign("HIDEAUTHOR", $settings->getValue('acl_hide_news_author')); $smarty->assign("NEWS", $aNews); $smarty->assign("CONTENT", "default.tpl"); ?> diff --git a/public/include/pages/register/register.inc.php b/public/include/pages/register/register.inc.php index ecb2c028..3c63963b 100644 --- a/public/include/pages/register/register.inc.php +++ b/public/include/pages/register/register.inc.php @@ -23,7 +23,7 @@ if ($setting->getValue('disable_invitations') && $setting->getValue('lock_regist if ($rsp->is_valid) { $smarty->assign("RECAPTCHA", recaptcha_get_html($setting->getValue('recaptcha_public_key'))); isset($_POST['token']) ? $token = $_POST['token'] : $token = ''; - if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) { + if ($user->register(@$_POST['username'], @$_POST['password1'], @$_POST['password2'], @$_POST['pin'], @$_POST['email1'], @$_POST['email2'], $token)) { ! $setting->getValue('accounts_confirm_email_disabled') ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); @@ -39,7 +39,7 @@ if ($setting->getValue('disable_invitations') && $setting->getValue('lock_regist // Captcha disabled } else { isset($_POST['token']) ? $token = $_POST['token'] : $token = ''; - if ($user->register($_POST['username'], $_POST['password1'], $_POST['password2'], $_POST['pin'], $_POST['email1'], $_POST['email2'], $token)) { + if ($user->register(@$_POST['username'], @$_POST['password1'], @$_POST['password2'], @$_POST['pin'], @$_POST['email1'], @$_POST['email2'], $token)) { ! $setting->getValue('accounts_confirm_email_disabled') ? $_SESSION['POPUP'][] = array('CONTENT' => 'Please check your mailbox to activate this account') : $_SESSION['POPUP'][] = array('CONTENT' => 'Account created, please login'); } else { $_SESSION['POPUP'][] = array('CONTENT' => 'Unable to create account: ' . $user->getError(), 'TYPE' => 'errormsg'); diff --git a/public/include/pages/statistics/blockfinder.inc.php b/public/include/pages/statistics/blockfinder.inc.php new file mode 100644 index 00000000..f471afdf --- /dev/null +++ b/public/include/pages/statistics/blockfinder.inc.php @@ -0,0 +1,29 @@ +isCached('master.tpl', $smarty_cache_key)) { + $debug->append('No cached version available, fetching from backend', 3); + $getBlocksSolvedbyAccount = $statistics->getBlocksSolvedbyAccount(); + $smarty->assign("BLOCKSSOLVEDBYACCOUNT", $getBlocksSolvedbyAccount); + + if(isset($_SESSION['USERDATA']['id'])){ + $getBlocksSolvedbyWorker = $statistics->getBlocksSolvedbyWorker($_SESSION['USERDATA']['id']); + $smarty->assign("BLOCKSSOLVEDBYWORKER", $getBlocksSolvedbyWorker); + } + +} else { + $debug->append('Using cached page', 3); +} + +// Public / private page detection +if ($setting->getValue('acl_blockfinder_statistics')) { + $smarty->assign("CONTENT", "finder.tpl"); +} else if ($user->isAuthenticated()) { + $smarty->assign("CONTENT", "finder.tpl"); +} else { + $smarty->assign("CONTENT", "default.tpl"); +} +?> diff --git a/public/include/pages/statistics/blocks.inc.php b/public/include/pages/statistics/blocks.inc.php index 116d999d..12f034c0 100644 --- a/public/include/pages/statistics/blocks.inc.php +++ b/public/include/pages/statistics/blocks.inc.php @@ -8,11 +8,84 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); // Grab the last blocks found $setting->getValue('statistics_block_count') ? $iLimit = $setting->getValue('statistics_block_count') : $iLimit = 20; - $aBlocksFoundData = $statistics->getBlocksFound($iLimit); + if (@$_REQUEST['limit'] && !empty($_REQUEST['limit']) && is_numeric($_REQUEST['limit'])) { + $iLimit = $_REQUEST['limit']; + if ( $iLimit > 40 ) + $iLimit = 40; + } + $iHeight = 0; + if (@$_REQUEST['next'] && !empty($_REQUEST['height']) && is_numeric($_REQUEST['height'])) { + $iHeight = @$roundstats->getNextBlockForStats($_REQUEST['height'], $iLimit); + if (!$iHeight) { + $iBlock = $block->getLast(); + $iHeight = $iBlock['height']; + } + } else if (@$_REQUEST['prev'] && !empty($_REQUEST['height']) && is_numeric($_REQUEST['height'])) { + $iHeight = $_REQUEST['height']; + } else if (empty($_REQUEST['height'])) { + $aBlock = $block->getLast(); + $iHeight = $aBlock['height']; + } + + $test = false; + if (@$_REQUEST['test'] && $user->isAdmin($_SESSION['USERDATA']['id'])) { + $test = true; + $count = 10; + $percent = 30; + if (@$_REQUEST['count'] && is_numeric($_REQUEST['count'])) + $count = $_REQUEST['count']; + if (@$_REQUEST['percent'] && is_numeric($_REQUEST['percent'])) + $percent = $_REQUEST['percent']; + } + + $aBlocksFoundData = $statistics->getBlocksFoundHeight($iHeight, $iLimit); + $use_average = false; + if ($config['payout_system'] == 'pplns') { + foreach($aBlocksFoundData as $key => $aData) { + $aBlocksFoundData[$key]['pplns_shares'] = $roundstats->getPPLNSRoundShares($aData['height']); + if ($setting->getValue('statistics_show_block_average') && !$test) { + $aBlocksFoundData[$key]['block_avg'] = round($block->getAvgBlockShares($aData['height'], $config['pplns']['blockavg']['blockcount'])); + $use_average = true; + } + } + } else if ($config['payout_system'] == 'prop' || $config['payout_system'] == 'pps') { + if ($setting->getValue('statistics_show_block_average') && !$test) { + foreach($aBlocksFoundData as $key => $aData) { + $aBlocksFoundData[$key]['block_avg'] = round($block->getAvgBlockShares($aData['height'], $config['pplns']['blockavg']['blockcount'])); + $use_average = true; + } + } + } + // show test data in graph + if ($test) { + $use_average = true; + foreach($aBlocksFoundData as $key => $aData) { + if ($_REQUEST['test'] == 1) { + $aBlocksFoundData[$key]['block_avg'] = round($block->getAvgBlockShares($aData['height'], $count)); + } else if ($_REQUEST['test'] == 2) { + $aBlocksFoundData[$key]['block_avg'] = round($block->getAvgBlockShares($aData['height'], $count) * (100 - $percent) / 100 + $aData['shares'] * $percent / 100); + } + } + } + + $iHours = 24; + $aPoolStatistics = $statistics->getPoolStatsHours($iHours); + $iFirstBlockFound = $statistics->getFirstBlockFound(); + $iTimeSinceFirstBlockFound = (time() - $iFirstBlockFound); + + // Coin target block generation time, default to 150 (2.5 minutes) + @$config['cointarget'] ? $smarty->assign("COINGENTIME", $config['cointarget']) : $smarty->assign("COINGENTIME", 150); + + // Past blocks found, max 4 weeks back + $iFoundBlocksByTime = $statistics->getLastBlocksbyTime(); // Propagate content our template + $smarty->assign("FIRSTBLOCKFOUND", $iTimeSinceFirstBlockFound); + $smarty->assign("LASTBLOCKSBYTIME", $iFoundBlocksByTime); $smarty->assign("BLOCKSFOUND", $aBlocksFoundData); $smarty->assign("BLOCKLIMIT", $iLimit); + $smarty->assign("USEBLOCKAVERAGE", $use_average); + $smarty->assign("POOLSTATS", $aPoolStatistics); } else { $debug->append('Using cached page', 3); } diff --git a/public/include/pages/statistics/pool.inc.php b/public/include/pages/statistics/pool.inc.php index 5416b984..91dca946 100644 --- a/public/include/pages/statistics/pool.inc.php +++ b/public/include/pages/statistics/pool.inc.php @@ -22,7 +22,7 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { // Top hash contributors $aContributorsHashes = $statistics->getTopContributors('hashes', 15); - // Grab the last 10 blocks found + // Grab the last 5 blocks found as a quick overview $iLimit = 5; $aBlocksFoundData = $statistics->getBlocksFound($iLimit); count($aBlocksFoundData) > 0 ? $aBlockData = $aBlocksFoundData[0] : $aBlockData = array(); @@ -34,9 +34,8 @@ if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $iCurrentPoolHashrate > 0 ? $iEstTime = $dDifficulty * pow(2,32) / ($iCurrentPoolHashrate * 1000) : $iEstTime = 0; // Time since last block - $now = new DateTime( "now" ); if (!empty($aBlockData)) { - $dTimeSinceLast = ($now->getTimestamp() - $aBlockData['time']); + $dTimeSinceLast = (time() - $aBlockData['time']); if ($dTimeSinceLast < 0) $dTimeSinceLast = 0; } else { $dTimeSinceLast = 0; diff --git a/public/include/pages/statistics/round.inc.php b/public/include/pages/statistics/round.inc.php index 4c37f749..4e7288d5 100644 --- a/public/include/pages/statistics/round.inc.php +++ b/public/include/pages/statistics/round.inc.php @@ -6,26 +6,40 @@ if (!defined('SECURITY')) die('Hacking attempt'); if (!$smarty->isCached('master.tpl', $smarty_cache_key)) { $debug->append('No cached version available, fetching from backend', 3); - if (@$_REQUEST['next'] && !empty($_REQUEST['height'])) { - $iKey = $roundstats->getNextBlock($_REQUEST['height']); - } else if (@$_REQUEST['prev'] && !empty($_REQUEST['height'])) { - $iKey = $roundstats->getPreviousBlock($_REQUEST['height']); - } else { - - if (empty($_REQUEST['height'])) { - $iBlock = $block->getLast(); - $iKey = $iBlock['height']; - } else { - $iKey = $_REQUEST['height']; - } + if (@$_REQUEST['search']) { + $_REQUEST['height'] = $roundstats->searchForBlockHeight($_REQUEST['search']); } - $aDetailsForBlockHeight = $roundstats->getDetailsForBlockHeight($iKey, $user->isAdmin(@$_SESSION['USERDATA']['id'])); - $aRoundShareStats = $roundstats->getRoundStatsForAccounts($iKey, $user->isAdmin(@$_SESSION['USERDATA']['id'])); - - if ($user->isAdmin(@$_SESSION['USERDATA']['id']) || $setting->getValue('acl_round_transactions')) { - $aUserRoundTransactions = $roundstats->getAllRoundTransactions($iKey, @$_SESSION['USERDATA']['id']); + if (@$_REQUEST['next'] && !empty($_REQUEST['height'])) { + $iHeight = @$roundstats->getNextBlock($_REQUEST['height']); + if (!$iHeight) { + $iBlock = $block->getLast(); + $iHeight = $iBlock['height']; + } + } else if (@$_REQUEST['prev'] && !empty($_REQUEST['height'])) { + $iHeight = $roundstats->getPreviousBlock($_REQUEST['height']); + } else if (empty($_REQUEST['height'])) { + $iBlock = $block->getLast(); + $iHeight = $iBlock['height']; } else { - $aUserRoundTransactions = $roundstats->getUserRoundTransactions($iKey, @$_SESSION['USERDATA']['id']); + $iHeight = $_REQUEST['height']; + } + $_REQUEST['height'] = $iHeight; + + $iPPLNSShares = 0; + $aDetailsForBlockHeight = $roundstats->getDetailsForBlockHeight($iHeight); + $aRoundShareStats = $roundstats->getRoundStatsForAccounts($iHeight); + $aUserRoundTransactions = $roundstats->getAllRoundTransactions($iHeight); + + if ($config['payout_system'] == 'pplns') { + $aPPLNSRoundShares = $roundstats->getPPLNSRoundStatsForAccounts($iHeight); + foreach($aPPLNSRoundShares as $aData) { + $iPPLNSShares += $aData['pplns_valid']; + } + $block_avg = $block->getAvgBlockShares($iHeight, $config['pplns']['blockavg']['blockcount']); + $smarty->assign('PPLNSROUNDSHARES', $aPPLNSRoundShares); + $smarty->assign("PPLNSSHARES", $iPPLNSShares); + $smarty->assign("BLOCKAVGCOUNT", $config['pplns']['blockavg']['blockcount']); + $smarty->assign("BLOCKAVERAGE", $block_avg ); } // Propagate content our template diff --git a/public/include/pages/statistics/uptime.inc.php b/public/include/pages/statistics/uptime.inc.php new file mode 100644 index 00000000..81a12799 --- /dev/null +++ b/public/include/pages/statistics/uptime.inc.php @@ -0,0 +1,27 @@ +isCached('master.tpl', $smarty_cache_key)) { + $debug->append('No cached version available, fetching from backend', 3); + if ($setting->getValue('monitoring_uptimerobot_api_keys') && $aStatus = $monitoring->getUptimeRobotStatus()) { + $smarty->assign("STATUS", $aStatus); + $smarty->assign("UPDATED", $setting->getValue('monitoring_uptimerobot_lastcheck')); + $smarty->assign("CODES", array( + 0 => 'Paused', + 1 => 'Unchecked', + 2 => 'Up', + 8 => 'Down', + 9 => 'Down' + )); + $smarty->assign("CONTENT", "default.tpl"); + } else { + $_SESSION['POPUP'][] = array('CONTENT' => 'UptimeRobot API Key not configured.', 'TYPE' => 'info'); + $smarty->assign("CONTENT", ""); + } +} else { + $debug->append('Using cached page', 3); +} + +?> diff --git a/public/include/smarty_globals.inc.php b/public/include/smarty_globals.inc.php index 8beb33b0..a9424836 100644 --- a/public/include/smarty_globals.inc.php +++ b/public/include/smarty_globals.inc.php @@ -65,9 +65,12 @@ $aGlobal = array( 'price' => $setting->getValue('price'), 'disable_mp' => $setting->getValue('disable_mp'), 'config' => array( + 'algorithm' => $config['algorithm'], + 'target_bits' => $config['target_bits'], 'accounts' => $config['accounts'], 'disable_invitations' => $setting->getValue('disable_invitations'), 'disable_notifications' => $setting->getValue('disable_notifications'), + 'monitoring_uptimerobot_api_keys' => $setting->getValue('monitoring_uptimerobot_api_keys'), 'statistics_ajax_refresh_interval' => $statistics_ajax_refresh_interval, 'price' => array( 'currency' => $config['price']['currency'] ), 'targetdiff' => $config['difficulty'], @@ -89,31 +92,21 @@ $aGlobal['website']['email'] = $setting->getValue('website_email'); $aGlobal['website']['api']['disabled'] = $setting->getValue('disable_api'); $aGlobal['website']['blockexplorer']['disabled'] = $setting->getValue('website_blockexplorer_disabled'); $aGlobal['website']['chaininfo']['disabled'] = $setting->getValue('website_chaininfo_disabled'); +$aGlobal['website']['donors']['disabled'] = $setting->getValue('disable_donors'); +$aGlobal['website']['about']['disabled'] = $setting->getValue('disable_about'); $setting->getValue('website_blockexplorer_url') ? $aGlobal['website']['blockexplorer']['url'] = $setting->getValue('website_blockexplorer_url') : $aGlobal['website']['blockexplorer']['url'] = 'http://explorer.litecoin.net/block/'; $setting->getValue('website_chaininfo_url') ? $aGlobal['website']['chaininfo']['url'] = $setting->getValue('website_chaininfo_url') : $aGlobal['website']['chaininfo']['url'] = 'http://allchains.info'; +// Google Analytics +$aGlobal['statistics']['analytics']['enabled'] = $setting->getValue('statistics_analytics_enabled'); +$aGlobal['statistics']['analytics']['code'] = $setting->getValue('statistics_analytics_code'); + // ACLs $aGlobal['acl']['pool']['statistics'] = $setting->getValue('acl_pool_statistics'); $aGlobal['acl']['block']['statistics'] = $setting->getValue('acl_block_statistics'); $aGlobal['acl']['round']['statistics'] = $setting->getValue('acl_round_statistics'); - -// We support some dynamic reward targets but fall back to our fixed value -// Special calculations for PPS Values based on reward_type setting and/or available blocks -if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) { - $pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount'])); -} else { - if ($config['pps']['reward']['type'] == 'block') { - if ($aLastBlock = $block->getLast()) { - $pps_reward = $aLastBlock['amount']; - } else { - $pps_reward = $config['pps']['reward']['default']; - } - } else { - $pps_reward = $config['pps']['reward']['default']; - } -} - -$aGlobal['ppsvalue'] = number_format(round($pps_reward / (pow(2,32) * $dDifficulty) * pow(2, $config['pps_target']), 12) ,12); +$aGlobal['acl']['blockfinder']['statistics'] = $setting->getValue('acl_blockfinder_statistics'); +$aGlobal['acl']['uptime']['statistics'] = $setting->getValue('acl_uptime_statistics'); // We don't want these session infos cached if (@$_SESSION['USERDATA']['id']) { @@ -122,17 +115,16 @@ if (@$_SESSION['USERDATA']['id']) { // Other userdata that we can cache savely $aGlobal['userdata']['shares'] = $statistics->getUserShares($_SESSION['USERDATA']['id']); - $aGlobal['userdata']['hashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']) * $dPersonalHashrateModifier; + $aGlobal['userdata']['rawhashrate'] = $statistics->getUserHashrate($_SESSION['USERDATA']['id']); + $aGlobal['userdata']['hashrate'] = $aGlobal['userdata']['rawhashrate'] * $dPersonalHashrateModifier; $aGlobal['userdata']['sharerate'] = $statistics->getUserSharerate($_SESSION['USERDATA']['id']); switch ($config['payout_system']) { - case 'prop' || 'pplns': + case 'prop': // Some estimations $aEstimates = $statistics->getUserEstimates($aRoundShares, $aGlobal['userdata']['shares'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees']); - $aGlobal['userdata']['est_block'] = $aEstimates['block']; - $aGlobal['userdata']['est_fee'] = $aEstimates['fee']; - $aGlobal['userdata']['est_donation'] = $aEstimates['donation']; - $aGlobal['userdata']['est_payout'] = $aEstimates['payout']; + $aGlobal['userdata']['estimates'] = $aEstimates; + break; case 'pplns': $aGlobal['pplns']['target'] = $config['pplns']['shares']['default']; if ($aLastBlock = $block->getLast()) { @@ -140,8 +132,31 @@ if (@$_SESSION['USERDATA']['id']) { $aGlobal['pplns']['target'] = $iAvgBlockShares; } } + $aEstimates = $statistics->getUserEstimates($aRoundShares, $aGlobal['userdata']['shares'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees']); + $aGlobal['userdata']['estimates'] = $aEstimates; break; case 'pps': + // We support some dynamic reward targets but fall back to our fixed value + // Special calculations for PPS Values based on reward_type setting and/or available blocks + if ($config['pps']['reward']['type'] == 'blockavg' && $block->getBlockCount() > 0) { + $pps_reward = round($block->getAvgBlockReward($config['pps']['blockavg']['blockcount'])); + } else { + if ($config['pps']['reward']['type'] == 'block') { + if ($aLastBlock = $block->getLast()) { + $pps_reward = $aLastBlock['amount']; + } else { + $pps_reward = $config['pps']['reward']['default']; + } + } else { + $pps_reward = $config['pps']['reward']['default']; + } + } + + $aGlobal['userdata']['pps']['unpaidshares'] = $statistics->getUserUnpaidPPSShares($_SESSION['USERDATA']['id'], $setting->getValue('pps_last_share_id')); + $aGlobal['ppsvalue'] = number_format(round($pps_reward / (pow(2, $config['target_bits']) * $dDifficulty), 12) ,12); + $aGlobal['poolppsvalue'] = $aGlobal['ppsvalue'] * pow(2, $config['difficulty'] - 16); + $aGlobal['userdata']['sharedifficulty'] = $statistics->getUserShareDifficulty($_SESSION['USERDATA']['id']); + $aGlobal['userdata']['estimates'] = $statistics->getUserEstimates($aGlobal['userdata']['sharerate'], $aGlobal['userdata']['sharedifficulty'], $aGlobal['userdata']['donate_percent'], $aGlobal['userdata']['no_fees'], $aGlobal['ppsvalue']); break; } @@ -162,5 +177,6 @@ $smarty->assign('DEBUG', DEBUG); // Make it available in Smarty $smarty->assign('PATH', 'site_assets/' . THEME); +$smarty->assign('GLOBALASSETS', 'site_assets/global'); $smarty->assign('GLOBAL', $aGlobal); ?> diff --git a/public/index.php b/public/index.php index f8ccb438..db94059e 100644 --- a/public/index.php +++ b/public/index.php @@ -51,7 +51,13 @@ if (is_dir(INCLUDE_DIR . '/pages/')) { } // Set a default action here if no page has been requested -$page = isset($_REQUEST['page']) && isset($arrPages[$_REQUEST['page']]) ? $_REQUEST['page'] : 'home'; +if (isset($_REQUEST['page']) && isset($arrPages[$_REQUEST['page']])) { + $page = $_REQUEST['page']; +} else if (isset($_REQUEST['page']) && ! isset($arrPages[$_REQUEST['page']])) { + $page = 'error'; +} else { + $page = 'home'; +} // Create our pages array from existing files if (is_dir(INCLUDE_DIR . '/pages/' . $page)) { diff --git a/public/site_assets/global/images/flags/ad.png b/public/site_assets/global/images/flags/ad.png new file mode 100755 index 00000000..625ca84f Binary files /dev/null and b/public/site_assets/global/images/flags/ad.png differ diff --git a/public/site_assets/global/images/flags/ae.png b/public/site_assets/global/images/flags/ae.png new file mode 100755 index 00000000..ef3a1ecf Binary files /dev/null and b/public/site_assets/global/images/flags/ae.png differ diff --git a/public/site_assets/global/images/flags/af.png b/public/site_assets/global/images/flags/af.png new file mode 100755 index 00000000..a4742e29 Binary files /dev/null and b/public/site_assets/global/images/flags/af.png differ diff --git a/public/site_assets/global/images/flags/ag.png b/public/site_assets/global/images/flags/ag.png new file mode 100755 index 00000000..556d5504 Binary files /dev/null and b/public/site_assets/global/images/flags/ag.png differ diff --git a/public/site_assets/global/images/flags/ai.png b/public/site_assets/global/images/flags/ai.png new file mode 100755 index 00000000..74ed29d9 Binary files /dev/null and b/public/site_assets/global/images/flags/ai.png differ diff --git a/public/site_assets/global/images/flags/al.png b/public/site_assets/global/images/flags/al.png new file mode 100755 index 00000000..92354cb6 Binary files /dev/null and b/public/site_assets/global/images/flags/al.png differ diff --git a/public/site_assets/global/images/flags/am.png b/public/site_assets/global/images/flags/am.png new file mode 100755 index 00000000..344a2a86 Binary files /dev/null and b/public/site_assets/global/images/flags/am.png differ diff --git a/public/site_assets/global/images/flags/an.png b/public/site_assets/global/images/flags/an.png new file mode 100755 index 00000000..633e4b89 Binary files /dev/null and b/public/site_assets/global/images/flags/an.png differ diff --git a/public/site_assets/global/images/flags/ao.png b/public/site_assets/global/images/flags/ao.png new file mode 100644 index 00000000..bcbd1d6d Binary files /dev/null and b/public/site_assets/global/images/flags/ao.png differ diff --git a/public/site_assets/global/images/flags/ar.png b/public/site_assets/global/images/flags/ar.png new file mode 100755 index 00000000..e5ef8f1f Binary files /dev/null and b/public/site_assets/global/images/flags/ar.png differ diff --git a/public/site_assets/global/images/flags/as.png b/public/site_assets/global/images/flags/as.png new file mode 100755 index 00000000..32f30e4c Binary files /dev/null and b/public/site_assets/global/images/flags/as.png differ diff --git a/public/site_assets/global/images/flags/at.png b/public/site_assets/global/images/flags/at.png new file mode 100755 index 00000000..0f15f34f Binary files /dev/null and b/public/site_assets/global/images/flags/at.png differ diff --git a/public/site_assets/global/images/flags/au.png b/public/site_assets/global/images/flags/au.png new file mode 100755 index 00000000..a01389a7 Binary files /dev/null and b/public/site_assets/global/images/flags/au.png differ diff --git a/public/site_assets/global/images/flags/aw.png b/public/site_assets/global/images/flags/aw.png new file mode 100755 index 00000000..a3579c2d Binary files /dev/null and b/public/site_assets/global/images/flags/aw.png differ diff --git a/public/site_assets/global/images/flags/ax.png b/public/site_assets/global/images/flags/ax.png new file mode 100755 index 00000000..1eea80a7 Binary files /dev/null and b/public/site_assets/global/images/flags/ax.png differ diff --git a/public/site_assets/global/images/flags/az.png b/public/site_assets/global/images/flags/az.png new file mode 100755 index 00000000..4ee9fe5c Binary files /dev/null and b/public/site_assets/global/images/flags/az.png differ diff --git a/public/site_assets/global/images/flags/ba.png b/public/site_assets/global/images/flags/ba.png new file mode 100755 index 00000000..c7749924 Binary files /dev/null and b/public/site_assets/global/images/flags/ba.png differ diff --git a/public/site_assets/global/images/flags/bb.png b/public/site_assets/global/images/flags/bb.png new file mode 100755 index 00000000..0df19c71 Binary files /dev/null and b/public/site_assets/global/images/flags/bb.png differ diff --git a/public/site_assets/global/images/flags/bd.png b/public/site_assets/global/images/flags/bd.png new file mode 100755 index 00000000..076a8bf8 Binary files /dev/null and b/public/site_assets/global/images/flags/bd.png differ diff --git a/public/site_assets/global/images/flags/be.png b/public/site_assets/global/images/flags/be.png new file mode 100755 index 00000000..d86ebc80 Binary files /dev/null and b/public/site_assets/global/images/flags/be.png differ diff --git a/public/site_assets/global/images/flags/bf.png b/public/site_assets/global/images/flags/bf.png new file mode 100755 index 00000000..ab5ce8fe Binary files /dev/null and b/public/site_assets/global/images/flags/bf.png differ diff --git a/public/site_assets/global/images/flags/bg.png b/public/site_assets/global/images/flags/bg.png new file mode 100755 index 00000000..0469f060 Binary files /dev/null and b/public/site_assets/global/images/flags/bg.png differ diff --git a/public/site_assets/global/images/flags/bh.png b/public/site_assets/global/images/flags/bh.png new file mode 100755 index 00000000..ea8ce687 Binary files /dev/null and b/public/site_assets/global/images/flags/bh.png differ diff --git a/public/site_assets/global/images/flags/bi.png b/public/site_assets/global/images/flags/bi.png new file mode 100755 index 00000000..5cc2e30c Binary files /dev/null and b/public/site_assets/global/images/flags/bi.png differ diff --git a/public/site_assets/global/images/flags/bj.png b/public/site_assets/global/images/flags/bj.png new file mode 100755 index 00000000..1cc8b458 Binary files /dev/null and b/public/site_assets/global/images/flags/bj.png differ diff --git a/public/site_assets/global/images/flags/bm.png b/public/site_assets/global/images/flags/bm.png new file mode 100755 index 00000000..c0c7aead Binary files /dev/null and b/public/site_assets/global/images/flags/bm.png differ diff --git a/public/site_assets/global/images/flags/bn.png b/public/site_assets/global/images/flags/bn.png new file mode 100755 index 00000000..8fb09849 Binary files /dev/null and b/public/site_assets/global/images/flags/bn.png differ diff --git a/public/site_assets/global/images/flags/bo.png b/public/site_assets/global/images/flags/bo.png new file mode 100755 index 00000000..ce7ba522 Binary files /dev/null and b/public/site_assets/global/images/flags/bo.png differ diff --git a/public/site_assets/global/images/flags/br.png b/public/site_assets/global/images/flags/br.png new file mode 100755 index 00000000..9b1a5538 Binary files /dev/null and b/public/site_assets/global/images/flags/br.png differ diff --git a/public/site_assets/global/images/flags/bs.png b/public/site_assets/global/images/flags/bs.png new file mode 100755 index 00000000..639fa6cf Binary files /dev/null and b/public/site_assets/global/images/flags/bs.png differ diff --git a/public/site_assets/global/images/flags/bt.png b/public/site_assets/global/images/flags/bt.png new file mode 100755 index 00000000..1d512dff Binary files /dev/null and b/public/site_assets/global/images/flags/bt.png differ diff --git a/public/site_assets/global/images/flags/bv.png b/public/site_assets/global/images/flags/bv.png new file mode 100755 index 00000000..160b6b5b Binary files /dev/null and b/public/site_assets/global/images/flags/bv.png differ diff --git a/public/site_assets/global/images/flags/bw.png b/public/site_assets/global/images/flags/bw.png new file mode 100755 index 00000000..fcb10394 Binary files /dev/null and b/public/site_assets/global/images/flags/bw.png differ diff --git a/public/site_assets/global/images/flags/by.png b/public/site_assets/global/images/flags/by.png new file mode 100755 index 00000000..504774ec Binary files /dev/null and b/public/site_assets/global/images/flags/by.png differ diff --git a/public/site_assets/global/images/flags/bz.png b/public/site_assets/global/images/flags/bz.png new file mode 100755 index 00000000..be63ee1c Binary files /dev/null and b/public/site_assets/global/images/flags/bz.png differ diff --git a/public/site_assets/global/images/flags/ca.png b/public/site_assets/global/images/flags/ca.png new file mode 100755 index 00000000..1f204193 Binary files /dev/null and b/public/site_assets/global/images/flags/ca.png differ diff --git a/public/site_assets/global/images/flags/catalonia.png b/public/site_assets/global/images/flags/catalonia.png new file mode 100644 index 00000000..5041e308 Binary files /dev/null and b/public/site_assets/global/images/flags/catalonia.png differ diff --git a/public/site_assets/global/images/flags/cc.png b/public/site_assets/global/images/flags/cc.png new file mode 100755 index 00000000..aed3d3b4 Binary files /dev/null and b/public/site_assets/global/images/flags/cc.png differ diff --git a/public/site_assets/global/images/flags/cd.png b/public/site_assets/global/images/flags/cd.png new file mode 100644 index 00000000..5e489424 Binary files /dev/null and b/public/site_assets/global/images/flags/cd.png differ diff --git a/public/site_assets/global/images/flags/cf.png b/public/site_assets/global/images/flags/cf.png new file mode 100755 index 00000000..da687bdc Binary files /dev/null and b/public/site_assets/global/images/flags/cf.png differ diff --git a/public/site_assets/global/images/flags/cg.png b/public/site_assets/global/images/flags/cg.png new file mode 100755 index 00000000..a859792e Binary files /dev/null and b/public/site_assets/global/images/flags/cg.png differ diff --git a/public/site_assets/global/images/flags/ch.png b/public/site_assets/global/images/flags/ch.png new file mode 100755 index 00000000..242ec01a Binary files /dev/null and b/public/site_assets/global/images/flags/ch.png differ diff --git a/public/site_assets/global/images/flags/ci.png b/public/site_assets/global/images/flags/ci.png new file mode 100755 index 00000000..3f2c62eb Binary files /dev/null and b/public/site_assets/global/images/flags/ci.png differ diff --git a/public/site_assets/global/images/flags/ck.png b/public/site_assets/global/images/flags/ck.png new file mode 100755 index 00000000..746d3d6f Binary files /dev/null and b/public/site_assets/global/images/flags/ck.png differ diff --git a/public/site_assets/global/images/flags/cl.png b/public/site_assets/global/images/flags/cl.png new file mode 100755 index 00000000..29c6d61b Binary files /dev/null and b/public/site_assets/global/images/flags/cl.png differ diff --git a/public/site_assets/global/images/flags/cm.png b/public/site_assets/global/images/flags/cm.png new file mode 100755 index 00000000..f65c5bd5 Binary files /dev/null and b/public/site_assets/global/images/flags/cm.png differ diff --git a/public/site_assets/global/images/flags/cn.png b/public/site_assets/global/images/flags/cn.png new file mode 100755 index 00000000..89144146 Binary files /dev/null and b/public/site_assets/global/images/flags/cn.png differ diff --git a/public/site_assets/global/images/flags/co.png b/public/site_assets/global/images/flags/co.png new file mode 100755 index 00000000..a118ff4a Binary files /dev/null and b/public/site_assets/global/images/flags/co.png differ diff --git a/public/site_assets/global/images/flags/cr.png b/public/site_assets/global/images/flags/cr.png new file mode 100755 index 00000000..c7a37317 Binary files /dev/null and b/public/site_assets/global/images/flags/cr.png differ diff --git a/public/site_assets/global/images/flags/cs.png b/public/site_assets/global/images/flags/cs.png new file mode 100755 index 00000000..8254790c Binary files /dev/null and b/public/site_assets/global/images/flags/cs.png differ diff --git a/public/site_assets/global/images/flags/cu.png b/public/site_assets/global/images/flags/cu.png new file mode 100755 index 00000000..083f1d61 Binary files /dev/null and b/public/site_assets/global/images/flags/cu.png differ diff --git a/public/site_assets/global/images/flags/cv.png b/public/site_assets/global/images/flags/cv.png new file mode 100755 index 00000000..a63f7eaf Binary files /dev/null and b/public/site_assets/global/images/flags/cv.png differ diff --git a/public/site_assets/global/images/flags/cx.png b/public/site_assets/global/images/flags/cx.png new file mode 100755 index 00000000..48e31adb Binary files /dev/null and b/public/site_assets/global/images/flags/cx.png differ diff --git a/public/site_assets/global/images/flags/cy.png b/public/site_assets/global/images/flags/cy.png new file mode 100755 index 00000000..5b1ad6c0 Binary files /dev/null and b/public/site_assets/global/images/flags/cy.png differ diff --git a/public/site_assets/global/images/flags/cz.png b/public/site_assets/global/images/flags/cz.png new file mode 100755 index 00000000..c8403dd2 Binary files /dev/null and b/public/site_assets/global/images/flags/cz.png differ diff --git a/public/site_assets/global/images/flags/de.png b/public/site_assets/global/images/flags/de.png new file mode 100755 index 00000000..ac4a9773 Binary files /dev/null and b/public/site_assets/global/images/flags/de.png differ diff --git a/public/site_assets/global/images/flags/dj.png b/public/site_assets/global/images/flags/dj.png new file mode 100755 index 00000000..582af364 Binary files /dev/null and b/public/site_assets/global/images/flags/dj.png differ diff --git a/public/site_assets/global/images/flags/dk.png b/public/site_assets/global/images/flags/dk.png new file mode 100755 index 00000000..e2993d3c Binary files /dev/null and b/public/site_assets/global/images/flags/dk.png differ diff --git a/public/site_assets/global/images/flags/dm.png b/public/site_assets/global/images/flags/dm.png new file mode 100755 index 00000000..5fbffcba Binary files /dev/null and b/public/site_assets/global/images/flags/dm.png differ diff --git a/public/site_assets/global/images/flags/do.png b/public/site_assets/global/images/flags/do.png new file mode 100755 index 00000000..5a04932d Binary files /dev/null and b/public/site_assets/global/images/flags/do.png differ diff --git a/public/site_assets/global/images/flags/dz.png b/public/site_assets/global/images/flags/dz.png new file mode 100755 index 00000000..335c2391 Binary files /dev/null and b/public/site_assets/global/images/flags/dz.png differ diff --git a/public/site_assets/global/images/flags/ec.png b/public/site_assets/global/images/flags/ec.png new file mode 100755 index 00000000..0caa0b1e Binary files /dev/null and b/public/site_assets/global/images/flags/ec.png differ diff --git a/public/site_assets/global/images/flags/ee.png b/public/site_assets/global/images/flags/ee.png new file mode 100755 index 00000000..0c82efb7 Binary files /dev/null and b/public/site_assets/global/images/flags/ee.png differ diff --git a/public/site_assets/global/images/flags/eg.png b/public/site_assets/global/images/flags/eg.png new file mode 100755 index 00000000..8a3f7a10 Binary files /dev/null and b/public/site_assets/global/images/flags/eg.png differ diff --git a/public/site_assets/global/images/flags/eh.png b/public/site_assets/global/images/flags/eh.png new file mode 100755 index 00000000..90a1195b Binary files /dev/null and b/public/site_assets/global/images/flags/eh.png differ diff --git a/public/site_assets/global/images/flags/england.png b/public/site_assets/global/images/flags/england.png new file mode 100755 index 00000000..3a7311d5 Binary files /dev/null and b/public/site_assets/global/images/flags/england.png differ diff --git a/public/site_assets/global/images/flags/er.png b/public/site_assets/global/images/flags/er.png new file mode 100755 index 00000000..13065ae9 Binary files /dev/null and b/public/site_assets/global/images/flags/er.png differ diff --git a/public/site_assets/global/images/flags/es.png b/public/site_assets/global/images/flags/es.png new file mode 100755 index 00000000..c2de2d71 Binary files /dev/null and b/public/site_assets/global/images/flags/es.png differ diff --git a/public/site_assets/global/images/flags/et.png b/public/site_assets/global/images/flags/et.png new file mode 100755 index 00000000..2e893fa0 Binary files /dev/null and b/public/site_assets/global/images/flags/et.png differ diff --git a/public/site_assets/global/images/flags/europeanunion.png b/public/site_assets/global/images/flags/europeanunion.png new file mode 100644 index 00000000..d6d87115 Binary files /dev/null and b/public/site_assets/global/images/flags/europeanunion.png differ diff --git a/public/site_assets/global/images/flags/fam.png b/public/site_assets/global/images/flags/fam.png new file mode 100755 index 00000000..cf50c759 Binary files /dev/null and b/public/site_assets/global/images/flags/fam.png differ diff --git a/public/site_assets/global/images/flags/fi.png b/public/site_assets/global/images/flags/fi.png new file mode 100755 index 00000000..14ec091b Binary files /dev/null and b/public/site_assets/global/images/flags/fi.png differ diff --git a/public/site_assets/global/images/flags/fj.png b/public/site_assets/global/images/flags/fj.png new file mode 100755 index 00000000..cee99889 Binary files /dev/null and b/public/site_assets/global/images/flags/fj.png differ diff --git a/public/site_assets/global/images/flags/fk.png b/public/site_assets/global/images/flags/fk.png new file mode 100755 index 00000000..ceaeb27d Binary files /dev/null and b/public/site_assets/global/images/flags/fk.png differ diff --git a/public/site_assets/global/images/flags/fm.png b/public/site_assets/global/images/flags/fm.png new file mode 100755 index 00000000..066bb247 Binary files /dev/null and b/public/site_assets/global/images/flags/fm.png differ diff --git a/public/site_assets/global/images/flags/fo.png b/public/site_assets/global/images/flags/fo.png new file mode 100755 index 00000000..cbceb809 Binary files /dev/null and b/public/site_assets/global/images/flags/fo.png differ diff --git a/public/site_assets/global/images/flags/fr.png b/public/site_assets/global/images/flags/fr.png new file mode 100755 index 00000000..8332c4ec Binary files /dev/null and b/public/site_assets/global/images/flags/fr.png differ diff --git a/public/site_assets/global/images/flags/ga.png b/public/site_assets/global/images/flags/ga.png new file mode 100755 index 00000000..0e0d4343 Binary files /dev/null and b/public/site_assets/global/images/flags/ga.png differ diff --git a/public/site_assets/global/images/flags/gb.png b/public/site_assets/global/images/flags/gb.png new file mode 100644 index 00000000..ff701e19 Binary files /dev/null and b/public/site_assets/global/images/flags/gb.png differ diff --git a/public/site_assets/global/images/flags/gd.png b/public/site_assets/global/images/flags/gd.png new file mode 100755 index 00000000..9ab57f54 Binary files /dev/null and b/public/site_assets/global/images/flags/gd.png differ diff --git a/public/site_assets/global/images/flags/ge.png b/public/site_assets/global/images/flags/ge.png new file mode 100755 index 00000000..728d9707 Binary files /dev/null and b/public/site_assets/global/images/flags/ge.png differ diff --git a/public/site_assets/global/images/flags/gf.png b/public/site_assets/global/images/flags/gf.png new file mode 100755 index 00000000..8332c4ec Binary files /dev/null and b/public/site_assets/global/images/flags/gf.png differ diff --git a/public/site_assets/global/images/flags/gh.png b/public/site_assets/global/images/flags/gh.png new file mode 100755 index 00000000..4e2f8965 Binary files /dev/null and b/public/site_assets/global/images/flags/gh.png differ diff --git a/public/site_assets/global/images/flags/gi.png b/public/site_assets/global/images/flags/gi.png new file mode 100755 index 00000000..e76797f6 Binary files /dev/null and b/public/site_assets/global/images/flags/gi.png differ diff --git a/public/site_assets/global/images/flags/gl.png b/public/site_assets/global/images/flags/gl.png new file mode 100755 index 00000000..ef12a73b Binary files /dev/null and b/public/site_assets/global/images/flags/gl.png differ diff --git a/public/site_assets/global/images/flags/gm.png b/public/site_assets/global/images/flags/gm.png new file mode 100755 index 00000000..0720b667 Binary files /dev/null and b/public/site_assets/global/images/flags/gm.png differ diff --git a/public/site_assets/global/images/flags/gn.png b/public/site_assets/global/images/flags/gn.png new file mode 100755 index 00000000..ea660b01 Binary files /dev/null and b/public/site_assets/global/images/flags/gn.png differ diff --git a/public/site_assets/global/images/flags/gp.png b/public/site_assets/global/images/flags/gp.png new file mode 100755 index 00000000..dbb086d0 Binary files /dev/null and b/public/site_assets/global/images/flags/gp.png differ diff --git a/public/site_assets/global/images/flags/gq.png b/public/site_assets/global/images/flags/gq.png new file mode 100755 index 00000000..ebe20a28 Binary files /dev/null and b/public/site_assets/global/images/flags/gq.png differ diff --git a/public/site_assets/global/images/flags/gr.png b/public/site_assets/global/images/flags/gr.png new file mode 100755 index 00000000..8651ade7 Binary files /dev/null and b/public/site_assets/global/images/flags/gr.png differ diff --git a/public/site_assets/global/images/flags/gs.png b/public/site_assets/global/images/flags/gs.png new file mode 100755 index 00000000..7ef0bf59 Binary files /dev/null and b/public/site_assets/global/images/flags/gs.png differ diff --git a/public/site_assets/global/images/flags/gt.png b/public/site_assets/global/images/flags/gt.png new file mode 100755 index 00000000..c43a70d3 Binary files /dev/null and b/public/site_assets/global/images/flags/gt.png differ diff --git a/public/site_assets/global/images/flags/gu.png b/public/site_assets/global/images/flags/gu.png new file mode 100755 index 00000000..92f37c05 Binary files /dev/null and b/public/site_assets/global/images/flags/gu.png differ diff --git a/public/site_assets/global/images/flags/gw.png b/public/site_assets/global/images/flags/gw.png new file mode 100755 index 00000000..b37bcf06 Binary files /dev/null and b/public/site_assets/global/images/flags/gw.png differ diff --git a/public/site_assets/global/images/flags/gy.png b/public/site_assets/global/images/flags/gy.png new file mode 100755 index 00000000..22cbe2f5 Binary files /dev/null and b/public/site_assets/global/images/flags/gy.png differ diff --git a/public/site_assets/global/images/flags/hk.png b/public/site_assets/global/images/flags/hk.png new file mode 100755 index 00000000..d5c380ca Binary files /dev/null and b/public/site_assets/global/images/flags/hk.png differ diff --git a/public/site_assets/global/images/flags/hm.png b/public/site_assets/global/images/flags/hm.png new file mode 100755 index 00000000..a01389a7 Binary files /dev/null and b/public/site_assets/global/images/flags/hm.png differ diff --git a/public/site_assets/global/images/flags/hn.png b/public/site_assets/global/images/flags/hn.png new file mode 100755 index 00000000..96f83885 Binary files /dev/null and b/public/site_assets/global/images/flags/hn.png differ diff --git a/public/site_assets/global/images/flags/hr.png b/public/site_assets/global/images/flags/hr.png new file mode 100755 index 00000000..696b5154 Binary files /dev/null and b/public/site_assets/global/images/flags/hr.png differ diff --git a/public/site_assets/global/images/flags/ht.png b/public/site_assets/global/images/flags/ht.png new file mode 100755 index 00000000..416052af Binary files /dev/null and b/public/site_assets/global/images/flags/ht.png differ diff --git a/public/site_assets/global/images/flags/hu.png b/public/site_assets/global/images/flags/hu.png new file mode 100755 index 00000000..7baafe44 Binary files /dev/null and b/public/site_assets/global/images/flags/hu.png differ diff --git a/public/site_assets/global/images/flags/id.png b/public/site_assets/global/images/flags/id.png new file mode 100755 index 00000000..c6bc0faf Binary files /dev/null and b/public/site_assets/global/images/flags/id.png differ diff --git a/public/site_assets/global/images/flags/ie.png b/public/site_assets/global/images/flags/ie.png new file mode 100755 index 00000000..26baa31e Binary files /dev/null and b/public/site_assets/global/images/flags/ie.png differ diff --git a/public/site_assets/global/images/flags/il.png b/public/site_assets/global/images/flags/il.png new file mode 100755 index 00000000..2ca772d0 Binary files /dev/null and b/public/site_assets/global/images/flags/il.png differ diff --git a/public/site_assets/global/images/flags/in.png b/public/site_assets/global/images/flags/in.png new file mode 100755 index 00000000..e4d7e81a Binary files /dev/null and b/public/site_assets/global/images/flags/in.png differ diff --git a/public/site_assets/global/images/flags/io.png b/public/site_assets/global/images/flags/io.png new file mode 100755 index 00000000..3e74b6a3 Binary files /dev/null and b/public/site_assets/global/images/flags/io.png differ diff --git a/public/site_assets/global/images/flags/iq.png b/public/site_assets/global/images/flags/iq.png new file mode 100755 index 00000000..878a3514 Binary files /dev/null and b/public/site_assets/global/images/flags/iq.png differ diff --git a/public/site_assets/global/images/flags/ir.png b/public/site_assets/global/images/flags/ir.png new file mode 100755 index 00000000..c5fd136a Binary files /dev/null and b/public/site_assets/global/images/flags/ir.png differ diff --git a/public/site_assets/global/images/flags/is.png b/public/site_assets/global/images/flags/is.png new file mode 100755 index 00000000..b8f6d0f0 Binary files /dev/null and b/public/site_assets/global/images/flags/is.png differ diff --git a/public/site_assets/global/images/flags/it.png b/public/site_assets/global/images/flags/it.png new file mode 100755 index 00000000..89692f74 Binary files /dev/null and b/public/site_assets/global/images/flags/it.png differ diff --git a/public/site_assets/global/images/flags/jm.png b/public/site_assets/global/images/flags/jm.png new file mode 100755 index 00000000..7be119e0 Binary files /dev/null and b/public/site_assets/global/images/flags/jm.png differ diff --git a/public/site_assets/global/images/flags/jo.png b/public/site_assets/global/images/flags/jo.png new file mode 100755 index 00000000..11bd4972 Binary files /dev/null and b/public/site_assets/global/images/flags/jo.png differ diff --git a/public/site_assets/global/images/flags/jp.png b/public/site_assets/global/images/flags/jp.png new file mode 100755 index 00000000..325fbad3 Binary files /dev/null and b/public/site_assets/global/images/flags/jp.png differ diff --git a/public/site_assets/global/images/flags/ke.png b/public/site_assets/global/images/flags/ke.png new file mode 100755 index 00000000..51879adf Binary files /dev/null and b/public/site_assets/global/images/flags/ke.png differ diff --git a/public/site_assets/global/images/flags/kg.png b/public/site_assets/global/images/flags/kg.png new file mode 100755 index 00000000..0a818f67 Binary files /dev/null and b/public/site_assets/global/images/flags/kg.png differ diff --git a/public/site_assets/global/images/flags/kh.png b/public/site_assets/global/images/flags/kh.png new file mode 100755 index 00000000..30f6bb1b Binary files /dev/null and b/public/site_assets/global/images/flags/kh.png differ diff --git a/public/site_assets/global/images/flags/ki.png b/public/site_assets/global/images/flags/ki.png new file mode 100755 index 00000000..2dcce4b3 Binary files /dev/null and b/public/site_assets/global/images/flags/ki.png differ diff --git a/public/site_assets/global/images/flags/km.png b/public/site_assets/global/images/flags/km.png new file mode 100755 index 00000000..812b2f56 Binary files /dev/null and b/public/site_assets/global/images/flags/km.png differ diff --git a/public/site_assets/global/images/flags/kn.png b/public/site_assets/global/images/flags/kn.png new file mode 100755 index 00000000..febd5b48 Binary files /dev/null and b/public/site_assets/global/images/flags/kn.png differ diff --git a/public/site_assets/global/images/flags/kp.png b/public/site_assets/global/images/flags/kp.png new file mode 100755 index 00000000..d3d509aa Binary files /dev/null and b/public/site_assets/global/images/flags/kp.png differ diff --git a/public/site_assets/global/images/flags/kr.png b/public/site_assets/global/images/flags/kr.png new file mode 100755 index 00000000..9c0a78eb Binary files /dev/null and b/public/site_assets/global/images/flags/kr.png differ diff --git a/public/site_assets/global/images/flags/kw.png b/public/site_assets/global/images/flags/kw.png new file mode 100755 index 00000000..96546da3 Binary files /dev/null and b/public/site_assets/global/images/flags/kw.png differ diff --git a/public/site_assets/global/images/flags/ky.png b/public/site_assets/global/images/flags/ky.png new file mode 100755 index 00000000..15c5f8e4 Binary files /dev/null and b/public/site_assets/global/images/flags/ky.png differ diff --git a/public/site_assets/global/images/flags/kz.png b/public/site_assets/global/images/flags/kz.png new file mode 100755 index 00000000..45a8c887 Binary files /dev/null and b/public/site_assets/global/images/flags/kz.png differ diff --git a/public/site_assets/global/images/flags/la.png b/public/site_assets/global/images/flags/la.png new file mode 100755 index 00000000..e28acd01 Binary files /dev/null and b/public/site_assets/global/images/flags/la.png differ diff --git a/public/site_assets/global/images/flags/lb.png b/public/site_assets/global/images/flags/lb.png new file mode 100755 index 00000000..d0d452bf Binary files /dev/null and b/public/site_assets/global/images/flags/lb.png differ diff --git a/public/site_assets/global/images/flags/lc.png b/public/site_assets/global/images/flags/lc.png new file mode 100644 index 00000000..a47d0655 Binary files /dev/null and b/public/site_assets/global/images/flags/lc.png differ diff --git a/public/site_assets/global/images/flags/li.png b/public/site_assets/global/images/flags/li.png new file mode 100755 index 00000000..6469909c Binary files /dev/null and b/public/site_assets/global/images/flags/li.png differ diff --git a/public/site_assets/global/images/flags/lk.png b/public/site_assets/global/images/flags/lk.png new file mode 100755 index 00000000..088aad6d Binary files /dev/null and b/public/site_assets/global/images/flags/lk.png differ diff --git a/public/site_assets/global/images/flags/lr.png b/public/site_assets/global/images/flags/lr.png new file mode 100755 index 00000000..89a5bc7e Binary files /dev/null and b/public/site_assets/global/images/flags/lr.png differ diff --git a/public/site_assets/global/images/flags/ls.png b/public/site_assets/global/images/flags/ls.png new file mode 100755 index 00000000..33fdef10 Binary files /dev/null and b/public/site_assets/global/images/flags/ls.png differ diff --git a/public/site_assets/global/images/flags/lt.png b/public/site_assets/global/images/flags/lt.png new file mode 100755 index 00000000..c8ef0da0 Binary files /dev/null and b/public/site_assets/global/images/flags/lt.png differ diff --git a/public/site_assets/global/images/flags/lu.png b/public/site_assets/global/images/flags/lu.png new file mode 100755 index 00000000..4cabba98 Binary files /dev/null and b/public/site_assets/global/images/flags/lu.png differ diff --git a/public/site_assets/global/images/flags/lv.png b/public/site_assets/global/images/flags/lv.png new file mode 100755 index 00000000..49b69981 Binary files /dev/null and b/public/site_assets/global/images/flags/lv.png differ diff --git a/public/site_assets/global/images/flags/ly.png b/public/site_assets/global/images/flags/ly.png new file mode 100755 index 00000000..b163a9f8 Binary files /dev/null and b/public/site_assets/global/images/flags/ly.png differ diff --git a/public/site_assets/global/images/flags/ma.png b/public/site_assets/global/images/flags/ma.png new file mode 100755 index 00000000..f3867702 Binary files /dev/null and b/public/site_assets/global/images/flags/ma.png differ diff --git a/public/site_assets/global/images/flags/mc.png b/public/site_assets/global/images/flags/mc.png new file mode 100755 index 00000000..1aa830f1 Binary files /dev/null and b/public/site_assets/global/images/flags/mc.png differ diff --git a/public/site_assets/global/images/flags/md.png b/public/site_assets/global/images/flags/md.png new file mode 100755 index 00000000..4e92c189 Binary files /dev/null and b/public/site_assets/global/images/flags/md.png differ diff --git a/public/site_assets/global/images/flags/me.png b/public/site_assets/global/images/flags/me.png new file mode 100644 index 00000000..ac725355 Binary files /dev/null and b/public/site_assets/global/images/flags/me.png differ diff --git a/public/site_assets/global/images/flags/mg.png b/public/site_assets/global/images/flags/mg.png new file mode 100755 index 00000000..d2715b3d Binary files /dev/null and b/public/site_assets/global/images/flags/mg.png differ diff --git a/public/site_assets/global/images/flags/mh.png b/public/site_assets/global/images/flags/mh.png new file mode 100755 index 00000000..fb523a8c Binary files /dev/null and b/public/site_assets/global/images/flags/mh.png differ diff --git a/public/site_assets/global/images/flags/mk.png b/public/site_assets/global/images/flags/mk.png new file mode 100755 index 00000000..db173aaf Binary files /dev/null and b/public/site_assets/global/images/flags/mk.png differ diff --git a/public/site_assets/global/images/flags/ml.png b/public/site_assets/global/images/flags/ml.png new file mode 100755 index 00000000..2cec8ba4 Binary files /dev/null and b/public/site_assets/global/images/flags/ml.png differ diff --git a/public/site_assets/global/images/flags/mm.png b/public/site_assets/global/images/flags/mm.png new file mode 100755 index 00000000..f464f67f Binary files /dev/null and b/public/site_assets/global/images/flags/mm.png differ diff --git a/public/site_assets/global/images/flags/mn.png b/public/site_assets/global/images/flags/mn.png new file mode 100755 index 00000000..9396355d Binary files /dev/null and b/public/site_assets/global/images/flags/mn.png differ diff --git a/public/site_assets/global/images/flags/mo.png b/public/site_assets/global/images/flags/mo.png new file mode 100755 index 00000000..deb801dd Binary files /dev/null and b/public/site_assets/global/images/flags/mo.png differ diff --git a/public/site_assets/global/images/flags/mp.png b/public/site_assets/global/images/flags/mp.png new file mode 100755 index 00000000..298d588b Binary files /dev/null and b/public/site_assets/global/images/flags/mp.png differ diff --git a/public/site_assets/global/images/flags/mq.png b/public/site_assets/global/images/flags/mq.png new file mode 100755 index 00000000..010143b3 Binary files /dev/null and b/public/site_assets/global/images/flags/mq.png differ diff --git a/public/site_assets/global/images/flags/mr.png b/public/site_assets/global/images/flags/mr.png new file mode 100755 index 00000000..319546b1 Binary files /dev/null and b/public/site_assets/global/images/flags/mr.png differ diff --git a/public/site_assets/global/images/flags/ms.png b/public/site_assets/global/images/flags/ms.png new file mode 100755 index 00000000..d4cbb433 Binary files /dev/null and b/public/site_assets/global/images/flags/ms.png differ diff --git a/public/site_assets/global/images/flags/mt.png b/public/site_assets/global/images/flags/mt.png new file mode 100755 index 00000000..00af9487 Binary files /dev/null and b/public/site_assets/global/images/flags/mt.png differ diff --git a/public/site_assets/global/images/flags/mu.png b/public/site_assets/global/images/flags/mu.png new file mode 100755 index 00000000..b7fdce1b Binary files /dev/null and b/public/site_assets/global/images/flags/mu.png differ diff --git a/public/site_assets/global/images/flags/mv.png b/public/site_assets/global/images/flags/mv.png new file mode 100755 index 00000000..5073d9ec Binary files /dev/null and b/public/site_assets/global/images/flags/mv.png differ diff --git a/public/site_assets/global/images/flags/mw.png b/public/site_assets/global/images/flags/mw.png new file mode 100755 index 00000000..13886e9f Binary files /dev/null and b/public/site_assets/global/images/flags/mw.png differ diff --git a/public/site_assets/global/images/flags/mx.png b/public/site_assets/global/images/flags/mx.png new file mode 100755 index 00000000..5bc58ab3 Binary files /dev/null and b/public/site_assets/global/images/flags/mx.png differ diff --git a/public/site_assets/global/images/flags/my.png b/public/site_assets/global/images/flags/my.png new file mode 100755 index 00000000..9034cbab Binary files /dev/null and b/public/site_assets/global/images/flags/my.png differ diff --git a/public/site_assets/global/images/flags/mz.png b/public/site_assets/global/images/flags/mz.png new file mode 100755 index 00000000..76405e06 Binary files /dev/null and b/public/site_assets/global/images/flags/mz.png differ diff --git a/public/site_assets/global/images/flags/na.png b/public/site_assets/global/images/flags/na.png new file mode 100755 index 00000000..63358c67 Binary files /dev/null and b/public/site_assets/global/images/flags/na.png differ diff --git a/public/site_assets/global/images/flags/nc.png b/public/site_assets/global/images/flags/nc.png new file mode 100755 index 00000000..2cad2837 Binary files /dev/null and b/public/site_assets/global/images/flags/nc.png differ diff --git a/public/site_assets/global/images/flags/ne.png b/public/site_assets/global/images/flags/ne.png new file mode 100755 index 00000000..d85f424f Binary files /dev/null and b/public/site_assets/global/images/flags/ne.png differ diff --git a/public/site_assets/global/images/flags/nf.png b/public/site_assets/global/images/flags/nf.png new file mode 100755 index 00000000..f9bcdda1 Binary files /dev/null and b/public/site_assets/global/images/flags/nf.png differ diff --git a/public/site_assets/global/images/flags/ng.png b/public/site_assets/global/images/flags/ng.png new file mode 100755 index 00000000..3eea2e02 Binary files /dev/null and b/public/site_assets/global/images/flags/ng.png differ diff --git a/public/site_assets/global/images/flags/ni.png b/public/site_assets/global/images/flags/ni.png new file mode 100755 index 00000000..3969aaaa Binary files /dev/null and b/public/site_assets/global/images/flags/ni.png differ diff --git a/public/site_assets/global/images/flags/nl.png b/public/site_assets/global/images/flags/nl.png new file mode 100755 index 00000000..fe44791e Binary files /dev/null and b/public/site_assets/global/images/flags/nl.png differ diff --git a/public/site_assets/global/images/flags/no.png b/public/site_assets/global/images/flags/no.png new file mode 100755 index 00000000..160b6b5b Binary files /dev/null and b/public/site_assets/global/images/flags/no.png differ diff --git a/public/site_assets/global/images/flags/np.png b/public/site_assets/global/images/flags/np.png new file mode 100755 index 00000000..aeb058b7 Binary files /dev/null and b/public/site_assets/global/images/flags/np.png differ diff --git a/public/site_assets/global/images/flags/nr.png b/public/site_assets/global/images/flags/nr.png new file mode 100755 index 00000000..705fc337 Binary files /dev/null and b/public/site_assets/global/images/flags/nr.png differ diff --git a/public/site_assets/global/images/flags/nu.png b/public/site_assets/global/images/flags/nu.png new file mode 100755 index 00000000..c3ce4aed Binary files /dev/null and b/public/site_assets/global/images/flags/nu.png differ diff --git a/public/site_assets/global/images/flags/nz.png b/public/site_assets/global/images/flags/nz.png new file mode 100755 index 00000000..10d6306d Binary files /dev/null and b/public/site_assets/global/images/flags/nz.png differ diff --git a/public/site_assets/global/images/flags/om.png b/public/site_assets/global/images/flags/om.png new file mode 100755 index 00000000..2ffba7e8 Binary files /dev/null and b/public/site_assets/global/images/flags/om.png differ diff --git a/public/site_assets/global/images/flags/pa.png b/public/site_assets/global/images/flags/pa.png new file mode 100755 index 00000000..9b2ee9a7 Binary files /dev/null and b/public/site_assets/global/images/flags/pa.png differ diff --git a/public/site_assets/global/images/flags/pe.png b/public/site_assets/global/images/flags/pe.png new file mode 100755 index 00000000..62a04977 Binary files /dev/null and b/public/site_assets/global/images/flags/pe.png differ diff --git a/public/site_assets/global/images/flags/pf.png b/public/site_assets/global/images/flags/pf.png new file mode 100755 index 00000000..771a0f65 Binary files /dev/null and b/public/site_assets/global/images/flags/pf.png differ diff --git a/public/site_assets/global/images/flags/pg.png b/public/site_assets/global/images/flags/pg.png new file mode 100755 index 00000000..10d62334 Binary files /dev/null and b/public/site_assets/global/images/flags/pg.png differ diff --git a/public/site_assets/global/images/flags/ph.png b/public/site_assets/global/images/flags/ph.png new file mode 100755 index 00000000..b89e1593 Binary files /dev/null and b/public/site_assets/global/images/flags/ph.png differ diff --git a/public/site_assets/global/images/flags/pk.png b/public/site_assets/global/images/flags/pk.png new file mode 100755 index 00000000..e9df70ca Binary files /dev/null and b/public/site_assets/global/images/flags/pk.png differ diff --git a/public/site_assets/global/images/flags/pl.png b/public/site_assets/global/images/flags/pl.png new file mode 100755 index 00000000..d413d010 Binary files /dev/null and b/public/site_assets/global/images/flags/pl.png differ diff --git a/public/site_assets/global/images/flags/pm.png b/public/site_assets/global/images/flags/pm.png new file mode 100755 index 00000000..ba91d2c7 Binary files /dev/null and b/public/site_assets/global/images/flags/pm.png differ diff --git a/public/site_assets/global/images/flags/pn.png b/public/site_assets/global/images/flags/pn.png new file mode 100755 index 00000000..aa9344f5 Binary files /dev/null and b/public/site_assets/global/images/flags/pn.png differ diff --git a/public/site_assets/global/images/flags/pr.png b/public/site_assets/global/images/flags/pr.png new file mode 100755 index 00000000..82d9130d Binary files /dev/null and b/public/site_assets/global/images/flags/pr.png differ diff --git a/public/site_assets/global/images/flags/ps.png b/public/site_assets/global/images/flags/ps.png new file mode 100755 index 00000000..f5f54776 Binary files /dev/null and b/public/site_assets/global/images/flags/ps.png differ diff --git a/public/site_assets/global/images/flags/pt.png b/public/site_assets/global/images/flags/pt.png new file mode 100755 index 00000000..ece79801 Binary files /dev/null and b/public/site_assets/global/images/flags/pt.png differ diff --git a/public/site_assets/global/images/flags/pw.png b/public/site_assets/global/images/flags/pw.png new file mode 100755 index 00000000..6178b254 Binary files /dev/null and b/public/site_assets/global/images/flags/pw.png differ diff --git a/public/site_assets/global/images/flags/py.png b/public/site_assets/global/images/flags/py.png new file mode 100755 index 00000000..cb8723c0 Binary files /dev/null and b/public/site_assets/global/images/flags/py.png differ diff --git a/public/site_assets/global/images/flags/qa.png b/public/site_assets/global/images/flags/qa.png new file mode 100755 index 00000000..ed4c621f Binary files /dev/null and b/public/site_assets/global/images/flags/qa.png differ diff --git a/public/site_assets/global/images/flags/re.png b/public/site_assets/global/images/flags/re.png new file mode 100755 index 00000000..8332c4ec Binary files /dev/null and b/public/site_assets/global/images/flags/re.png differ diff --git a/public/site_assets/global/images/flags/ro.png b/public/site_assets/global/images/flags/ro.png new file mode 100755 index 00000000..57e74a65 Binary files /dev/null and b/public/site_assets/global/images/flags/ro.png differ diff --git a/public/site_assets/global/images/flags/rs.png b/public/site_assets/global/images/flags/rs.png new file mode 100644 index 00000000..9439a5b6 Binary files /dev/null and b/public/site_assets/global/images/flags/rs.png differ diff --git a/public/site_assets/global/images/flags/ru.png b/public/site_assets/global/images/flags/ru.png new file mode 100755 index 00000000..47da4214 Binary files /dev/null and b/public/site_assets/global/images/flags/ru.png differ diff --git a/public/site_assets/global/images/flags/rw.png b/public/site_assets/global/images/flags/rw.png new file mode 100755 index 00000000..53564917 Binary files /dev/null and b/public/site_assets/global/images/flags/rw.png differ diff --git a/public/site_assets/global/images/flags/sa.png b/public/site_assets/global/images/flags/sa.png new file mode 100755 index 00000000..b4641c7e Binary files /dev/null and b/public/site_assets/global/images/flags/sa.png differ diff --git a/public/site_assets/global/images/flags/sb.png b/public/site_assets/global/images/flags/sb.png new file mode 100755 index 00000000..a9937ccf Binary files /dev/null and b/public/site_assets/global/images/flags/sb.png differ diff --git a/public/site_assets/global/images/flags/sc.png b/public/site_assets/global/images/flags/sc.png new file mode 100755 index 00000000..39ee3718 Binary files /dev/null and b/public/site_assets/global/images/flags/sc.png differ diff --git a/public/site_assets/global/images/flags/scotland.png b/public/site_assets/global/images/flags/scotland.png new file mode 100755 index 00000000..a0e57b41 Binary files /dev/null and b/public/site_assets/global/images/flags/scotland.png differ diff --git a/public/site_assets/global/images/flags/sd.png b/public/site_assets/global/images/flags/sd.png new file mode 100755 index 00000000..eaab69eb Binary files /dev/null and b/public/site_assets/global/images/flags/sd.png differ diff --git a/public/site_assets/global/images/flags/se.png b/public/site_assets/global/images/flags/se.png new file mode 100755 index 00000000..1994653d Binary files /dev/null and b/public/site_assets/global/images/flags/se.png differ diff --git a/public/site_assets/global/images/flags/sg.png b/public/site_assets/global/images/flags/sg.png new file mode 100755 index 00000000..dd34d612 Binary files /dev/null and b/public/site_assets/global/images/flags/sg.png differ diff --git a/public/site_assets/global/images/flags/sh.png b/public/site_assets/global/images/flags/sh.png new file mode 100755 index 00000000..4b1d2a29 Binary files /dev/null and b/public/site_assets/global/images/flags/sh.png differ diff --git a/public/site_assets/global/images/flags/si.png b/public/site_assets/global/images/flags/si.png new file mode 100755 index 00000000..bb1476ff Binary files /dev/null and b/public/site_assets/global/images/flags/si.png differ diff --git a/public/site_assets/global/images/flags/sj.png b/public/site_assets/global/images/flags/sj.png new file mode 100755 index 00000000..160b6b5b Binary files /dev/null and b/public/site_assets/global/images/flags/sj.png differ diff --git a/public/site_assets/global/images/flags/sk.png b/public/site_assets/global/images/flags/sk.png new file mode 100755 index 00000000..7ccbc827 Binary files /dev/null and b/public/site_assets/global/images/flags/sk.png differ diff --git a/public/site_assets/global/images/flags/sl.png b/public/site_assets/global/images/flags/sl.png new file mode 100755 index 00000000..12d812d2 Binary files /dev/null and b/public/site_assets/global/images/flags/sl.png differ diff --git a/public/site_assets/global/images/flags/sm.png b/public/site_assets/global/images/flags/sm.png new file mode 100755 index 00000000..3df2fdcf Binary files /dev/null and b/public/site_assets/global/images/flags/sm.png differ diff --git a/public/site_assets/global/images/flags/sn.png b/public/site_assets/global/images/flags/sn.png new file mode 100755 index 00000000..eabb71db Binary files /dev/null and b/public/site_assets/global/images/flags/sn.png differ diff --git a/public/site_assets/global/images/flags/so.png b/public/site_assets/global/images/flags/so.png new file mode 100755 index 00000000..4a1ea4b2 Binary files /dev/null and b/public/site_assets/global/images/flags/so.png differ diff --git a/public/site_assets/global/images/flags/sr.png b/public/site_assets/global/images/flags/sr.png new file mode 100755 index 00000000..5eff9271 Binary files /dev/null and b/public/site_assets/global/images/flags/sr.png differ diff --git a/public/site_assets/global/images/flags/st.png b/public/site_assets/global/images/flags/st.png new file mode 100755 index 00000000..2978557b Binary files /dev/null and b/public/site_assets/global/images/flags/st.png differ diff --git a/public/site_assets/global/images/flags/sv.png b/public/site_assets/global/images/flags/sv.png new file mode 100755 index 00000000..24987990 Binary files /dev/null and b/public/site_assets/global/images/flags/sv.png differ diff --git a/public/site_assets/global/images/flags/sy.png b/public/site_assets/global/images/flags/sy.png new file mode 100755 index 00000000..f5ce30dc Binary files /dev/null and b/public/site_assets/global/images/flags/sy.png differ diff --git a/public/site_assets/global/images/flags/sz.png b/public/site_assets/global/images/flags/sz.png new file mode 100755 index 00000000..914ee861 Binary files /dev/null and b/public/site_assets/global/images/flags/sz.png differ diff --git a/public/site_assets/global/images/flags/tc.png b/public/site_assets/global/images/flags/tc.png new file mode 100755 index 00000000..8fc1156b Binary files /dev/null and b/public/site_assets/global/images/flags/tc.png differ diff --git a/public/site_assets/global/images/flags/td.png b/public/site_assets/global/images/flags/td.png new file mode 100755 index 00000000..667f21fd Binary files /dev/null and b/public/site_assets/global/images/flags/td.png differ diff --git a/public/site_assets/global/images/flags/tf.png b/public/site_assets/global/images/flags/tf.png new file mode 100755 index 00000000..80529a43 Binary files /dev/null and b/public/site_assets/global/images/flags/tf.png differ diff --git a/public/site_assets/global/images/flags/tg.png b/public/site_assets/global/images/flags/tg.png new file mode 100755 index 00000000..3aa00ad4 Binary files /dev/null and b/public/site_assets/global/images/flags/tg.png differ diff --git a/public/site_assets/global/images/flags/th.png b/public/site_assets/global/images/flags/th.png new file mode 100755 index 00000000..dd8ba917 Binary files /dev/null and b/public/site_assets/global/images/flags/th.png differ diff --git a/public/site_assets/global/images/flags/tj.png b/public/site_assets/global/images/flags/tj.png new file mode 100755 index 00000000..617bf645 Binary files /dev/null and b/public/site_assets/global/images/flags/tj.png differ diff --git a/public/site_assets/global/images/flags/tk.png b/public/site_assets/global/images/flags/tk.png new file mode 100755 index 00000000..67b8c8cb Binary files /dev/null and b/public/site_assets/global/images/flags/tk.png differ diff --git a/public/site_assets/global/images/flags/tl.png b/public/site_assets/global/images/flags/tl.png new file mode 100755 index 00000000..77da181e Binary files /dev/null and b/public/site_assets/global/images/flags/tl.png differ diff --git a/public/site_assets/global/images/flags/tm.png b/public/site_assets/global/images/flags/tm.png new file mode 100755 index 00000000..828020ec Binary files /dev/null and b/public/site_assets/global/images/flags/tm.png differ diff --git a/public/site_assets/global/images/flags/tn.png b/public/site_assets/global/images/flags/tn.png new file mode 100755 index 00000000..183cdd3d Binary files /dev/null and b/public/site_assets/global/images/flags/tn.png differ diff --git a/public/site_assets/global/images/flags/to.png b/public/site_assets/global/images/flags/to.png new file mode 100755 index 00000000..f89b8ba7 Binary files /dev/null and b/public/site_assets/global/images/flags/to.png differ diff --git a/public/site_assets/global/images/flags/tr.png b/public/site_assets/global/images/flags/tr.png new file mode 100755 index 00000000..be32f77e Binary files /dev/null and b/public/site_assets/global/images/flags/tr.png differ diff --git a/public/site_assets/global/images/flags/tt.png b/public/site_assets/global/images/flags/tt.png new file mode 100755 index 00000000..2a11c1e2 Binary files /dev/null and b/public/site_assets/global/images/flags/tt.png differ diff --git a/public/site_assets/global/images/flags/tv.png b/public/site_assets/global/images/flags/tv.png new file mode 100755 index 00000000..28274c5f Binary files /dev/null and b/public/site_assets/global/images/flags/tv.png differ diff --git a/public/site_assets/global/images/flags/tw.png b/public/site_assets/global/images/flags/tw.png new file mode 100755 index 00000000..f31c654c Binary files /dev/null and b/public/site_assets/global/images/flags/tw.png differ diff --git a/public/site_assets/global/images/flags/tz.png b/public/site_assets/global/images/flags/tz.png new file mode 100755 index 00000000..c00ff796 Binary files /dev/null and b/public/site_assets/global/images/flags/tz.png differ diff --git a/public/site_assets/global/images/flags/ua.png b/public/site_assets/global/images/flags/ua.png new file mode 100755 index 00000000..09563a21 Binary files /dev/null and b/public/site_assets/global/images/flags/ua.png differ diff --git a/public/site_assets/global/images/flags/ug.png b/public/site_assets/global/images/flags/ug.png new file mode 100755 index 00000000..33f4affa Binary files /dev/null and b/public/site_assets/global/images/flags/ug.png differ diff --git a/public/site_assets/global/images/flags/um.png b/public/site_assets/global/images/flags/um.png new file mode 100755 index 00000000..c1dd9654 Binary files /dev/null and b/public/site_assets/global/images/flags/um.png differ diff --git a/public/site_assets/global/images/flags/us.png b/public/site_assets/global/images/flags/us.png new file mode 100755 index 00000000..10f451fe Binary files /dev/null and b/public/site_assets/global/images/flags/us.png differ diff --git a/public/site_assets/global/images/flags/uy.png b/public/site_assets/global/images/flags/uy.png new file mode 100755 index 00000000..31d948a0 Binary files /dev/null and b/public/site_assets/global/images/flags/uy.png differ diff --git a/public/site_assets/global/images/flags/uz.png b/public/site_assets/global/images/flags/uz.png new file mode 100755 index 00000000..fef5dc17 Binary files /dev/null and b/public/site_assets/global/images/flags/uz.png differ diff --git a/public/site_assets/global/images/flags/va.png b/public/site_assets/global/images/flags/va.png new file mode 100755 index 00000000..b31eaf22 Binary files /dev/null and b/public/site_assets/global/images/flags/va.png differ diff --git a/public/site_assets/global/images/flags/vc.png b/public/site_assets/global/images/flags/vc.png new file mode 100755 index 00000000..8fa17b06 Binary files /dev/null and b/public/site_assets/global/images/flags/vc.png differ diff --git a/public/site_assets/global/images/flags/ve.png b/public/site_assets/global/images/flags/ve.png new file mode 100755 index 00000000..00c90f9a Binary files /dev/null and b/public/site_assets/global/images/flags/ve.png differ diff --git a/public/site_assets/global/images/flags/vg.png b/public/site_assets/global/images/flags/vg.png new file mode 100755 index 00000000..41569079 Binary files /dev/null and b/public/site_assets/global/images/flags/vg.png differ diff --git a/public/site_assets/global/images/flags/vi.png b/public/site_assets/global/images/flags/vi.png new file mode 100755 index 00000000..ed26915a Binary files /dev/null and b/public/site_assets/global/images/flags/vi.png differ diff --git a/public/site_assets/global/images/flags/vn.png b/public/site_assets/global/images/flags/vn.png new file mode 100755 index 00000000..ec7cd48a Binary files /dev/null and b/public/site_assets/global/images/flags/vn.png differ diff --git a/public/site_assets/global/images/flags/vu.png b/public/site_assets/global/images/flags/vu.png new file mode 100755 index 00000000..b3397bc6 Binary files /dev/null and b/public/site_assets/global/images/flags/vu.png differ diff --git a/public/site_assets/global/images/flags/wales.png b/public/site_assets/global/images/flags/wales.png new file mode 100755 index 00000000..e0d7cee1 Binary files /dev/null and b/public/site_assets/global/images/flags/wales.png differ diff --git a/public/site_assets/global/images/flags/wf.png b/public/site_assets/global/images/flags/wf.png new file mode 100755 index 00000000..9f955873 Binary files /dev/null and b/public/site_assets/global/images/flags/wf.png differ diff --git a/public/site_assets/global/images/flags/ws.png b/public/site_assets/global/images/flags/ws.png new file mode 100755 index 00000000..c1695080 Binary files /dev/null and b/public/site_assets/global/images/flags/ws.png differ diff --git a/public/site_assets/global/images/flags/ye.png b/public/site_assets/global/images/flags/ye.png new file mode 100755 index 00000000..468dfad0 Binary files /dev/null and b/public/site_assets/global/images/flags/ye.png differ diff --git a/public/site_assets/global/images/flags/yt.png b/public/site_assets/global/images/flags/yt.png new file mode 100755 index 00000000..c298f378 Binary files /dev/null and b/public/site_assets/global/images/flags/yt.png differ diff --git a/public/site_assets/global/images/flags/za.png b/public/site_assets/global/images/flags/za.png new file mode 100755 index 00000000..57c58e21 Binary files /dev/null and b/public/site_assets/global/images/flags/za.png differ diff --git a/public/site_assets/global/images/flags/zm.png b/public/site_assets/global/images/flags/zm.png new file mode 100755 index 00000000..c25b07be Binary files /dev/null and b/public/site_assets/global/images/flags/zm.png differ diff --git a/public/site_assets/global/images/flags/zw.png b/public/site_assets/global/images/flags/zw.png new file mode 100755 index 00000000..53c97259 Binary files /dev/null and b/public/site_assets/global/images/flags/zw.png differ diff --git a/public/site_assets/global/js/jquery.easypiechart.min.js b/public/site_assets/global/js/jquery.easypiechart.min.js new file mode 100644 index 00000000..7c75a07c --- /dev/null +++ b/public/site_assets/global/js/jquery.easypiechart.min.js @@ -0,0 +1,9 @@ +/**! + * easyPieChart + * Lightweight plugin to render simple, animated and retina optimized pie charts + * + * @license Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + * @author Robert Fleischmann (http://robert-fleischmann.de) + * @version 2.1.0 + **/ +!function(a,b){"object"==typeof exports?module.exports=b(require("jQuery")):"function"==typeof define&&define.amd?define("EasyPieChart",["jQuery"],b):b(a.jQuery)}(this,function(a){var b=function(a,b){var c,d=document.createElement("canvas");"undefined"!=typeof G_vmlCanvasManager&&G_vmlCanvasManager.initElement(d);var e=d.getContext("2d");d.width=d.height=b.size,a.appendChild(d);var f=1;window.devicePixelRatio>1&&(f=window.devicePixelRatio,d.style.width=d.style.height=[b.size,"px"].join(""),d.width=d.height=b.size*f,e.scale(f,f)),e.translate(b.size/2,b.size/2),e.rotate((-0.5+b.rotate/180)*Math.PI);var g=(b.size-b.lineWidth)/2;b.scaleColor&&b.scaleLength&&(g-=b.scaleLength+2),Date.now=Date.now||function(){return+new Date};var h=function(a,b,c){c=Math.min(Math.max(0,c||1),1),e.beginPath(),e.arc(0,0,g,0,2*Math.PI*c,!1),e.strokeStyle=a,e.lineWidth=b,e.stroke()},i=function(){var a,c,d=24;e.lineWidth=1,e.fillStyle=b.scaleColor,e.save();for(var d=24;d>0;--d)0===d%6?(c=b.scaleLength,a=0):(c=.6*b.scaleLength,a=b.scaleLength-c),e.fillRect(-b.size/2+a,0,c,1),e.rotate(Math.PI/12);e.restore()},j=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(a){window.setTimeout(a,1e3/60)}}(),k=function(){b.scaleColor&&i(),b.trackColor&&h(b.trackColor,b.lineWidth)};this.clear=function(){e.clearRect(b.size/-2,b.size/-2,b.size,b.size)},this.draw=function(a){b.scaleColor||b.trackColor?e.getImageData&&e.putImageData?c?e.putImageData(c,0,0):(k(),c=e.getImageData(0,0,b.size*f,b.size*f)):(this.clear(),k()):this.clear(),e.lineCap=b.lineCap;var d;d="function"==typeof b.barColor?b.barColor(a):b.barColor,a>0&&h(d,b.lineWidth,a/100)}.bind(this),this.animate=function(a,c){var d=Date.now();b.onStart(a,c);var e=function(){var f=Math.min(Date.now()-d,b.animate),g=b.easing(this,f,a,c-a,b.animate);this.draw(g),b.onStep(a,c,g),f>=b.animate?b.onStop(a,c):j(e)}.bind(this);j(e)}.bind(this)},c=function(a,c){var d={barColor:"#ef1e25",trackColor:"#f9f9f9",scaleColor:"#dfe0e0",scaleLength:5,lineCap:"round",lineWidth:3,size:110,rotate:0,animate:1e3,easing:function(a,b,c,d,e){return b/=e/2,1>b?d/2*b*b+c:-d/2*(--b*(b-2)-1)+c},onStart:function(){},onStep:function(){},onStop:function(){}};if("undefined"!=typeof b)d.renderer=b;else{if("undefined"==typeof SVGRenderer)throw new Error("Please load either the SVG- or the CanvasRenderer");d.renderer=SVGRenderer}var e={},f=0,g=function(){this.el=a,this.options=e;for(var b in d)d.hasOwnProperty(b)&&(e[b]=c&&"undefined"!=typeof c[b]?c[b]:d[b],"function"==typeof e[b]&&(e[b]=e[b].bind(this)));e.easing="string"==typeof e.easing&&"undefined"!=typeof jQuery&&jQuery.isFunction(jQuery.easing[e.easing])?jQuery.easing[e.easing]:d.easing,this.renderer=new e.renderer(a,e),this.renderer.draw(f),a.dataset&&a.dataset.percent?this.update(parseFloat(a.dataset.percent)):a.getAttribute&&a.getAttribute("data-percent")&&this.update(parseFloat(a.getAttribute("data-percent")))}.bind(this);this.update=function(a){return a=parseFloat(a),e.animate?this.renderer.animate(f,a):this.renderer.draw(a),f=a,this}.bind(this),g()};a.fn.easyPieChart=function(b){return this.each(function(){a.data(this,"easyPieChart")||a.data(this,"easyPieChart",new c(this,b))})}}); \ No newline at end of file diff --git a/public/site_assets/mmcFE/css/date_input.css b/public/site_assets/mmcFE/css/date_input.css deleted file mode 100644 index 6f413d7c..00000000 --- a/public/site_assets/mmcFE/css/date_input.css +++ /dev/null @@ -1,149 +0,0 @@ -/* Some resets for compatibility with existing CSS */ -.date_selector, .date_selector * { - width: auto; - height: auto; - border: none; - background: none; - margin: 0; - padding: 0; - text-align: left; - text-decoration: none; - } - -.date_selector { - background: #fbfbfb; - border: 1px solid #ccc; - padding: 10px; - margin: 0; - margin-top: -1px; - position: absolute; - z-index: 100000; - display: none; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - box-shadow: 0 0 5px #aaa; - -moz-box-shadow: 0 0 5px #aaa; - -webkit-box-shadow: 0 0 5px #aaa; - } - -.date_selector_ieframe { - position: absolute; - z-index: 99999; - display: none; - } - -.date_selector .nav { - width: 17.5em; /* 7 * 2.5em */ - } - -.date_selector .nav p { clear: none; } - -.date_selector .month_nav, .date_selector .year_nav { - margin: 0 0 3px 0; - padding: 0; - display: block; - position: relative; - text-align: center; - } - -.date_selector .month_nav { - float: left; - width: 55%; - } - -.date_selector .year_nav { - float: right; - width: 42%; - margin-right: -8px; /* Compensates for cell borders */ - } - -.date_selector .month_name, .date_selector .year_name { - font-weight: bold; - line-height: 20px; - } - -.date_selector .button { - display: block; - position: absolute; - top: 0; - width: 18px; - height: 18px; - line-height: 16px; - font-weight: bold; - color: #008ee8; - text-align: center; - font-size: 12px; - overflow: hidden; - border: 1px solid #ccc; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - border-radius: 2px; - } - -.date_selector .button:hover, .date_selector .button.hover { - background: #ed9c35; - color: #fff; - cursor: pointer; - border-color: #d77011; - } - -.date_selector .prev { - left: 0; - } - -.date_selector .next { - right: 0; - } - -.date_selector table { - border-spacing: 0; - border-collapse: collapse; - clear: both; - margin: 0; - } - -.date_selector th, .date_selector td { - width: 2.5em; - height: 2em; - padding: 0 !important; - text-align: center !important; - color: #666; - font-weight: normal; - } - -.date_selector th { - font-size: 11px; - } - -.date_selector td { - border: 1px solid #ccc; - line-height: 2em; - text-align: center; - white-space: nowrap; - color: #008ee8; - background: #fff; - } - -.date_selector td.today { - background: #eee; - } - -.date_selector td.unselected_month { - color: #ccc; - } - -.date_selector td.selectable_day { - cursor: pointer; - } - -.date_selector td.selected { - background: #008ee8; - color: #fff; - font-weight: bold; - } - -.date_selector td.selectable_day:hover, .date_selector td.selectable_day.hover { - background: #ec8526; - color: #fff; - } \ No newline at end of file diff --git a/public/site_assets/mmcFE/css/facebox.css b/public/site_assets/mmcFE/css/facebox.css deleted file mode 100644 index 7c5c8c5d..00000000 --- a/public/site_assets/mmcFE/css/facebox.css +++ /dev/null @@ -1,83 +0,0 @@ -#facebox .b { background:url(../images/b.png); } -#facebox .tl { background:url(../images/tl.png); } -#facebox .tr { background:url(../images/tr.png); } -#facebox .bl { background:url(../images/bl.png); } -#facebox .br { background:url(../images/br.png); } - -#facebox { - position: absolute; - top: 0; - left: 0; - z-index: 100; - text-align: left; - } - -#facebox .popup { - position: relative; - } - -#facebox table { - border-collapse: collapse; - } - -#facebox td { - border-bottom: 0; - padding: 0; - } - -#facebox .body { - padding: 10px; - background: #fff; - width: auto !important; - width: 400px; - min-width: 400px; - } - -#facebox .loading { - text-align: center; - } - -#facebox .image { - text-align: center; - } - -#facebox img { - border: 0; - margin: 0; - } - -#facebox .footer { - border-top: 1px solid #DDDDDD; - padding-top: 5px; - margin-top: 10px; - text-align: right; - } - -#facebox .tl, #facebox .tr, #facebox .bl, #facebox .br { - height: 10px; - width: 10px; - overflow: hidden; - padding: 0; - } - -#facebox_overlay { - position: fixed; - top: 0px; - left: 0px; - height:100%; - width:100%; - } - -.facebox_hide { - z-index:-100; - } - -.facebox_overlayBG { - background-color: #000; - z-index: 99; - } - -* html #facebox_overlay { /* ie6 hack */ - position: absolute; - height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'); - } diff --git a/public/site_assets/mmcFE/css/ie.css b/public/site_assets/mmcFE/css/ie.css deleted file mode 100644 index eacb199c..00000000 --- a/public/site_assets/mmcFE/css/ie.css +++ /dev/null @@ -1,39 +0,0 @@ -.block { padding-bottom: 0 !important; } -.block form input.file { height: 19px; } -.block .visualize { margin-top: 30px; } -.block .visualize ul.visualize-key .visualize-key-color { margin-top: -2px; } -.block .block_content .imglist { padding-bottom: 20px; } -.block .block_content .imglist li img { top: 1px; } - -.block .paggination.right, -.block .tableactions { padding-bottom: 0; } - -.block, -*html .block .block_head, -*html .block .block_content, -*html .block .block_content form input, -*html .block .message, -*html .block .paggination, -*html .block .tableactions, -*html .block .block_content .imglist { - zoom: 1; - } - -*html .wrapper { width:expression(document.body.clientWidth < 1024 ? "960px" : "90%" ); } -*html .bendl, *html .bendr { font-size: 1px; } -*html .block .block_content .imglist li { margin: 13px 24px 13px 3px; } -*html .block form .cmf-skinned-select { background: none; border: 0; padding: 0; } -*html #header #nav li:hover li a, #header #nav li.iehover li a { background: none; } - -/* Navigation 2nd level */ -*html #header #nav ul li { background: #1c1c1c; } -*html #header #nav ul li.iehover { background: #000; } - -/* Navigation 3rd level */ -*html #header #nav li.iehover li.iehover li a { background: #1c1c1c; } -*html #header #nav li.iehover li.iehover li a:hover, -*html #header #nav li.iehover li.iehover li.iehover a { background: #000; } - -/* Navigation 4th level */ -*html #header #nav li.iehover li.iehover li.iehover li a { background: #1c1c1c; } -*html #header #nav li.iehover li.iehover li.iehover li a:hover { background: #000; } \ No newline at end of file diff --git a/public/site_assets/mmcFE/css/jquery.wysiwyg.css b/public/site_assets/mmcFE/css/jquery.wysiwyg.css deleted file mode 100644 index a41d5d4b..00000000 --- a/public/site_assets/mmcFE/css/jquery.wysiwyg.css +++ /dev/null @@ -1,56 +0,0 @@ -div.wysiwyg { border: 1px solid #bbb; padding: 0; background-color: #fefefe; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; width: 99.5% !important; } -div.wysiwyg * { margin: 0; padding: 0; outline: none; } - -div.wysiwyg ul.panel { border-bottom: 1px solid #cccccc; background: #fafafa; float: left; width: 99.1%; padding: 5px; margin: 0; -webkit-border-top-left-radius: 3px; -webkit-border-top-right-radius: 3px; -moz-border-radius-topleft: 3px; -moz-border-radius-topright: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; } -div.wysiwyg ul.panel li { list-style-type: none; float: left; padding: 0; margin: 2px 2px 2px 0; background: none; height: 19px; overflow: hidden; } -div.wysiwyg ul.panel li.separator { height: 18px; margin: 3px 4px 0; border-left: 1px solid #cccccc; } -div.wysiwyg ul.panel li a { text-indent: -5000px; opacity: 0.85; filter:alpha(opacity=85); display: block; width: 16px; height: 16px; background: url('../images/../images/jquery.wysiwyg.gif') no-repeat -64px -80px; border: 0; cursor: pointer; padding: 0; margin: 1px; border: 1px solid #fafafa; } -div.wysiwyg ul.panel li a:hover, div.wysiwyg ul.panel li a.active { opacity: 1.00; filter:alpha(opacity=100); } -div.wysiwyg ul.panel li a.active { border: 1px solid #ddd; background-color: #fff; padding: 0; } - -div.wysiwyg ul.panel li a.bold { background-position: 0 -17px; } -div.wysiwyg ul.panel li a.italic { background-position: -16px -17px; } -div.wysiwyg ul.panel li a.strikeThrough { background-position: -32px -17px; } -div.wysiwyg ul.panel li a.underline { background-position: -48px -16px; } - -div.wysiwyg ul.panel li a.justifyLeft { background-position: 0 0; } -div.wysiwyg ul.panel li a.justifyCenter { background-position: -16px 0; } -div.wysiwyg ul.panel li a.justifyRight { background-position: -32px 0; } -div.wysiwyg ul.panel li a.justifyFull { background-position: -48px 0; } - -div.wysiwyg ul.panel li a.indent { background-position: -64px 0; } -div.wysiwyg ul.panel li a.outdent { background-position: -80px 0; } - -div.wysiwyg ul.panel li a.subscript { background-position: -64px -16px; } -div.wysiwyg ul.panel li a.superscript { background-position: -80px -16px; } - -div.wysiwyg ul.panel li a.undo { background-position: 0 -63px; } -div.wysiwyg ul.panel li a.redo { background-position: -16px -65px; } - -div.wysiwyg ul.panel li a.insertOrderedList { background-position: -32px -48px; } -div.wysiwyg ul.panel li a.insertUnorderedList { background-position: -16px -48px; } -div.wysiwyg ul.panel li a.insertHorizontalRule { background-position: 0 -48px; } - -div.wysiwyg ul.panel li a.h1 { background-position: 0 -32px; } -div.wysiwyg ul.panel li a.h2 { background-position: -16px -32px; } -div.wysiwyg ul.panel li a.h3 { background-position: -32px -32px; } -div.wysiwyg ul.panel li a.h4 { background-position: -48px -32px; } -div.wysiwyg ul.panel li a.h5 { background-position: -64px -32px; } -div.wysiwyg ul.panel li a.h6 { background-position: -80px -32px; } - -div.wysiwyg ul.panel li a.cut { background-position: -32px -64px; } -div.wysiwyg ul.panel li a.copy { background-position: -48px -64px; } -div.wysiwyg ul.panel li a.paste { background-position: -64px -64px; } - -div.wysiwyg ul.panel li a.increaseFontSize { background-position: -16px -80px; } -div.wysiwyg ul.panel li a.decreaseFontSize { background-position: -32px -80px; } - -div.wysiwyg ul.panel li a.createLink { background-position: -80px -48px; } -div.wysiwyg ul.panel li a.insertImage { background-position: -80px -80px; } - -div.wysiwyg ul.panel li a.html { background-position: -47px -46px; } -div.wysiwyg ul.panel li a.removeFormat { background-position: -80px -64px; } - -div.wysiwyg ul.panel li a.empty { background-position: -64px -80px; } - -div.wysiwyg iframe { border: 0; clear: left; margin: 0; width: 100% !important; } \ No newline at end of file diff --git a/public/site_assets/mmcFE/css/mainstyle.css b/public/site_assets/mmcFE/css/mainstyle.css deleted file mode 100644 index 4e17cbf5..00000000 --- a/public/site_assets/mmcFE/css/mainstyle.css +++ /dev/null @@ -1,162 +0,0 @@ - -a:active,a:link,a:visited{ - color:#000; -} -a:hover{ - color:#000; -} - -/*-----End of Defaults------*/ - -a.menu:active,a.menu:link,a.menu:visited{ - color:#FFF; - font-weight:bold; - font-size:.975em; - text-align:center; - text-decoration: none; -} - -a.menu:hover{ - color:#aaa; - font-weight:bold; - font-size:.975em; - text-align:center; - text-decoration: none; -} - -#siteinfo { - font-family: Verdana, Arial, sans-serif; - font-size: 20px; - font-weight: bold; - color: #000; - margin:5px; - margin-top:5px; - width:auto; - float:left; -} - -.slogan { - font-family: Verdana, Arial, sans-serif; - font-size: 10px; - font-weight: bold; - color: #aA9A9A; - margin-left:12px; - margin-top:12px; -} - -#ministats { - font-family: Verdana, Arial, sans-serif; - font-size: 10px; - font-weight: bold; - color: #000; - position:absolute; - - right:5px; - top:22px; - float:right; -} - -#ministats li { - font-size: 11px; - font-weight: bold; - color: #000; -} - -#loginForm{ - position:relative; - top:.5em; - left:.5em; -} - -#lostPassForm{ - position:relative; - top:0; - left: .5em; -} - -#userInfo{ - float: left; - background-color:#DDD; - width:15em; - height: 100%; - padding:.5em; -} - -/*-----Text-----*/ -span.goodMessage{ - position:relative; - color:green; - font-weight:bold; - font-size:1em; - top:-5px; -} - -span.returnMessage{ - position:relative; - color:orange; - font-weight:bold; - font-size:1em; - top:-15px; -} - -#content { - height:auto; - border-style: solid solid solid solid; - border-width:1px 1px 1px 1px; - border-color:BBC3D3; - white-space: normal; - padding: 1em; - margin-top:5px; - margin-left: 18.2em; - background-color: #FFF; -} - -#stats_section { - border-style: solid solid solid solid; - border-width:1px 1px 1px 1px; - border-color:BBC3D3; - margin-bottom:10px; - background-color:#F8F8F9; - width: auto; - font-size:.9em; - - height:auto; - padding:.5em; - padding-bottom:1.25em; - -} - -#generic_infobox { - border-style: solid solid solid solid; - border-width:1px 1px 1px 1px; - border-color:BBC3D3; - margin-bottom:5px; - background-color:#F8F8F9; - font-size:.9em; - width:98%; - - height:auto; - padding:.5em; - padding-bottom:1.25em; -} - -#search_infobox { - background-color:#F8F8F9; - line-height:10px; - overflow:none; - display: none; -} - -.search_results { - display: inherit; -} - -.tooltip { - display:none; - background:transparent url(site_assets/mmcFE/images/black_arrow.png); - font-size:11px; - height:70px; - width:160px; - padding:25px; - color:#fff; -} diff --git a/public/site_assets/mmcFE/css/style.css b/public/site_assets/mmcFE/css/style.css deleted file mode 100644 index e9284371..00000000 --- a/public/site_assets/mmcFE/css/style.css +++ /dev/null @@ -1,1062 +0,0 @@ -* { padding:0; margin:0; } - -body { - background: #fbfbfb url(../images/bg.jpg) 0 0 repeat; - font-family: "Lucida Grande", Verdana, sans-serif; - font-size: 12px; - color: #666; - line-height: 15px; - -webkit-text-size-adjust: none; -} - -:focus { -moz-outline-style: none; } - -p { padding-bottom: 15px; } -img, a img { border: 0; } -ul li { list-style: none; } -.clear { clear: both; } - -a { - color: #008ee8; - /* text-decoration: none; */ - outline: none; -} - -a:hover { - color: #ec8526; - text-decoration: none; -} - -#hld { - /* background: url(../images/hld.jpg) 0 0 repeat-x; */ - min-height: 402px; -} - -.wrapper { - width:99%; - min-width: 960px; - margin:0 auto; - padding-top: 5px; -} -/* Site Header (top image) */ - -#siteheader{ - width:100%; - height:35; - background-color:#fff; - border-radius:5px; - -moz-border-radius:5px; - -webkit-border-radius:5px; -} - -/* Header (Menu) */ - -#header { - height: 35px; - line-height: 35px; - background: url(../images/hdr.gif) 0 0 repeat-x; - color: #ccc; - font-weight: bold; - margin-top: -15px; - margin-bottom: 5px; - border-radius:5px; - -moz-border-radius:5px; - -webkit-border-radius:5px; -} - -#header .hdrl { - width: 20px; - height: 35px; - float: left; - background: url(../images/hdrl.gif) top left no-repeat; -} - -#header .hdrr { - width: 20px; - height: 35px; - float: right; - background: url(../images/hdrr.gif) top right no-repeat; -} - -#header a { - color: #ccc; - text-decoration: none; -} - -#header a:hover { color: #fff; } - -#header h1 { - float: left; - margin-right: 80px; - font-family: "Titillium800", "Trebuchet MS", Arial, sans-serif; - font-size: 18px; - font-weight: normal; - text-transform: uppercase; - color: #fff; -} - -#header h1 a { color: #fff; } -#header h1 a:hover { color: #008ee8; } - - -/* Navigation */ -#header #nav, #header #nav * { z-index: 20; } - -#header #nav { - margin: 0; - border: 0 none; - padding: 0; - width: auto; /*For KHTML*/ - list-style: none; - height: 35px; - padding: 0; - float: left; -} - -#header #nav li { - margin: 0; - border: 0 none; - padding: 0; - float: left; /*For Gecko*/ - display: inline; - list-style: none; - position: relative; - height: 35px; - padding: 0 0px; - background: url(../images/nsp.gif) center right no-repeat; - line-height: 35px; -} - -#header #nav li.nobg { background: none; } - -#header #nav ul { - margin: 0; - border: 0 none; - padding: 0; - width: 190px; - list-style: none; - display: none; - position: absolute; - top: 35px; - left: 0; -} - -#header #nav ul:after /*From IE 7 lack of compliance*/{ - clear: both; - display: block; - font: 1px/0px serif; - content: "."; - height: 0; - visibility: hidden; -} - -#header #nav ul li { - width: 190px; - float: left; /*For IE 7 lack of compliance*/ - display: block !important; - display: inline; /*For IE*/ - position: relative; - top: 0; - height: 30px; - line-height: 30px; - padding: 0; - background: none; -} - -/* Root Menu */ -#header #nav a { - float: none !important; /*For Opera*/ - float: left; /*For IE*/ - display: block; - height: auto !important; - height: 1%; /*For IE*/ - height: 30px; - padding: 0 10px; -} - -#header #nav li.active a { color: #ddd; } -#header #nav li.active a:hover { color: #fff; } - -#header #nav a.hasmore { - border: 1px solid red; -} - -/* Root Menu Hover Persistence */ -#header #nav a:hover, -#header #nav li:hover a, -#header #nav li.iehover a { - color: #0096db; -} - -/* 2nd Menu */ -#header #nav li:hover li a, -#header #nav li.iehover li a { - text-transform: none; - padding: 0 15px; - font-size: 11px; - font-weight: bold; - line-height: 30px; - color: #fff; - background: url(../images/mbg.png) 0 0 repeat; -} - -/* 2nd Menu Hover Persistence */ -#header #nav li:hover li a:hover, -#header #nav li:hover li:hover a, -#header #nav li.iehover li a:hover, -#header #nav li.iehover li.iehover a { - color: #0096db; - background: #000; -} - -/* 3rd Menu */ -#header #nav li:hover li:hover li a, -#header #nav li.iehover li.iehover li a { - float: none; - color: #ccc; - background: url(../images/mbg.png) 0 0 repeat; -} - -/* 3rd Menu Hover Persistence */ -#header #nav li:hover li:hover li a:hover, -#header #nav li:hover li:hover li:hover a, -#header #nav li.iehover li.iehover li a:hover, -#header #nav li.iehover li.iehover li.iehover a { - color: #fff; - background: #000; -} - -/* 4th Menu */ -#header #nav li:hover li:hover li:hover li a, -#header #nav li.iehover li.iehover li.iehover li a { - color: #ccc; - background: url(../images/mbg.png) 0 0 repeat; -} - -/* 4th Menu Hover */ -#header #nav li:hover li:hover li:hover li a:hover, -#header #nav li.iehover li.iehover li.iehover li a:hover { - color: #fff; - background: #000; -} - -#header #nav ul ul, -#header #nav ul ul ul { - display: none; - position: absolute; - top: 0; - left: 190px; -} - -/* Do Not Move - Must Come Before display:block for Gecko */ -#header #nav li:hover ul ul, -#header #nav li:hover ul ul ul, -#header #nav li.iehover ul ul, -#header #nav li.iehover ul ul ul { - display: none; -} - -#header #nav li:hover ul, -#header #nav ul li:hover ul, -#header #nav ul ul li:hover ul, -#header #nav li.iehover ul, -#header #nav ul li.iehover ul, -#header #nav ul ul li.iehover ul { - display: block; -} - -#header .user { - float: right; - font-size: 11px; -} - -#header .user a { text-decoration: underline; } -#header .user a:hover { text-decoration: none; } - - -/* Blocks */ - -.block { - padding-bottom: 5px; - margin-bottom: 25px; - clear: both; - background: #fff url(../images/bnd.gif) bottom center repeat-x; -} - -.block .bendl { - width: 5px; - height: 5px; - background: url(../images/bendl.gif) bottom left no-repeat; - float: left; -} - -.block .bendr { - width: 5px; - height: 5px; - background: url(../images/bendr.gif) bottom left no-repeat; - float: right; -} - - -/* Block head */ - -.block .block_head { - /* height: 54px; */ - height: 35px; - /* line-height: 54px; */ - line-height: 35px; - background: url(../images/bhead.gif) 0 0 repeat-x; - overflow: hidden; - - border-style: solid solid solid solid; - border-width:0px 0px 1px 0px; - border-color:BBC3D3; - -} - -.block .block_head .bheadl { - width: 20px; - height: 35px; - float: left; - background: url(../images/bheadl.gif) top left no-repeat; -} - -.block .block_head .bheadr { - width: 20px; - height: 35px; - float: right; - background: url(../images/bheadr.gif) top right no-repeat; -} - -.block .block_head h2 { - font-family: "Titillium999", "Trebuchet MS", Arial, sans-serif; - /* font-size: 18px; */ - font-size: 15px; - font-weight: normal; - text-transform: uppercase; - color: #555; - text-shadow: 1px 1px 0 #fff; - float: left; -} - -.block .block_head ul { - float: right; - text-transform: uppercase; - font-size: 11px; - font-weight: bold; - text-shadow: 1px 1px 0 #fff; -} - -.block .block_head ul li { - display: inline; - padding: 3px 0; - padding-left: 20px; - background: url(../images/phs.gif) 7px center no-repeat; -} - -.block .block_head ul li.nobg { background: none; } - -.block .block_head ul li a { - text-decoration: none; - color: #666; - outline: none; -} - -.block .block_head ul li.active a { color: #888; } -.block .block_head ul li a:hover { color: #008ee8; } - -.block .block_head form { - float: right; - padding: 15px 0; - height: 34px; - line-height: 24px; -} - -.block .block_head form .text { - width: 129px; - height: 15px; - padding: 5px 10px 5px 25px; - border: 0; - font-size: 11px; - color: #999; - margin: 0; - background: url(../images/srch.gif) left center no-repeat; -} - -.block .block_head form .text:focus { - color: #666; - background: url(../images/srch_.gif) left center no-repeat; -} - -.block .block_head select { - text-transform: none; -} - -/* Block content */ - -.block .block_content { - overflow: hidden; - background: #fff; - border-left: 1px solid #ccc; - border-right: 1px solid #ccc; - padding: 10px 20px 0; -} - -.block .block_content h1, -.block .block_content h2 { - font-family: "Titillium999", "Trebuchet MS", Arial, sans-serif; - font-size: 18px; - font-weight: normal; - color: #454545; - margin-bottom: 10px; -} - -.block .block_content h3 { - font-family: "Lucida Grande", Helvetica, Arial, sans-serif; - font-size: 14px; - font-weight: bold; - color: #666; - margin-bottom: 5px; -} - -.block .block_content h4 { - font-family: Arial, Helvetica, sans-serif; - font-size: 13px; - font-weight: bold; - color: #666; - margin-bottom: 5px; -} - -.block table { - text-align: left; - margin-bottom: 15px; -} - -.block table tr.even td { - background: #fbfbfb; -} - -.block table tr td, -.block table tr th { - border-bottom: 1px solid #ddd; - padding: 10px; - line-height: 3px; - text-align: left; -} - -.block table tr th.headerSortUp { - color: #333; - background: url(../images/sortd.gif) 95% center no-repeat; -} - -.block table tr th.headerSortDown { - color: #333; - background: url(../images/sorta.gif) 95% center no-repeat; -} - -.block table tr td.delete { - text-align: right; - font-size: 11px; -} - -.block table tr td.right{ - text-align: right; -} -.block table tr th.right{ - text-align: right; -} -.block table tr td.center{ - text-align: center; -} -.block table tr th.center{ - text-align: center; -} - - -.block table tr td.delete a { color: #666; } -.block table tr td.delete a:hover { color: #dd0000; } - -.block .tableactions { - overflow: hidden; - padding-bottom: 20px; - float: left; -} - -.block .tableactions select { - width: 100px; - margin-right: 5px; - vertical-align: middle; - outline: none; -} - -.block .pagination { - font-size: 10px; - font-weight: bold; - padding-bottom: 15px; - padding-top: 15px; - border-top: 0px solid #eee; -} - -.block .pagination.right { - float: right; - text-align: right; - padding-top: 1px; - padding-right: 8px; - border: 0; - - overflow: hidden; -} - -.block .pagination a { - border: 1px solid #ccc; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - padding: 3px 6px; - margin: 0 1px; -} - -.block .pagination a.active { - background: #39a3e6; - border: 1px solid #0082d5; - color: #fff; -} - -.block .pagination a:hover { - color: #fff; - background: #ec8526; - border: 1px solid #d77011; -} - -.block .block_content ul { - padding-left: 15px; - padding-bottom: 15px; -} - -.block .block_content ol { - padding-left: 15px; - padding-bottom: 15px; - list-style-position: inside; -} - -.block .block_content ul li { - margin-bottom: 5px; - padding-left: 15px; - background: url(../images/li.gif) 0 7px no-repeat; -} - -.block .block_content ol li { - margin-bottom: 5px; -} - -.block hr { - height: 1px; - border: 0; - background: no-repeat; - border-bottom: 1px solid #ddd; - margin-bottom: 15px; -} - -.block .block_content .breadcrumb { - color: #666; - background: #f9f9f9; - border-bottom: 1px solid #e6e6e6; - padding: 9px 12px; - line-height: normal; - margin-bottom: 10px; - font-size: 11px; - -webkit-border-top-left-radius: 5px; - -webkit-border-top-right-radius: 5px; - -moz-border-radius-topleft: 5px; - -moz-border-radius-topright: 5px; - border-top-left-radius: 5px; - border-top-right-radius: 5px; -} - - -/* Messages */ - -.block .message { - padding: 10px 15px 10px 40px; - margin: 0px 0; - margin-bottom:10px; - font-weight: bold; - overflow: hidden; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.block .message p { - padding: 0; - width: 93%; - float: left; -} - -.block .message.errormsg { - border: 1px solid #e9c59b; - background: #ffecce url(../images/error.gif) 12px 12px no-repeat; - color: #e3302c; -} - -.block .message.success { - border: 1px solid #bfde84; - background: #edfbd8 url(../images/success.gif) 12px 12px no-repeat; - color: #508600; -} - -.block .message.info { - border: 1px solid #bbdbe0; - background: #ecf9ff url(../images/info.gif) 12px 12px no-repeat; - color: #0888c3; -} - -.block .message.warning { - border: 1px solid #e5e181; - background: #fefde2 url(../images/warning.gif) 12px 12px no-repeat; - color: #666; -} - -.block .message .close { - display: block; - float: right; - width: 16px; - height: 16px; - background: url(../images/close.png) 0 0 no-repeat; - margin-top: 2px; - cursor: pointer; - -moz-opacity: 0.7; - opacity: 0.7; -} - -.block .message .close.hover { - -moz-opacity: 1; - opacity: 1; -} - - -/* Forms */ - -.block form p { - overflow: hidden; - clear: both; -} - -.block form label { - font-family: "Lucida Grande", Verdana, sans-serif; - font-size: 12px; - font-weight: bold; - color: #555; - margin-right: 10px; - vertical-align: middle; -} - -.block form input.text { - width: 410px; - background: #fefefe; - border: 1px solid #bbb; - font-family: "Lucida Grande", Verdana, sans-serif; - font-size: 14px; - color: #333; - padding: 7px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - outline: none; - vertical-align: middle; -} - -.block form input.text.medium { width: 600px; } -.block form input.text.big { width: 98%; } -.block form input.text.tiny { width: 210px; } -.block form input.text.pin { width: 55px; } - -.block form input.text.date_picker { - width: 100px; - padding-right: 36px; - cursor: pointer; - background: url(../images/cal.jpg) center right no-repeat; -} - -.block form textarea { - width: 98%; - height: 140px; - padding: 5px; - background: #fefefe; - border: 1px solid #bbb; - font-family: "Lucida Grande", Verdana, sans-serif; - font-size: 14px; - color: #333; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - outline: none; -} - -.block form textarea.wysiwyg { width: 98%; padding: 0; } - -.block form input.radio, -.block form input.checkbox { - vertical-align: middle; -} - - -/* Custom form select element */ - -.block form select.styled { - width: 245px; - height: 18px; - margin-right: 20px; - cursor: pointer; -} - -.block form .cmf-skinned-select { - padding: 7px; - display: block; - margin-right: 20px; - border: 1px solid #bbb; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - background: url('../images/sdd.jpg') center right no-repeat; -} - -.block form .cmf-skinned-select:hover { - background: url('../images/sdd_.jpg') center right no-repeat; -} - -.block form div.cmf-skinned-text { - padding: 5px 7px; - font-family: "Lucida Grande", Verdana, sans-serif; - font-size: 14px; - color: #333; -} - - -/* Custom file input */ - -.block form .fileupload { - position: relative; -} - -.block form .fileupload #uploadmsg { - display: block; - position: absolute; - top: 20px; - left: 350px; - height: 30px; - line-height: 30px; - margin-left: 5px; - font-size: 11px; -} - -.block form .fileupload #uploadmsg.loading { - background: url(../images/ajax-loader.gif) center left no-repeat; - width: 30px; - padding-left: 20px; -} - -.block form input.file { - width: 250px; - height: 20px; - background: #fefefe; - border: 1px solid #bbb; - border-right: 0; - font-family: "Lucida Grande", Verdana, sans-serif; - font-size: 14px; - color: #333; - padding: 4px; - -webkit-border-top-left-radius: 3px; - -webkit-border-bottom-left-radius: 3px; - -moz-border-radius-topleft: 3px; - -moz-border-radius-bottomleft: 3px; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - outline: none; -} - -.block form .note { - padding-left: 10px; - font-size: 11px; - font-weight: bold; - color: #757575; - vertical-align: middle; -} - -.block form .note.error { color: #e3302c; } - -.block form input.submit { - width: 85px; - height: 30px; - line-height: 30px; - background: url(../images/btns.gif) top center no-repeat; - border: 0; - font-family: "Titillium800", "Trebuchet MS", Arial, sans-serif; - font-size: 14px; - font-weight: normal; - text-transform: uppercase; - color: #fff; - text-shadow: 1px 1px 0 #0a5482; - cursor: pointer; - margin-right: 10px; - vertical-align: middle; -} - -.block form input.submit:hover { - background: url(../images/btns_.gif) top center no-repeat; - text-shadow: 1px 1px 0 #b55f10; -} - -.block form input.submit.tiny { - width: 105px; - height: 20px; - line-height: 20px; - padding-bottom: 2px; - vertical-align: middle; - font-family: "Lucida Grande", Arial, Helvetica, sans-serif; - font-size: 10px; - font-weight: bold; - text-transform: none; - background: url(../images/tiny.gif) top center no-repeat; - opacity:0.75; -} - -.block form input.submit.tiny:hover { background: url(../images/tiny_.gif) top center no-repeat; } - -.block form input.submit.mid { opacity:0.75; width: 115px; background: url(../images/btnm.gif) top center no-repeat; } -.block form input.submit.mid:hover { background: url(../images/btnm_.gif) top center no-repeat; } - -.block form input.submit.long { opacity:0.75; width: 165px; background: url(../images/btnb.gif) top center no-repeat; } -.block form input.submit.long:hover { background: url(../images/btnb_.gif) top center no-repeat; } - - -/* Small blocks */ - -.block.small { - width: 49%; - background: #fff url(../images/bnd.gif) bottom center repeat-x; - padding-bottom: 0; -} - -.block.small.left { float: left; clear: none; } -.block.small.right { float: right; clear: none; } - -.block.small .block_head { background: url(../images/bhead.gif) 0 0 repeat-x; } -.block.small .block_content { background: #fff; border-left: 1px solid #ccc; border-right: 1px solid #ccc; } - -.block.small .block_content input.text, -.block.small .block_content textarea { width: 96%; padding: 7px; } - -.block.small.center { - float: none; - margin: 0 auto; -} - - -/* Login block */ - -.block.small.center.login { - width: 470px; - background: #fff url(../images/bnd.gif) bottom center repeat-x; - margin-top: 100px; - margin-bottom: 100px; - padding-bottom: 5px; -} - - -/* Block sidebar */ - -.block.withsidebar .bendl { - width: 191px; - background: url(../images/bendsb.gif) bottom left no-repeat; -} - -.block.withsidebar .block_content { - overflow: hidden; - padding: 0; - /* background: url(../images/sidebar.gif) 0 0 repeat-y; */ -} - -.block.withsidebar .block_content .sidebar { - width: 230px; - float: left; - font-size: 10px; -} - -.block.withsidebar .block_content .sidebar p { - padding: 0 13px 15px; - line-height: 18px; -} - -.block.withsidebar .block_content .sidebar ul.sidemenu { - padding: 0; - margin-bottom: 10px; -} - -.block.withsidebar .block_content .sidebar ul.sidemenu li { - padding: 0; - margin: 0; - background: 0; - border-bottom: 1px solid #ccc; -} - -.block.withsidebar .block_content .sidebar ul.sidemenu li a { - display: block; - padding: 10px 13px; - font-weight: bold; -} - -.block.withsidebar .block_content .sidebar ul.sidemenu li a:hover { - background: #fdfdfd; - border-right: 1px solid #ccc; -} - -.block.withsidebar .block_content .sidebar ul.sidemenu li.active a, -.block.withsidebar .block_content .sidebar ul.sidemenu li.active a:hover { - background: #fff; - border: 0; -} - -.block.withsidebar .block_content .sidebar_content { - padding: 15px 20px 15px 230px; -} - -/* Image list */ - -.block .block_content .imglist { - overflow: hidden; - margin: 0; - padding: 0; - padding-bottom: 10px; -} - -.block .block_content .imglist ul { - padding: 0; -} - -.block .block_content .imglist li { - padding: 0; - margin: 0; - background: 0; - width: 100px; - height: 100px; - display: block; - float: left; - position: relative; - margin: 13px 17px 10px 10px; - border: 1px solid #ccc; - padding: 1px; - box-shadow: 0 0 3px #ccc; - -moz-box-shadow: 0 0 3px #ccc; - -webkit-box-shadow: 0 0 3px #ccc; -} - -.block .block_content .imglist li img { - position: absolute; - z-index: 1; - width: 100px; - height: 100px; -} - -.block .block_content .imglist li ul { - position: absolute; - z-index: 10; - background: url(../images/imgo.gif) center no-repeat; - width: 62px; - height: 58px; - left: 20px; - top: 22px; - display: none; -} - -.block .block_content .imglist li:hover ul { display: block; } - -.block .block_content .imglist li ul li { - display: block; - width: 62px; - height: 29px; - text-align: center; - margin: 0; - padding: 0; - border: 0; - box-shadow: none; - -moz-box-shadow: none; - -webkit-box-shadow: none; -} - -.block .block_content .imglist li ul li a { - display: block; - color: #fff; - text-transform: uppercase; - font-size: 10px; - font-weight: bold; - height: 29px; - line-height: 29px; - text-shadow: 0 0 2px #000; - outline: none; -} - -.block .block_content .imglist li ul li.view a:hover { background: url(../images/imgt.gif) bottom center no-repeat; } -.block .block_content .imglist li ul li.delete a:hover { background: url(../images/imgb.gif) top center no-repeat; } - - -/* Footer */ - -#footer { - /* background: url(../images/ft.gif) 0 0 repeat-x; */ - padding: 0px 0 0px; - font-size: 10px; - color: #757575; - clear: both; - overflow: hidden; - text-align:center; -} - -#footer a { - color: #757575; - text-decoration: underline; -} - -#footer a:hover { - color: #666; - text-decoration: none; -} - -#footer .left { - float: left; -} - -#footer .right { - float: right; - text-align: right; -} - - -/* random customizations */ -#leftsidebar_stats { - padding-top:5px; - padding-left:5px; -} - -/* Custom checkboxes */ -input[type=checkbox] { - display:none; -} - -input[type=checkbox] + label -{ - background: url('../images/error.gif'); - height: 16px; - width: 16px; - display:inline-block; - padding: 0 0 0 0px; -} - -input[type=checkbox]:checked + label -{ - background: url('../images/success.gif'); - height: 16px; - width: 16px; - display:inline-block; - padding: 0 0 0 0px; -} diff --git a/public/site_assets/mmcFE/css/visualize.css b/public/site_assets/mmcFE/css/visualize.css deleted file mode 100644 index ab2168aa..00000000 --- a/public/site_assets/mmcFE/css/visualize.css +++ /dev/null @@ -1,47 +0,0 @@ -/*plugin styles*/ -.block .visualize { border: 1px solid #bbb; position: relative; background: #fbfbfb; margin: 20px auto 40px auto; z-index: 1; } -.block .visualize canvas { position: absolute; } -.block .visualize ul, .block .visualize ul li { margin: 0; padding: 0; background: none; } -.block .visualize-bar { border-top: 0; } - -/*table title, key elements*/ -.block .visualize .visualize-info { padding: 0 0 2px 8px; background: #fafafa; border: 1px solid #aaa; position: absolute; top: -15px; right: 10px; font-size: 11px; } -.block .visualize .visualize-title { display: block; color: #333; margin-bottom: 3px; } -.block .visualize ul.visualize-key { list-style: none; } -.block .visualize ul.visualize-key li { list-style: none; float: left; margin-right: 10px; padding-left: 10px; position: relative;} -.block .visualize ul.visualize-key .visualize-key-color { width: 6px; height: 6px; left: 0; position: absolute; top: 50%; margin-top: -3px; font-size: 6px; } -.block .visualize ul.visualize-key .visualize-key-label { color: #333; } - -/*pie labels*/ -.visualize-pie .visualize-labels { list-style: none; } -.visualize-pie .visualize-label-pos, .visualize-pie .visualize-label { position: absolute; margin: 0; padding:0; } -.visualize-pie .visualize-label { display: block; color: #fff; font-weight: bold; font-size: 1em; } -.visualize-pie-outside .visualize-label { color: #000; font-weight: normal; } - -/*line,bar, area labels*/ -.block .visualize-labels-x,.visualize-labels-y { position: absolute; left: 0; top: 0; list-style: none; } -.block .visualize-labels-x li, .visualize-labels-y li { position: absolute; bottom: 0; } -.block .visualize-labels-x li span.label, .visualize-labels-y li span.label { position: absolute; color: #555; } -.block .visualize-labels-x li span.line, .visualize-labels-y li span.line { position: absolute; border: 0 solid #ccc; } -.block .visualize-labels-x li { height: 100%; font-size: 10px; } -.block .visualize-labels-x li span.label { top: 100%; margin-top: 15px; left:-10; -webkit-transform: rotate(-45deg); -moz-transform: rotate(-45deg); } -.block .visualize-labels-x li span.line { border-left-width: 1px; height: 100%; display: block; } -.block .visualize-labels-x li span.line { border: 0;} /*hide vertical lines on area, line, bar*/ -.block .visualize-labels-y li { width: 100%; font-size: 11px; line-height: normal; } -.block .visualize-labels-y li span.label { right: 100%; margin-right: 5px; display: block; width: 100px; text-align: right; } -.block .visualize-labels-y li span.line { border-top-width: 1px; width: 100%; } -.block .visualize-bar .visualize-labels-x li span.label { width: 100%; text-align: center; } - -/*tooltips*/ -.block .visualize .chart_tooltip { - padding: 6px 7px; - background: #000; - background: url(../images/mbg.png) 0 0 repeat; - margin: 3px 4px 0; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - color: #ddd; - font-size: 10px; - line-height: normal; - } diff --git a/public/site_assets/mmcFE/images/ajax-loader.gif b/public/site_assets/mmcFE/images/ajax-loader.gif deleted file mode 100644 index 7eca4167..00000000 Binary files a/public/site_assets/mmcFE/images/ajax-loader.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/b.png b/public/site_assets/mmcFE/images/b.png deleted file mode 100644 index f184e626..00000000 Binary files a/public/site_assets/mmcFE/images/b.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/background.gif b/public/site_assets/mmcFE/images/background.gif deleted file mode 100644 index 864b95e9..00000000 Binary files a/public/site_assets/mmcFE/images/background.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bbg.gif b/public/site_assets/mmcFE/images/bbg.gif deleted file mode 100644 index ed5933a9..00000000 Binary files a/public/site_assets/mmcFE/images/bbg.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bbgs.gif b/public/site_assets/mmcFE/images/bbgs.gif deleted file mode 100644 index 9cc48671..00000000 Binary files a/public/site_assets/mmcFE/images/bbgs.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bendl.gif b/public/site_assets/mmcFE/images/bendl.gif deleted file mode 100644 index ff438843..00000000 Binary files a/public/site_assets/mmcFE/images/bendl.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bendr.gif b/public/site_assets/mmcFE/images/bendr.gif deleted file mode 100644 index 6d8d67d4..00000000 Binary files a/public/site_assets/mmcFE/images/bendr.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bendsb.gif b/public/site_assets/mmcFE/images/bendsb.gif deleted file mode 100644 index c3148c39..00000000 Binary files a/public/site_assets/mmcFE/images/bendsb.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bg.jpg b/public/site_assets/mmcFE/images/bg.jpg deleted file mode 100644 index a5cd7839..00000000 Binary files a/public/site_assets/mmcFE/images/bg.jpg and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bhead.gif b/public/site_assets/mmcFE/images/bhead.gif deleted file mode 100644 index 057cad7e..00000000 Binary files a/public/site_assets/mmcFE/images/bhead.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bheadl.gif b/public/site_assets/mmcFE/images/bheadl.gif deleted file mode 100644 index 6b2524b3..00000000 Binary files a/public/site_assets/mmcFE/images/bheadl.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bheadr.gif b/public/site_assets/mmcFE/images/bheadr.gif deleted file mode 100644 index a3702656..00000000 Binary files a/public/site_assets/mmcFE/images/bheadr.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bitcoin.png b/public/site_assets/mmcFE/images/bitcoin.png deleted file mode 100644 index 0d8b6d73..00000000 Binary files a/public/site_assets/mmcFE/images/bitcoin.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bl.png b/public/site_assets/mmcFE/images/bl.png deleted file mode 100644 index f6271859..00000000 Binary files a/public/site_assets/mmcFE/images/bl.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/black_arrow.png b/public/site_assets/mmcFE/images/black_arrow.png deleted file mode 100644 index 800c7e1d..00000000 Binary files a/public/site_assets/mmcFE/images/black_arrow.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/bnd.gif b/public/site_assets/mmcFE/images/bnd.gif deleted file mode 100644 index 4733f086..00000000 Binary files a/public/site_assets/mmcFE/images/bnd.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/br.png b/public/site_assets/mmcFE/images/br.png deleted file mode 100644 index 31f204fc..00000000 Binary files a/public/site_assets/mmcFE/images/br.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/btnb.gif b/public/site_assets/mmcFE/images/btnb.gif deleted file mode 100644 index 590aae49..00000000 Binary files a/public/site_assets/mmcFE/images/btnb.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/btnb_.gif b/public/site_assets/mmcFE/images/btnb_.gif deleted file mode 100644 index dabcccd5..00000000 Binary files a/public/site_assets/mmcFE/images/btnb_.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/btnm.gif b/public/site_assets/mmcFE/images/btnm.gif deleted file mode 100644 index 9372f2f5..00000000 Binary files a/public/site_assets/mmcFE/images/btnm.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/btnm_.gif b/public/site_assets/mmcFE/images/btnm_.gif deleted file mode 100644 index ba495ff7..00000000 Binary files a/public/site_assets/mmcFE/images/btnm_.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/btns.gif b/public/site_assets/mmcFE/images/btns.gif deleted file mode 100644 index cccd2aca..00000000 Binary files a/public/site_assets/mmcFE/images/btns.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/btns_.gif b/public/site_assets/mmcFE/images/btns_.gif deleted file mode 100644 index ac096887..00000000 Binary files a/public/site_assets/mmcFE/images/btns_.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/cal.jpg b/public/site_assets/mmcFE/images/cal.jpg deleted file mode 100644 index 7356516d..00000000 Binary files a/public/site_assets/mmcFE/images/cal.jpg and /dev/null differ diff --git a/public/site_assets/mmcFE/images/close.png b/public/site_assets/mmcFE/images/close.png deleted file mode 100644 index 18b5614c..00000000 Binary files a/public/site_assets/mmcFE/images/close.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/closelabel.gif b/public/site_assets/mmcFE/images/closelabel.gif deleted file mode 100644 index 87b4f8bd..00000000 Binary files a/public/site_assets/mmcFE/images/closelabel.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/contentBg-header.png b/public/site_assets/mmcFE/images/contentBg-header.png deleted file mode 100644 index f93ec4cb..00000000 Binary files a/public/site_assets/mmcFE/images/contentBg-header.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/contentBg.png b/public/site_assets/mmcFE/images/contentBg.png deleted file mode 100644 index 5307d412..00000000 Binary files a/public/site_assets/mmcFE/images/contentBg.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/error.gif b/public/site_assets/mmcFE/images/error.gif deleted file mode 100644 index 3e847331..00000000 Binary files a/public/site_assets/mmcFE/images/error.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/first.png b/public/site_assets/mmcFE/images/first.png deleted file mode 100644 index 6f11fcb0..00000000 Binary files a/public/site_assets/mmcFE/images/first.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/ft.gif b/public/site_assets/mmcFE/images/ft.gif deleted file mode 100644 index df5a4de3..00000000 Binary files a/public/site_assets/mmcFE/images/ft.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/hdr.gif b/public/site_assets/mmcFE/images/hdr.gif deleted file mode 100644 index 3dac7770..00000000 Binary files a/public/site_assets/mmcFE/images/hdr.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/hdrl.gif b/public/site_assets/mmcFE/images/hdrl.gif deleted file mode 100644 index d74e2356..00000000 Binary files a/public/site_assets/mmcFE/images/hdrl.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/hdrr.gif b/public/site_assets/mmcFE/images/hdrr.gif deleted file mode 100644 index 54eba2ac..00000000 Binary files a/public/site_assets/mmcFE/images/hdrr.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/hld.jpg b/public/site_assets/mmcFE/images/hld.jpg deleted file mode 100644 index 747b88c7..00000000 Binary files a/public/site_assets/mmcFE/images/hld.jpg and /dev/null differ diff --git a/public/site_assets/mmcFE/images/imgb.gif b/public/site_assets/mmcFE/images/imgb.gif deleted file mode 100644 index 9d41c4fc..00000000 Binary files a/public/site_assets/mmcFE/images/imgb.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/imgo.gif b/public/site_assets/mmcFE/images/imgo.gif deleted file mode 100644 index 63b6db6f..00000000 Binary files a/public/site_assets/mmcFE/images/imgo.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/imgt.gif b/public/site_assets/mmcFE/images/imgt.gif deleted file mode 100644 index a8031343..00000000 Binary files a/public/site_assets/mmcFE/images/imgt.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/info.gif b/public/site_assets/mmcFE/images/info.gif deleted file mode 100644 index d44d855d..00000000 Binary files a/public/site_assets/mmcFE/images/info.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/jquery.wysiwyg.gif b/public/site_assets/mmcFE/images/jquery.wysiwyg.gif deleted file mode 100644 index fd64e366..00000000 Binary files a/public/site_assets/mmcFE/images/jquery.wysiwyg.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/last.png b/public/site_assets/mmcFE/images/last.png deleted file mode 100644 index 72079357..00000000 Binary files a/public/site_assets/mmcFE/images/last.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/left_main_top_bg.gif b/public/site_assets/mmcFE/images/left_main_top_bg.gif deleted file mode 100644 index e097cf2a..00000000 Binary files a/public/site_assets/mmcFE/images/left_main_top_bg.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/li.gif b/public/site_assets/mmcFE/images/li.gif deleted file mode 100644 index f1c0306d..00000000 Binary files a/public/site_assets/mmcFE/images/li.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/loading.gif b/public/site_assets/mmcFE/images/loading.gif deleted file mode 100644 index f864d5fd..00000000 Binary files a/public/site_assets/mmcFE/images/loading.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/mbg.png b/public/site_assets/mmcFE/images/mbg.png deleted file mode 100644 index 0faf2be3..00000000 Binary files a/public/site_assets/mmcFE/images/mbg.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/next.png b/public/site_assets/mmcFE/images/next.png deleted file mode 100644 index 4a2f9d4e..00000000 Binary files a/public/site_assets/mmcFE/images/next.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/nsp.gif b/public/site_assets/mmcFE/images/nsp.gif deleted file mode 100644 index b805aff6..00000000 Binary files a/public/site_assets/mmcFE/images/nsp.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/phs.gif b/public/site_assets/mmcFE/images/phs.gif deleted file mode 100644 index ae1fd7fb..00000000 Binary files a/public/site_assets/mmcFE/images/phs.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/prev.png b/public/site_assets/mmcFE/images/prev.png deleted file mode 100644 index 15d1584b..00000000 Binary files a/public/site_assets/mmcFE/images/prev.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/questionmark.png b/public/site_assets/mmcFE/images/questionmark.png deleted file mode 100644 index f3405a01..00000000 Binary files a/public/site_assets/mmcFE/images/questionmark.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/sdd.jpg b/public/site_assets/mmcFE/images/sdd.jpg deleted file mode 100644 index 8228fbaa..00000000 Binary files a/public/site_assets/mmcFE/images/sdd.jpg and /dev/null differ diff --git a/public/site_assets/mmcFE/images/sdd_.jpg b/public/site_assets/mmcFE/images/sdd_.jpg deleted file mode 100644 index 623904cf..00000000 Binary files a/public/site_assets/mmcFE/images/sdd_.jpg and /dev/null differ diff --git a/public/site_assets/mmcFE/images/shares_round.png b/public/site_assets/mmcFE/images/shares_round.png deleted file mode 100644 index 0d807853..00000000 Binary files a/public/site_assets/mmcFE/images/shares_round.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/sidebar.gif b/public/site_assets/mmcFE/images/sidebar.gif deleted file mode 100644 index 85577b1c..00000000 Binary files a/public/site_assets/mmcFE/images/sidebar.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/sorta.gif b/public/site_assets/mmcFE/images/sorta.gif deleted file mode 100644 index 9c19b5cc..00000000 Binary files a/public/site_assets/mmcFE/images/sorta.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/sortd.gif b/public/site_assets/mmcFE/images/sortd.gif deleted file mode 100644 index 8ce166ba..00000000 Binary files a/public/site_assets/mmcFE/images/sortd.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/srch.gif b/public/site_assets/mmcFE/images/srch.gif deleted file mode 100644 index 24e4bc9f..00000000 Binary files a/public/site_assets/mmcFE/images/srch.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/srch_.gif b/public/site_assets/mmcFE/images/srch_.gif deleted file mode 100644 index 4766feb8..00000000 Binary files a/public/site_assets/mmcFE/images/srch_.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/success.gif b/public/site_assets/mmcFE/images/success.gif deleted file mode 100644 index 69af8590..00000000 Binary files a/public/site_assets/mmcFE/images/success.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/thumb1.jpg b/public/site_assets/mmcFE/images/thumb1.jpg deleted file mode 100644 index c4095307..00000000 Binary files a/public/site_assets/mmcFE/images/thumb1.jpg and /dev/null differ diff --git a/public/site_assets/mmcFE/images/thumb2.jpg b/public/site_assets/mmcFE/images/thumb2.jpg deleted file mode 100644 index 1e947fb6..00000000 Binary files a/public/site_assets/mmcFE/images/thumb2.jpg and /dev/null differ diff --git a/public/site_assets/mmcFE/images/thumb3.jpg b/public/site_assets/mmcFE/images/thumb3.jpg deleted file mode 100644 index 213c443b..00000000 Binary files a/public/site_assets/mmcFE/images/thumb3.jpg and /dev/null differ diff --git a/public/site_assets/mmcFE/images/tiny.gif b/public/site_assets/mmcFE/images/tiny.gif deleted file mode 100644 index 56c2b473..00000000 Binary files a/public/site_assets/mmcFE/images/tiny.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/tiny_.gif b/public/site_assets/mmcFE/images/tiny_.gif deleted file mode 100644 index 6f17709b..00000000 Binary files a/public/site_assets/mmcFE/images/tiny_.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/tl.png b/public/site_assets/mmcFE/images/tl.png deleted file mode 100644 index d99c8f6c..00000000 Binary files a/public/site_assets/mmcFE/images/tl.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/tr.png b/public/site_assets/mmcFE/images/tr.png deleted file mode 100644 index e99b6ec8..00000000 Binary files a/public/site_assets/mmcFE/images/tr.png and /dev/null differ diff --git a/public/site_assets/mmcFE/images/upload.gif b/public/site_assets/mmcFE/images/upload.gif deleted file mode 100644 index 5a5b06ce..00000000 Binary files a/public/site_assets/mmcFE/images/upload.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/images/warning.gif b/public/site_assets/mmcFE/images/warning.gif deleted file mode 100644 index ce03c453..00000000 Binary files a/public/site_assets/mmcFE/images/warning.gif and /dev/null differ diff --git a/public/site_assets/mmcFE/js/custom.js b/public/site_assets/mmcFE/js/custom.js deleted file mode 100644 index 9d63724a..00000000 --- a/public/site_assets/mmcFE/js/custom.js +++ /dev/null @@ -1,159 +0,0 @@ -$(function () { - - // CSS tweaks - $('#header #nav li:last').addClass('nobg'); - $('.block_head ul').each(function () { - $('li:first', this).addClass('nobg'); - }); - $('.block form input[type=file]').addClass('file'); - - // Web stats - $('table.stats').each(function () { - - if ($(this).attr('rel')) { - var statsType = $(this).attr('rel'); - } else { - var statsType = 'area'; - } - - // hack to statically set width as something is broken with div width calculation - anni - var chart_width = $(document).width() - 400; - - if (statsType == 'line' || statsType == 'pie') { - $(this).hide().visualize({ - type: statsType, - // 'bar', 'area', 'pie', 'line' - width: chart_width, - height: '240px', - colors: ['#6fb9e8', '#ec8526', '#9dc453', '#ddd74c'], - lineDots: 'double', - interaction: true, - multiHover: 5, - tooltip: true, - tooltiphtml: function (data) { - var html = ''; - for (var i = 0; i < data.point.length; i++) { - html += '

' + data.point[i].value + ' ' + data.point[i].yLabels[0] + '

'; - } - return html; - } - }); - } else { - $(this).hide().visualize({ - // 'bar', 'area', 'pie', 'line' - width: chart_width, - type: statsType, - height: '240px', - colors: ['#6fb9e8', '#ec8526', '#9dc453', '#ddd74c'] - }); - } - }); - - // Sort table - $("table.sortable").tablesorter({ - headers: { - 0: { - //sorter: false - }, - 5: { - //sorter: false - } - }, - // Disabled on the 1st and 6th columns - widgets: ['zebra'] - }); - - $("table.pagesort") - .tablesorter({ widgets: ['zebra'] }) - .tablesorterPager({ positionFixed: false, container: $("#pager") }); - $("table.pagesort2") - .tablesorter({ widgets: ['zebra'] }) - .tablesorterPager({ positionFixed: false, container: $("#pager2") }); - $("table.pagesort4") - .tablesorter({ widgets: ['zebra'] }) - .tablesorterPager({ positionFixed: false, container: $("#pager3") }); - - $('.block table tr th.header').css('cursor', 'pointer'); - - // Check / uncheck all checkboxes - $('.check_all').click(function () { - $(this).parents('form').find('input:checkbox').attr('checked', $(this).is(':checked')); - }); - - // Messages - $('.block .message').hide().append('').fadeIn('slow'); - $('.block .message .close').hover( - - function () { - $(this).addClass('hover'); - }, function () { - $(this).removeClass('hover'); - }); - - $('.block .message .close').click(function () { - $(this).parent().fadeOut('slow', function () { - $(this).remove(); - }); - }); - - // Tabs - $(".tab_content").hide(); - $("ul.tabs li:first-child").addClass("active").show(); - $(".block").find(".tab_content:first").show(); - - $("ul.tabs li").click(function () { - $(this).parent().find('li').removeClass("active"); - $(this).addClass("active"); - $(this).parents('.block').find(".tab_content").hide(); - - var activeTab = $(this).find("a").attr("href"); - $(activeTab).show(); - - // refresh visualize for IE - $(activeTab).find('.visualize').trigger('visualizeRefresh'); - - return false; - }); - - // Sidebar Tabs - $(".sidebar_content").hide(); - - if (window.location.hash && window.location.hash.match('sb')) { - - $("ul.sidemenu li a[href=" + window.location.hash + "]").parent().addClass("active").show(); - $(".block .sidebar_content#" + window.location.hash).show(); - } else { - - $("ul.sidemenu li:first-child").addClass("active").show(); - $(".block .sidebar_content:first").show(); - } - - $("ul.sidemenu li").click(function () { - - var activeTab = $(this).find("a").attr("href"); - window.location.hash = activeTab; - - $(this).parent().find('li').removeClass("active"); - $(this).addClass("active"); - $(this).parents('.block').find(".sidebar_content").hide(); - $(activeTab).show(); - return false; - }); - - // Block search - $('.block .block_head form .text').bind('click', function () { - $(this).attr('value', ''); - }); - - // Navigation dropdown fix for IE6 - if (jQuery.browser.version.substr(0, 1) < 7) { - $('#header #nav li').hover( - - function () { - $(this).addClass('iehover'); - }, function () { - $(this).removeClass('iehover'); - }); - } - -}); diff --git a/public/site_assets/mmcFE/js/excanvas.js b/public/site_assets/mmcFE/js/excanvas.js deleted file mode 100644 index 3cf5d274..00000000 --- a/public/site_assets/mmcFE/js/excanvas.js +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2006 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -document.createElement("canvas").getContext||(function(){var s=Math,j=s.round,F=s.sin,G=s.cos,V=s.abs,W=s.sqrt,k=10,v=k/2;function X(){return this.context_||(this.context_=new H(this))}var L=Array.prototype.slice;function Y(b,a){var c=L.call(arguments,2);return function(){return b.apply(a,c.concat(L.call(arguments)))}}var M={init:function(b){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var a=b||document;a.createElement("canvas");a.attachEvent("onreadystatechange",Y(this.init_,this,a))}},init_:function(b){b.namespaces.g_vml_|| -b.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML");b.namespaces.g_o_||b.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML");if(!b.styleSheets.ex_canvas_){var a=b.createStyleSheet();a.owningElement.id="ex_canvas_";a.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}g_o_\\:*{behavior:url(#default#VML)}"}var c=b.getElementsByTagName("canvas"),d=0;for(;d','","");this.element_.insertAdjacentHTML("BeforeEnd",t.join(""))};i.stroke=function(b){var a=[],c=P(b?this.fillStyle:this.strokeStyle),d=c.color,f=c.alpha*this.globalAlpha;a.push("g.x)g.x=e.x;if(h.y==null||e.yg.y)g.y=e.y}}a.push(' ">');if(b)if(typeof this.fillStyle=="object"){var m=this.fillStyle,r=0,n={x:0,y:0},o=0,q=1;if(m.type_=="gradient"){var t=m.x1_/this.arcScaleX_,E=m.y1_/this.arcScaleY_,p=this.getCoords_(m.x0_/this.arcScaleX_,m.y0_/this.arcScaleY_), -z=this.getCoords_(t,E);r=Math.atan2(z.x-p.x,z.y-p.y)*180/Math.PI;if(r<0)r+=360;if(r<1.0E-6)r=0}else{var p=this.getCoords_(m.x0_,m.y0_),w=g.x-h.x,x=g.y-h.y;n={x:(p.x-h.x)/w,y:(p.y-h.y)/x};w/=this.arcScaleX_*k;x/=this.arcScaleY_*k;var R=s.max(w,x);o=2*m.r0_/R;q=2*m.r1_/R-o}var u=m.colors_;u.sort(function(ba,ca){return ba.offset-ca.offset});var J=u.length,da=u[0].color,ea=u[J-1].color,fa=u[0].alpha*this.globalAlpha,ga=u[J-1].alpha*this.globalAlpha,S=[],l=0;for(;l')}else a.push('');else{var K=this.lineScale_*this.lineWidth;if(K<1)f*=K;a.push("')}a.push("");this.element_.insertAdjacentHTML("beforeEnd",a.join(""))};i.fill=function(){this.stroke(true)};i.closePath=function(){this.currentPath_.push({type:"close"})};i.getCoords_=function(b,a){var c=this.m_;return{x:k*(b*c[0][0]+a*c[1][0]+c[2][0])-v,y:k*(b*c[0][1]+a*c[1][1]+c[2][1])-v}};i.save=function(){var b={};O(this,b);this.aStack_.push(b);this.mStack_.push(this.m_);this.m_=y(I(),this.m_)};i.restore=function(){O(this.aStack_.pop(), -this);this.m_=this.mStack_.pop()};function ha(b){var a=0;for(;a<3;a++){var c=0;for(;c<2;c++)if(!isFinite(b[a][c])||isNaN(b[a][c]))return false}return true}function A(b,a,c){if(!!ha(a)){b.m_=a;if(c)b.lineScale_=W(V(a[0][0]*a[1][1]-a[0][1]*a[1][0]))}}i.translate=function(b,a){A(this,y([[1,0,0],[0,1,0],[b,a,1]],this.m_),false)};i.rotate=function(b){var a=G(b),c=F(b);A(this,y([[a,c,0],[-c,a,0],[0,0,1]],this.m_),false)};i.scale=function(b,a){this.arcScaleX_*=b;this.arcScaleY_*=a;A(this,y([[b,0,0],[0,a, -0],[0,0,1]],this.m_),true)};i.transform=function(b,a,c,d,f,h){A(this,y([[b,a,0],[c,d,0],[f,h,1]],this.m_),true)};i.setTransform=function(b,a,c,d,f,h){A(this,[[b,a,0],[c,d,0],[f,h,1]],true)};i.clip=function(){};i.arcTo=function(){};i.createPattern=function(){return new U};function D(b){this.type_=b;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}D.prototype.addColorStop=function(b,a){a=P(a);this.colors_.push({offset:b,color:a.color,alpha:a.alpha})};function U(){}G_vmlCanvasManager= -M;CanvasRenderingContext2D=H;CanvasGradient=D;CanvasPattern=U})(); - diff --git a/public/site_assets/mmcFE/js/jquery-1.9.1.min.js b/public/site_assets/mmcFE/js/jquery-1.9.1.min.js deleted file mode 100644 index 006e9531..00000000 --- a/public/site_assets/mmcFE/js/jquery-1.9.1.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license -//@ sourceMappingURL=jquery.min.map -*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="
t
",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj; -return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="
",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:b.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l) -}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("