WP_Ajax hook. Convert MS Word footnotes to Pressbooks compatible [footnotes]
16301
static function convertWordFootnotes() {

		if ( ! current_user_can( 'edit_posts' ) || ! check_ajax_referer( 'pb-footnote-convert', false, false ) ) {
			static::ajaxFailure( __( 'Invalid permissions.', 'pressbooks' ) );
			return;
		}

		$html = urldecode( stripslashes( $_POST['content'] ) );

		/**
		 * Regular expression tip:
		 * (?: ), in contrast to ( ), is used to avoid capturing text.
		 *
		 * A $pattern must capture:
		 *  [0] => ... full capture ...
		 *  [1] => #_ftnref130
		 *  [2] => 130
		 *  [3] => ... the text we want to move ...
		 *
		 * Known MS Word variations:
		 *  href="#_ftn123" (-> #_ftnref123)
		 *  href="#_edn123" (-> #_ednref123)
		 *  href="/Users/foo/Documents/bar/9781426766497.doc#_ftn123" (-> .doc#_ftnref123)
		 *  href="/Users/foo/Documents/bar/9781426766497.docx#_edn123" (-> .docx#_ednref123)
		 *
		 * Known Libre Office variations:
		 *  href="#sdfootnote123sym" (-> #sdfootnote123anc)
		 */
		$patterns = [
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+(#_ftnref([0-9]+))["\']+.*?>(?:[^<]+|.*?)?</a>(.*?)</div>~si', // MS Word
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+.*?[\.doc|\.docx](#_ftnref([0-9]+))["\']+.*?>(?:[^<]+|.*?)?</a>(.*?)</div>~si', // MS Word
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+(#_ednref([0-9]+))["\']+.*?>(?:[^<]+|.*?)?</a>(.*?)</div>~si', // MS Word
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+.*?[\.doc|\.docx](#_ednref([0-9]+))["\']+.*?>(?:[^<]+|.*?)?</a>(.*?)</div>~si', // MS Word
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+(#sdfootnote([0-9]+)anc)["\']+.*?>(?:[^<]+|.*?)?</a>(.*?)</div>~si', // Libre Office
		];

		/**
		 * A $replacer must be in the same position as a corresponding $pattern above,
		 * use __REPLACE_ME__ to substitute for what we don't know yet.
		 */
		$replacers = [
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+(?:#_ftn__REPLACE_ME__)["\']+.*?>(?:[^<]+|.*?)?</a>~si', // MS Word
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+.*?[\.doc|\.docx](?:#_ftn__REPLACE_ME__)["\']+.*?>(?:[^<]+|.*?)?</a>~si', // MS Word
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+(?:#_edn__REPLACE_ME__)["\']+.*?>(?:[^<]+|.*?)?</a>~si', // MS Word
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+.*?[\.doc|\.docx](?:#_edn__REPLACE_ME__)["\']+.*?>(?:[^<]+|.*?)?</a>~si', // MS Word
			'~<a[\s]+[^>]*?href[\s]?=[\s"\']+(?:#sdfootnote__REPLACE_ME__sym)["\']+.*?>(?:[^<]+|.*?)?</a>~si', // Libre Office
		];

		$footnotes = [];
		$find = [];
		$replace = [];

		foreach ( $patterns as $i => $pattern ) {

			preg_match_all( $pattern, $html, $footnotes, PREG_SET_ORDER );

			foreach ( $footnotes as $footnote ) {

				$tmp = wp_kses(
					$footnote[3], [
						'b' => [],
						'em' => [],
						'i' => [],
						'strong' => [],
					]
				);
				$tmp = \Pressbooks\Sanitize\remove_control_characters( $tmp );
				$tmp = trim( preg_replace( '/\s+/', ' ', $tmp ) ); // Normalize white spaces

				$find[] = str_replace( '__REPLACE_ME__', preg_quote( $footnote[2] ), $replacers[ $i ] );
				$replace[] = '[footnote]' . $tmp . '[/footnote]';
			}

			// Remove originals when done
			$find[] = $pattern;
			$replace[] = '&hellip;</div>';
		}

		// Twerk it
		$html = preg_replace( $find, $replace, $html );

		// Important, complex regular expressions have been known to, literally, crash PHP.
		// When testing, make sure this function exits as expected.

		// Send back JSON
		header( 'Content-Type: application/json' );
		$json = wp_json_encode(
			[
				'content' => $html,
			]
		);
		echo $json;

		wp_die();
	}
729723
function plugin(options){
  options = options || {};
  var from = options.from || 'markdown';
  var to   = options.to   || 'html5';
  var args = options.args || [];
  var opts = options.opts || {};
  var pattern = options.pattern || '**/*.md';
  var extension = options.ext || '.html';

  if (to === 'docx' || to === 'pdf') {
    opts.encoding = 'binary'
  }

  return function(files, metalsmith, done){
    selectedFiles = match(Object.keys(files), pattern)
    async.eachLimit(selectedFiles, 100, function(file, cb){

      var data = files[file];
      var dir = dirname(file);
      var html = basename(file, extname(file)) + extension;
      if ('.' != dir) html = dir + '/' + html;

      debug('Converting file %s', file);
      var md = data.contents.toString();

      pandoc = pdc.stream(from, to, args, opts);

      var result = new Buffer(0);
      var chunks = [];
      var size = 0;
      var error = '';

      // listen on error
      pandoc.on('error', function (err) {
        debug('error: ', err);
        return cb(err);
      });

      // collect result data
      pandoc.stdout.on('data', function (data) {
        chunks.push(data);
        size += data.length;
      });

      // collect error data
      pandoc.stderr.on('data', function (data) {
        error += data;
      });

      // listen on exit
      pandoc.on('close', function (code) {
        var msg = '';
        if (code !== 0)
          msg += 'pandoc exited with code ' + code + (error ? ': ' : '.');
        if (error)
          msg += error;

        if (msg)
          return cb(new Error(msg));

        var result = Buffer.concat(chunks, size);

        data.contents = result;
        delete files[file];
        files[html] = data;
        cb(null, result);
      });

      // finally, send source string
      pandoc.stdin.end(md, 'utf8');

    }, done);
  };
}
806105
def convert(self):
        """"""
        lines = []

        pages = self.flatten_pages(self.config['pages'])

        f_exclude = mkdocs_pandoc.filters.exclude.ExcludeFilter(
                exclude=self.exclude)

        f_include = mkdocs_pandoc.filters.include.IncludeFilter(
                base_path=self.config['docs_dir'],
                encoding=self.encoding)

        # First, do the processing that must be done on a per-file basis:
        # Adjust header levels, insert chapter headings and adjust image paths.

        f_headlevel = mkdocs_pandoc.filters.headlevels.HeadlevelFilter(pages)

        for page in pages:
            fname = os.path.join(self.config['docs_dir'], page['file'])
            try:
                p = codecs.open(fname, 'r', self.encoding)
            except IOError as e:
                raise FatalError("Couldn't open %s for reading: %s" % (fname,
                    e.strerror), 1)
            f_chapterhead = mkdocs_pandoc.filters.chapterhead.ChapterheadFilter(
                    headlevel=page['level'],
                    title=page['title']
                    )

            f_image = mkdocs_pandoc.filters.images.ImageFilter(
                    filename=page['file'],
                    image_path=self.config['site_dir'],
                    image_ext=self.image_ext)

            lines_tmp = []

            for line in p.readlines():
                lines_tmp.append(line.rstrip())

            if self.exclude:
                lines_tmp = f_exclude.run(lines_tmp)

            if self.filter_include:
                lines_tmp = f_include.run(lines_tmp)

            lines_tmp = f_headlevel.run(lines_tmp)
            lines_tmp = f_chapterhead.run(lines_tmp)
            lines_tmp = f_image.run(lines_tmp)
            lines.extend(lines_tmp)
            # Add an empty line between pages to prevent text from a previous
            # file from butting up against headers in a subsequent file.
            lines.append('')

        # Strip anchor tags
        if self.strip_anchors:
            lines = mkdocs_pandoc.filters.anchors.AnchorFilter().run(lines)

        # Fix cross references
        if self.filter_xrefs:
            lines = mkdocs_pandoc.filters.xref.XrefFilter().run(lines)

        if self.filter_toc:
            lines = mkdocs_pandoc.filters.toc.TocFilter().run(lines)

        if self.filter_tables:
            lines = mkdocs_pandoc.filters.tables.TableFilter().run(lines)

        return(lines)
512987
public function footnotes()
    {
        foreach ($this->printedfootnotes as $element) {
            if (($this->GetY() + $element->getFootnoteHeight($this)) > $this->getPageHeight()) {
                $this->AddPage();
            }

            $element->renderFootnote($this);

            if ($this->GetY() > $this->getPageHeight()) {
                $this->AddPage();
            }
        }
    }
817503
function captureTomDoc(comments) {
  // collect lines up to first paragraph break
  const lines = []
  for (let i = 0; i < comments.length; i++) {
    const comment = comments[i]
    if (comment.value.match(/^\s*$/)) break
    lines.push(comment.value.trim())
  }

  // return doctrine-like object
  const statusMatch = lines.join(' ').match(/^(Public|Internal|Deprecated):\s*(.+)/)
  if (statusMatch) {
    return {
      description: statusMatch[2],
      tags: [{
        title: statusMatch[1].toLowerCase(),
        description: statusMatch[2],
      }],
    }
  }
}
675057
def cmd_wp(self, args):
        ''''''
        usage = "usage: wp <editor|list|load|update|save|set|clear|loop|remove|move|movemulti|changealt>"
        if len(args) < 1:
            print(usage)
            return

        if args[0] == "load":
            if len(args) != 2:
                print("usage: wp load <filename>")
                return
            self.load_waypoints(args[1])
        elif args[0] == "update":
            if len(args) < 2:
                print("usage: wp update <filename> <wpnum>")
                return
            if len(args) == 3:
                wpnum = int(args[2])
            else:
                wpnum = -1
            self.update_waypoints(args[1], wpnum)
        elif args[0] == "list":
            self.wp_op = "list"
            self.master.waypoint_request_list_send()
        elif args[0] == "save":
            if len(args) != 2:
                print("usage: wp save <filename>")
                return
            self.wp_save_filename = args[1]
            self.wp_op = "save"
            self.master.waypoint_request_list_send()
        elif args[0] == "savecsv":
            if len(args) != 2:
                print("usage: wp savecsv <filename.csv>")
                return
            self.savecsv(args[1])
        elif args[0] == "savelocal":
            if len(args) != 2:
                print("usage: wp savelocal <filename>")
                return
            self.wploader.save(args[1])
        elif args[0] == "show":
            if len(args) != 2:
                print("usage: wp show <filename>")
                return
            self.wploader.load(args[1])
        elif args[0] == "move":
            self.cmd_wp_move(args[1:])
        elif args[0] == "movemulti":
            self.cmd_wp_movemulti(args[1:], None)
        elif args[0] == "changealt":
            self.cmd_wp_changealt(args[1:])
        elif args[0] == "param":
            self.cmd_wp_param(args[1:])
        elif args[0] == "remove":
            self.cmd_wp_remove(args[1:])
        elif args[0] == "undo":
            self.cmd_wp_undo()
        elif args[0] == "set":
            if len(args) != 2:
                print("usage: wp set <wpindex>")
                return
            self.master.waypoint_set_current_send(int(args[1]))
        elif args[0] == "clear":
            self.master.waypoint_clear_all_send()
            self.wploader.clear()
        elif args[0] == "editor":
            if self.module('misseditor'):
                self.mpstate.functions.process_stdin("module reload misseditor", immediate=True)
            else:
                self.mpstate.functions.process_stdin("module load misseditor", immediate=True)
        elif args[0] == "draw":
            if not 'draw_lines' in self.mpstate.map_functions:
                print("No map drawing available")
                return
            if self.get_home() is None:
                print("Need home location - please run gethome")
                return
            if len(args) > 1:
                self.settings.wpalt = int(args[1])
            self.mpstate.map_functions['draw_lines'](self.wp_draw_callback)
            print("Drawing waypoints on map at altitude %d" % self.settings.wpalt)
        elif args[0] == "sethome":
            self.set_home_location()
        elif args[0] == "loop":
            self.wp_loop()
        elif args[0] == "noflyadd":
            self.nofly_add()
        elif args[0] == "status":
            self.wp_status()
        elif args[0] == "slope":
            self.wp_slope()
        else:
            print(usage)
799313
private function footnoteEndHandler(): void
    {
        if ($this->process_footnote) {
            $this->print_data = array_pop($this->print_data_stack);
            $temp             = trim($this->current_element->getValue());
            if (strlen($temp) > 3) {
                $this->wt_report->addElement($this->current_element);
            }
            $this->current_element = $this->footnote_element;
        } else {
            $this->process_footnote = true;
        }
    }
1263803
function column_status( $item ) {

		$add_url = sprintf( get_admin_url() . 'index.php?page=%s&action=%s&ID=%s', $_REQUEST['page'], 'add', $item['ID'] );
		$add_url = esc_url( add_query_arg( '_wpnonce', wp_create_nonce( $item['ID'] ), $add_url ) );
		$add_url = static::addSearchParamsToUrl( $add_url );

		$remove_url = sprintf( get_admin_url() . 'index.php?page=%s&action=%s&ID=%s', $_REQUEST['page'], 'remove', $item['ID'] );
		$remove_url = esc_url( add_query_arg( '_wpnonce', wp_create_nonce( $item['ID'] ), $remove_url ) );
		$remove_url = static::addSearchParamsToUrl( $remove_url );

		// TODO, Better HTML?
		if ( $item['status'] ) {
			$status = '<span data-icon="b" class="yes-icon"></span><span class="assistive-text">Yes</span>';
			$actions = [
				'remove' => sprintf( '<a href="%s">%s</a>', $remove_url, __( 'Hide in Catalog', 'pressbooks' ) ),
			];
		} else {
			$status = '<span data-icon="c" class="no-icon"></span><span class="assistive-text">No</span>';
			$actions = [
				'add' => sprintf( '<a href="%s">%s</a>', $add_url, __( 'Show in Catalog', 'pressbooks' ) ),
			];
		}

		// Return the title contents
		return sprintf(
			'<span class="status">%1$s</span> %2$s',
			$status,
			$this->row_actions( $actions )
		);
	}
1316559
function popFootnoteTokens(tokens) {
  const from = tokens.findIndex(token => token.type === 'footnote_block_open');
  const to = tokens.findIndex(token => token.type === 'footnote_block_close');
  if (from === -1 || to === -1) {
    return [];
  }

  return tokens.splice(from, (to - from) + 1);
}
33023
function prepareDOM() {
        var i, pageNum,
            zoomLevel, maxZoom,
            ptWidth, ptHeight,
            pxWidth, pxHeight,
            pt2px = util.calculatePtSize(),
            dimensions = config.metadata.dimensions,
            skeleton = '';

        // adjust page scale if the pages are too small/big
        // it's adjusted so 100% == DOCUMENT_100_PERCENT_WIDTH px;
        config.pageScale = DOCUMENT_100_PERCENT_WIDTH / (dimensions.width * pt2px);

        // add zoom levels to accomodate the scale
        zoomLevel = config.zoomLevels[config.zoomLevels.length - 1];
        maxZoom = 3 / config.pageScale;
        while (zoomLevel < maxZoom) {
            zoomLevel += zoomLevel / 2;
            config.zoomLevels.push(zoomLevel);
        }

        dimensions.exceptions = dimensions.exceptions || {};

        // create skeleton
        for (i = config.pageStart - 1; i < config.pageEnd; i++) {
            pageNum = i + 1;
            if (pageNum in dimensions.exceptions) {
                ptWidth = dimensions.exceptions[pageNum].width;
                ptHeight = dimensions.exceptions[pageNum].height;
            } else {
                ptWidth = dimensions.width;
                ptHeight = dimensions.height;
            }
            pxWidth = ptWidth * pt2px;
            pxHeight = ptHeight * pt2px;
            pxWidth *= config.pageScale;
            pxHeight *= config.pageScale;
            skeleton += util.template(Crocodoc.pageTemplate, {
                w: pxWidth,
                h: pxHeight
            });
        }

        // insert skeleton and keep a reference to the jq object
        config.$pages = $(skeleton).appendTo(config.$doc);
    }
687764
public function getPluginsWithPressbooksInDescription() {
		$plugins = get_plugins();
		$matches = [];
		foreach ( $plugins as $file => $plugin ) {
			if ( $plugin['Name'] === 'Pressbooks' ) {
				continue; // Fix Pluginception...
			}
			if ( stristr( $plugin['Name'], 'pressbooks' ) || stristr( $plugin['Description'], 'pressbooks' ) || stristr( $file, 'pressbooks' ) ) {
				$matches[ $file ] = $plugin;
			}
		}
		return $matches;
	}
651085
function footnote_ref(state, silent) {
    var label,
        pos,
        footnoteId,
        footnoteSubId,
        token,
        max = state.posMax,
        start = state.pos;

    // should be at least 4 chars - "[^x]"
    if (start + 3 > max) { return false; }

    if (!state.env.footnotes || !state.env.footnotes.refs) { return false; }
    if (state.src.charCodeAt(start) !== 0x5B/* [ */) { return false; }
    if (state.src.charCodeAt(start + 1) !== 0x5E/* ^ */) { return false; }

    for (pos = start + 2; pos < max; pos++) {
      if (state.src.charCodeAt(pos) === 0x20) { return false; }
      if (state.src.charCodeAt(pos) === 0x0A) { return false; }
      if (state.src.charCodeAt(pos) === 0x5D /* ] */) {
        break;
      }
    }

    if (pos === start + 2) { return false; } // no empty footnote labels
    if (pos >= max) { return false; }
    pos++;

    label = state.src.slice(start + 2, pos - 1);
    if (typeof state.env.footnotes.refs[':' + label] === 'undefined') { return false; }

    if (!silent) {
      if (!state.env.footnotes.list) { state.env.footnotes.list = []; }

      if (state.env.footnotes.refs[':' + label] < 0) {
        footnoteId = state.env.footnotes.list.length;
        state.env.footnotes.list[footnoteId] = { label: label, count: 0 };
        state.env.footnotes.refs[':' + label] = footnoteId;
      } else {
        footnoteId = state.env.footnotes.refs[':' + label];
      }

      footnoteSubId = state.env.footnotes.list[footnoteId].count;
      state.env.footnotes.list[footnoteId].count++;

      token      = state.push('footnote_ref', '', 0);
      token.meta = { id: footnoteId, subId: footnoteSubId, label: label };
    }

    state.pos = pos;
    state.posMax = max;
    return true;
  }
19283
def build_pubnote(title, volume, page_start=None, page_end=None, artid=None):
    """"""
    if title and volume and artid and artid != page_start:
        pubnote_format = u'{title},{volume},{artid}'
    elif title and volume and page_start and page_end:
        pubnote_format = u'{title},{volume},{page_start}-{page_end}'
    elif title and volume and page_start:
        pubnote_format = u'{title},{volume},{page_start}'
    elif title and volume:
        pubnote_format = u'{title},{volume}'
    else:
        return None

    return pubnote_format.format(
        title=title, volume=volume, page_start=page_start, page_end=page_end, artid=artid
    )
658415
protected function bookInfoPid() {

		$post = ( new Metadata() )->getMetaPost();
		if ( empty( $post->ID ) ) {
			$new_post = [
				'post_title' => __( 'Book Info', 'pressbooks' ),
				'post_type' => 'metadata',
				'post_status' => 'publish',
			];
			$pid = wp_insert_post( add_magic_quotes( $new_post ) );
		} else {
			$pid = $post->ID;
		}

		return $pid;
	}
728126
public function makeThumbnailsForBookCover() {

		$post = $this->getMetaPost();
		if ( $post ) {

			$pb_cover_image = get_post_meta( $post->ID, 'pb_cover_image', true );
			if ( $pb_cover_image && ! \Pressbooks\Image\is_default_cover( $pb_cover_image ) ) {

				$path = \Pressbooks\Utility\get_media_path( $pb_cover_image );
				$type = wp_check_filetype( $path );
				$type = $type['type'];

				// Insert new image, create thumbnails
				$args = [
					'post_mime_type' => $type,
					'post_title' => __( 'Cover Image', 'pressbooks' ),
					'post_content' => '',
					'post_status' => 'inherit',
				];

				include_once( ABSPATH . 'wp-admin/includes/image.php' );
				$id = wp_insert_attachment( $args, $path, $post->ID );
				wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $path ) );
				Book::deleteBookObjectCache();
			}
		}
	}
328695
public function customize_controls_print_footer_scripts() {
		wp_enqueue_script(
			'kirki-set-setting-value',
			URL::get_from_path( __DIR__ . '/assets/scripts/set-setting-value.js' ),
			[ 'jquery' ],
			KIRKI_VERSION,
			false
		);

		wp_enqueue_script(
			'kirki-preset',
			URL::get_from_path( __DIR__ . '/assets/scripts/preset.js' ),
			[ 'jquery', 'kirki-set-setting-value' ],
			KIRKI_VERSION,
			false
		);
	}
72905
function convert() {

		// Get most recent Epub file

		$input_folder = static::getExportFolder();
		$input_path = false;
		$files = \Pressbooks\Utility\scandir_by_date( $input_folder );
		foreach ( $files as $file ) {
			if ( preg_match( '/\.epub/i', $file ) ) {
				$input_path = $input_folder . $file;
				break;
			}
		}

		if ( ! $input_path ) {
			$this->logError( "Could not convert to MOBI because no EPUB file was found in $input_folder" );

			return false;
		}

		// Convert

		$filename = $this->timestampedFileName( '.mobi' );
		$this->outputPath = $filename;

		$command = PB_KINDLEGEN_COMMAND . ' ' . escapeshellcmd( $input_path ) . ' -locale en -o ' . escapeshellcmd( basename( $this->outputPath ) ) . ' 2>&1';

		$output = [];
		$return_var = 0;
		exec( $command, $output, $return_var );

		// Check build results

		$last_line = array_filter( $output );
		$last_line = strtolower( end( $last_line ) );
		if ( false !== strpos( $last_line, 'mobi file built successfully' ) ) {

			// Ok!
			return true;

		} elseif ( false !== strpos( $last_line, 'mobi file built with warnings' ) ) {

			// Built, but has warnings
			$this->hasWarnings = true;
			$this->logError( implode( "\n", $output ) );

			return true;

		} else {

			// Error
			$this->logError( implode( "\n", $output ) );

			return false;
		}

	}