From dbd73195a969b428df7d3b8470fc4f225f193326 Mon Sep 17 00:00:00 2001 From: Michael Hamann Date: Sat, 30 Mar 2013 23:22:31 +0100 Subject: [PATCH] Add new namespace move functionality including ajax Namespace moves are now split into junks of at maximum 10 pages --- _test/namespace_move.test.php | 40 +++++++ action.php | 68 ++++++++++++ admin.php | 146 ++++++++++++++++++++---- helper.php | 202 ++++++++++++++++++++++++++++++++++ lang/en/lang.php | 9 ++ script.js | 58 +++++++++- 6 files changed, 498 insertions(+), 25 deletions(-) create mode 100644 _test/namespace_move.test.php diff --git a/_test/namespace_move.test.php b/_test/namespace_move.test.php new file mode 100644 index 0000000..d6d51d1 --- /dev/null +++ b/_test/namespace_move.test.php @@ -0,0 +1,40 @@ +pluginsEnabled[] = 'pagemove'; + parent::setUp(); + } + + public function test_move_wiki_namespace() { + global $AUTH_ACL; + + $AUTH_ACL[] = "wiki:*\t@ALL\t16"; + + idx_addPage('wiki:dokuwiki'); + idx_addPage('wiki:syntax'); + + /** @var helper_plugin_pagemove $pagemove */ + $pagemove = plugin_load('helper', 'pagemove'); + $opts = array( + 'ns' => 'wiki', + 'newns' => 'foo', + 'media' => true + ); + + $this->assertSame(3, $pagemove->start_namespace_move($opts)); + $this->assertSame(1, $pagemove->continue_namespace_move()); + $this->assertSame(0, $pagemove->continue_namespace_move()); + + $this->assertFileExists(wikiFN('foo:dokuwiki')); + $this->assertFileNotExists(wikiFN('wiki:syntax')); + $this->assertFileExists(mediaFN('foo:dokuwiki-128.png')); + } +} diff --git a/action.php b/action.php index 3033301..09ae870 100644 --- a/action.php +++ b/action.php @@ -22,6 +22,7 @@ public function register($controller) { $controller->register_hook('PARSER_CACHE_USE', 'BEFORE', $this, 'handle_cache', array()); $controller->register_hook('INDEXER_VERSION_GET', 'BEFORE', $this, 'handle_index_version'); $controller->register_hook('INDEXER_PAGE_ADD', 'BEFORE', $this, 'index_media_use'); + $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax_call'); } /** @@ -151,4 +152,71 @@ private function get_media_references_from_instructions($instructions, &$media_r } } } + + /** + * Handle the plugin_pagemove_ns_continue ajax call + * + * @param Doku_Event $event The event that is handled + * @param array $params Optional parameters (unused) + */ + public function handle_ajax_call(Doku_Event $event, $params) { + if ($event->data == 'plugin_pagemove_ns_continue') { + $event->preventDefault(); + $event->stopPropagation(); + + /** @var helper_plugin_pagemove $helper */ + $helper = $this->loadHelper('pagemove', false); + $opts = $helper->get_namespace_move_opts(); + $id = cleanID((string)$_POST['id']); + $skip = (int)$_POST['skip']; + if ($opts !== false) { + if ($skip) { + $helper->skip_namespace_move_item(); + } + $remaining = $helper->continue_namespace_move(); + $newid = $helper->getNewID($id, $opts['ns'], $opts['newns']); + + $result = array(); + $result['remaining'] = $remaining; + $result['pages'] = $opts['num_pages']; + $result['media'] = $opts['num_media']; + $result['redirect_url'] = wl($newid, '', true); + ob_start(); + html_msgarea(); + if ($remaining === false) { + $form = new Doku_Form(array('action' => wl($id), 'method' => 'post', 'class' => 'pagemove__nsform pagemove__nscontinue')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $id); + $form->addHidden('continue_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_tryagain'))); + $form->printForm(); + $form = new Doku_Form(array('action' => wl($id), 'method' => 'post', 'class' => 'pagemove__nsform')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $id); + $form->addHidden('abort_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_abort'))); + $form->printForm(); + $form = new Doku_Form(array('action' => wl($id), 'method' => 'post', 'class' => 'pagemove__nsform pagemove__nsskip')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $id); + $form->addHidden('skip_continue_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_skip'))); + $form->printForm(); + ptln('

'.sprintf($this->getLang('pm_ns_move_error'), $opts['ns'], $opts['newns']).'

'); + } else { + ptln('

'.sprintf($this->getLang('pm_ns_move_continued'), $opts['ns'], $opts['newns'], $remaining).'

'); + } + $result['html'] = ob_get_clean(); + } else { + $result = array(); + $result['remaining'] = 0; + $result['pages'] = 0; + $result['media'] = 0; + $result['redirect_url'] = wl('', '', true); + $result['html'] = ''; + } + $json = new JSON(); + echo $json->encode($result); + } + } } \ No newline at end of file diff --git a/admin.php b/admin.php index 07b41bd..f6a723e 100644 --- a/admin.php +++ b/admin.php @@ -16,6 +16,7 @@ class admin_plugin_pagemove extends DokuWiki_Admin_Plugin { var $opts = array(); + private $ns_opts = false; /** * Get the sort number that defines the position in the admin menu. @@ -62,9 +63,71 @@ function getMenuText() { * @author Gary Owen */ function html() { + global $ID; ptln(''); ptln( $this->locale_xhtml('pagemove') ); - $this->printForm(); + ptln('
'); + if ($this->ns_opts !== false && isset($this->ns_opts['started'])) { + ptln('

'); + ptln(sprintf($this->getLang('pm_ns_move_started'), hsc($this->ns_opts['ns']), hsc($this->ns_opts['newns']), $this->ns_opts['num_pages'], $this->ns_opts['num_media'])); + ptln('

'); + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__nsform pagemove__nscontinue')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('continue_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_continue'))); + $form->printForm(); + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__nsform')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('abort_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_abort'))); + $form->printForm(); + } elseif ($this->ns_opts !== false && isset($this->ns_opts['remaining'])) { + if ($this->ns_opts['remaining'] === false) { + ptln('

'); + ptln(sprintf($this->getLang('pm_ns_move_error'), $this->ns_opts['ns'], $this->ns_opts['newns'])); + ptln('

'); + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__nsform pagemove__nscontinue')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('continue_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_tryagain'))); + $form->printForm(); + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__nsform')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('abort_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_abort'))); + $form->printForm(); + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__nsform')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('skip_continue_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_skip'))); + $form->printForm(); + } else { + ptln('

'); + ptln(sprintf($this->getLang('pm_ns_move_continued'), $this->ns_opts['ns'], $this->ns_opts['newns'], $this->ns_opts['remaining'])); + ptln('

'); + + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__nsform')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('continue_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_continue'))); + $form->printForm(); + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__nsform')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('abort_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_abort'))); + $form->printForm(); + } + } else { + $this->printForm(); + } + ptln('
'); ptln(''); } @@ -93,16 +156,39 @@ function printForm() { $form->endFieldset(); $form->printForm(); - $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__form')); - $form->addHidden('page', $this->getPluginName()); - $form->addHidden('id', $ID); - $form->addHidden('move_type', 'namespace'); - $form->startFieldset($this->getLang('pm_movens')); - $form->addElement(form_makeMenuField('targetns', $ns_select_data, $this->opts['targetns'], $this->getLang('pm_targetns'), '', 'block')); - $form->addElement(form_makeTextField('newnsname', $this->opts['newnsname'], $this->getLang('pm_newnsname'), '', 'block')); - $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_submit'))); - $form->endFieldset(); - $form->printForm(); + if ($this->ns_opts !== false && !isset($this->ns_opts['remaining'])) { + ptln('
'); + ptln(''); + ptln($this->getLang('pm_movens')); + ptln(''); + ptln('

'); + ptln(sprintf($this->getLang('pm_ns_move_in_progress'), $this->ns_opts['num_pages'], $this->ns_opts['num_media'], ':'.hsc($this->ns_opts['ns']), ':'.hsc($this->ns_opts['newns']))); + ptln('

'); + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__nsform pagemove__nscontinue')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('continue_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_continue'))); + $form->printForm(); + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__nsform')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('abort_namespace_move', true); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_ns_move_abort'))); + $form->printForm(); + ptln('
'); + } else { + $form = new Doku_Form(array('action' => wl($ID), 'method' => 'post', 'class' => 'pagemove__form')); + $form->addHidden('page', $this->getPluginName()); + $form->addHidden('id', $ID); + $form->addHidden('move_type', 'namespace'); + $form->startFieldset($this->getLang('pm_movens')); + $form->addElement(form_makeMenuField('targetns', $ns_select_data, $this->opts['targetns'], $this->getLang('pm_targetns'), '', 'block')); + $form->addElement(form_makeTextField('newnsname', $this->opts['newnsname'], $this->getLang('pm_newnsname'), '', 'block')); + $form->addElement(form_makeButton('submit', 'admin', $this->getLang('pm_submit'))); + $form->endFieldset(); + $form->printForm(); + } } @@ -153,11 +239,35 @@ function handle() { $this->opts['newnsname'] = ''; $this->opts['move_type'] = 'page'; + /** @var helper_plugin_pagemove $helper */ + $helper = $this->loadHelper('pagemove', true); + if (!$helper) return; + + $this->ns_opts = $helper->get_namespace_move_opts(); + // Only continue when the form was submitted if ($_SERVER['REQUEST_METHOD'] != 'POST') { return; } + if (isset($_POST['continue_namespace_move']) || isset($_POST['skip_continue_namespace_move'])) { + if (isset($_POST['skip_continue_namespace_move'])) { + $helper->skip_namespace_move_item(); + } + $this->ns_opts['remaining'] = $helper->continue_namespace_move(); + if ($this->ns_opts['remaining'] === 0) { + $ID = $helper->getNewID($INFO['id'], $this->opts['ns'], $this->opts['newns']); + $ACT = 'show'; + } + + return; + } elseif (isset($_POST['abort_namespace_move'])) { + $helper->abort_namespace_move(); + $this->ns_opts = false; + + return; + } + // Store the form data in the options and clean the submitted data. if (isset($_POST['ns_for_page'])) $this->opts['ns_for_page'] = cleanID((string)$_POST['ns_for_page']); if (isset($_POST['newns'])) $this->opts['newns'] = cleanID((string)$_POST['newns']); @@ -166,22 +276,20 @@ function handle() { if (isset($_POST['newnsname'])) $this->opts['newnsname'] = cleanID((string)$_POST['newnsname']); if (isset($_POST['move_type'])) $this->opts['move_type'] = (string)$_POST['move_type']; - /** @var helper_plugin_pagemove $helper */ - $helper = $this->loadHelper('pagemove', true); - if (!$helper) return; - // check the input for completeness if( $this->opts['move_type'] == 'namespace' ) { + $this->opts['media'] = true; // FIXME: add checkbox later! + if ($this->opts['targetns'] == '') { $this->opts['newns'] = $this->opts['newnsname']; } else { $this->opts['newns'] = $this->opts['targetns'].':'.$this->opts['newnsname']; } - if ($helper->move_namespace($this->opts, true) && - $helper->move_namespace($this->opts)) { - $ID = $helper->getNewID($INFO['id'], $this->opts['ns'], $this->opts['newns']); - $ACT = 'show'; + $started = $helper->start_namespace_move($this->opts); + if ($started !== false) { + $this->ns_opts = $helper->get_namespace_move_opts(); + $this->ns_opts['started'] = $started; } } else { // check that the pagename is valid diff --git a/helper.php b/helper.php index 17a8d78..8fe8e15 100644 --- a/helper.php +++ b/helper.php @@ -44,6 +44,208 @@ function move_namespace(&$opts, $checkonly = false) { return true; } + /** + * Start a namespace move by creating the list of all pages and media files that shall be moved + * + * @param array $opts The options for the namespace move + * @return int The number of items to move + */ + public function start_namespace_move(&$opts) { + global $conf; + + // generate and save a list of all pages + $pagelist = array(); + $pathToSearch = utf8_encodeFN(str_replace(':', '/', $opts['ns'])); + $searchOpts = array('depth' => 0, 'skipacl' => true); + search($pagelist, $conf['datadir'], 'search_allpages', $searchOpts, $pathToSearch); + $pages = array(); + foreach ($pagelist as $page) { + $pages[] = $page['id']; + } + unset($pagelist); + + $opts['num_pages'] = count($pages); + + $files = $this->get_namespace_meta_files(); + io_saveFile($files['pagelist'], implode("\n", $pages)); + unset($pages); + + // generate and save a list of all media files + $medialist = array(); + if (isset($opts['media']) && $opts['media']) { + search($medialist, $conf['mediadir'], 'search_media', $searchOpts, $pathToSearch); + } + + $media_files = array(); + foreach ($medialist as $media) { + $media_files[] = $media['id']; + } + unset ($medialist); + + $opts['num_media'] = count($media_files); + + io_saveFile($files['medialist'], implode("\n", $media_files)); + + // save the options + io_saveFile($files['opts'], serialize($opts)); + + return $opts['num_pages'] + $opts['num_media']; + } + + /** + * Execute the next steps (moving up to 10 pages or media files) of the currently running namespace move + * + * @return bool|int False if an error occurred, otherwise the number of remaining moves + */ + public function continue_namespace_move() { + global $ID; + $files = $this->get_namespace_meta_files(); + + if (!@file_exists($files['opts'])) { + msg('Error: there are no saved options', -1); + return false; + } + + $opts = unserialize(file_get_contents($files['opts'])); + + if (@file_exists($files['pagelist'])) { + $pagelist = file($files['pagelist'], FILE_IGNORE_NEW_LINES); + + $limit = min(10, count($pagelist)); + for ($i = 0; $i < $limit; ++$i) { + $ID = array_pop($pagelist); + $newID = $this->getNewID($ID, $opts['ns'], $opts['newns']); + $pageOpts = $opts; + $pageOpts['ns'] = getNS($ID); + $pageOpts['name'] = noNS($ID); + $pageOpts['newname'] = noNS($ID); + $pageOpts['newns'] = getNS($newID); + if (!$this->move_page($pageOpts)) return false; + + // save the list of pages after every move + if (empty($pagelist)) { + unlink($files['pagelist']); + } else { + io_saveFile($files['pagelist'], implode("\n", $pagelist)); + } + } + return count($pagelist) + $opts['num_media']; + } elseif (@file_exists($files['medialist'])) { + $medialist = file($files['medialist'], FILE_IGNORE_NEW_LINES); + + $limit = min(10, count($medialist)); + for ($i = 0; $i < $limit; ++$i) { + $ID = array_pop($medialist); + $newID = $this->getNewID($ID, $opts['ns'], $opts['newns']); + $pageOpts = $opts; + $pageOpts['ns'] = getNS($ID); + $pageOpts['name'] = noNS($ID); + $pageOpts['newname'] = noNS($ID); + $pageOpts['newns'] = getNS($newID); + if (!$this->move_media($pageOpts)) return false; + + // save the list of media files after every move + if (empty($medialist)) { + unlink($files['medialist']); + unlink($files['opts']); + } else { + io_saveFile($files['medialist'], implode("\n", $medialist)); + } + } + return count($medialist); + } else { + unlink($files['opts']); + return 0; + } + } + + /** + * Skip the item that would be executed next in the current namespace move + * + * @return bool|int False if an error occurred, otherwise the number of remaining moves + */ + public function skip_namespace_move_item() { + global $ID; + $files = $this->get_namespace_meta_files(); + + if (!@file_exists($files['opts'])) { + msg('Error: there are no saved options', -1); + return false; + } + + $opts = unserialize(file_get_contents($files['opts'])); + + if (@file_exists($files['pagelist'])) { + $pagelist = file($files['pagelist'], FILE_IGNORE_NEW_LINES); + + $ID = array_pop($pagelist); + // save the list of pages after every move + if (empty($pagelist)) { + unlink($files['pagelist']); + } else { + io_saveFile($files['pagelist'], implode("\n", $pagelist)); + } + return count($pagelist) + $opts['num_media']; + } elseif (@file_exists($files['medialist'])) { + $medialist = file($files['medialist'], FILE_IGNORE_NEW_LINES); + + $ID = array_pop($medialist); + // save the list of media files after every move + if (empty($medialist)) { + unlink($files['medialist']); + unlink($files['opts']); + } else { + io_saveFile($files['medialist'], implode("\n", $medialist)); + } + + return count($medialist); + } else { + unlink($files['opts']); + return 0; + } + } + + /** + * Abort the currently running namespace move + */ + public function abort_namespace_move() { + $files = $this->get_namespace_meta_files(); + foreach ($files as $file) { + @unlink($file); + } + } + + /** + * Get the options for the namespace move that is currently in progress if there is any + * + * @return bool|array False if there is no namespace move in progress, otherwise the array of options + */ + public function get_namespace_move_opts() { + $files = $this->get_namespace_meta_files(); + + if (!@file_exists($files['opts'])) { + return false; + } + + $opts = unserialize(file_get_contents($files['opts'])); + + return $opts; + } + + /** + * Get the filenames for the metadata of the pagemove plugin + * + * @return array The file names for opts, pagelist and medialist + */ + protected function get_namespace_meta_files() { + global $conf; + return array( + 'opts' => $conf['metadir'].'/__pagemove_opts', + 'pagelist' => $conf['metadir'].'/__pagemove_pagelist', + 'medialist' => $conf['metadir'].'/__pagemove_medialist' + ); + } + /** * Get the id of a page after a namespace move * diff --git a/lang/en/lang.php b/lang/en/lang.php index 73f1375..c6b5bdc 100644 --- a/lang/en/lang.php +++ b/lang/en/lang.php @@ -36,6 +36,15 @@ $lang['pm_nomediatargetperms'] = 'You don\'t have the permission to create the media file %s.'; $lang['pm_filelocked'] = 'The page %s is locked. Try again later.'; $lang['pm_linkchange'] = 'Links to %s changed to %s'; + +$lang['pm_ns_move_in_progress'] = 'There is currently a namespace move of %s page and %s media files from namespace %s to namespace %s in progress.'; +$lang['pm_ns_move_continue'] = 'Continue the namespace move'; +$lang['pm_ns_move_abort'] = 'Abort the namespace move'; +$lang['pm_ns_move_continued'] = 'The namespace move from namespace %s to namespace %s was continued, %s items are still remaining.'; +$lang['pm_ns_move_started'] = 'A namespace move from namespace %s to namespace %s was started, %s pages and %s media files will be moved.'; +$lang['pm_ns_move_error'] = 'An error occurred while continueing the namespace move from %s to %s.'; +$lang['pm_ns_move_tryagain'] = 'Try again'; +$lang['pm_ns_move_skip'] = 'Skip the current item'; // Form labels $lang['pm_newname'] = 'New page name:'; $lang['pm_newnsname'] = 'New namespace name:'; diff --git a/script.js b/script.js index 853d067..312f655 100644 --- a/script.js +++ b/script.js @@ -10,9 +10,9 @@ jQuery(function() { var $preview = jQuery('

'); $this.find('input[type=submit]').before($preview); var updatePreview = function() { - if ($this.find('input[name=move_type]').attr('value') == 'namespace') { - var targetns = $this.find('select[name=targetns]').attr('value'); - var newnsname = $this.find('input[name=newnsname]').attr('value'); + if ($this.find('input[name=move_type]').val() == 'namespace') { + var targetns = $this.find('select[name=targetns]').val(); + var newnsname = $this.find('input[name=newnsname]').val(); var previewns; if (targetns == ':') { previewns = newnsname; @@ -21,10 +21,13 @@ jQuery(function() { } $preview.text(LANG['plugins']['pagemove']['pm_previewns'].replace('OLDNS', JSINFO['namespace']).replace('NEWNS', previewns)); } else { - var ns_for_page = $this.find('select[name=ns_for_page]').attr('value'); - var newns = $this.find('input[name=newns]').attr('value'); - var newname = $this.find('input[name=newname]').attr('value'); + var ns_for_page = $this.find('select[name=ns_for_page]').val(); + var newns = $this.find('input[name=newns]').val(); + var newname = $this.find('input[name=newname]').val(); var newid = ''; + if (typeof newns == 'undefined') { + return; + } if (newns.replace(/\s/g) != '') { newid = newns + ':'; } else if (ns_for_page != ':') { @@ -39,4 +42,47 @@ jQuery(function() { $this.find('input,select').change(updatePreview); $this.find('input').keyup(updatePreview); }); + + jQuery('form.pagemove__nscontinue').each(function() { + var $this = jQuery(this); + var $container = jQuery('div.plugin__pagemove_forms'); + var submit_handler = function() { + $container.empty(); + var $progressbar = jQuery('
'); + $container.append($progressbar); + $progressbar.progressbar({value: false}); + var $message = jQuery('
'); + $container.append($message); + var skip = jQuery(this).hasClass('pagemove__nsskip'); + + var continue_move = function() { + jQuery.post( + DOKU_BASE + 'lib/exe/ajax.php', + { + call: 'plugin_pagemove_ns_continue', + id: JSINFO['id'], + skip: skip + }, + function(data) { + $progressbar.progressbar('option', 'value', data.pages + data.media - data.remaining); + $progressbar.progressbar('option', 'max', data.pages + data.media); + $message.html(data.html); + if (data.remaining === false) { + $container.find('form.pagemove__nscontinue, form.pagemove__nsskip').submit(submit_handler); + } else if (data.remaining === 0) { + window.location.href = data.redirect_url; + } else { + window.setTimeout(continue_move, 200); + } + }, + 'json' + ); + skip = false; + }; + + continue_move(); + return false; + }; + $this.submit(submit_handler); + }); }); \ No newline at end of file