text:
#include "id_manager.hpp"
#include "utils.hpp"
#include "string_ref.hpp"
#include <boost/make_shared.hpp>
#include <boost/unordered_map.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/foreach.hpp>
#include <deque>
#include <vector>
#include <cctype>

namespace quickbook
{
    // TODO: This should possibly try to make ids are generated:
    // http://www.w3.org/TR/REC-xml/#NT-NameStartChar

    //
    // Forward declarations
    //

    struct id_placeholder;
    struct id_data;
    std::string replace_ids(id_state& state, std::string const& xml,
            bool use_resolved_ids = true);
    std::string process_ids(id_state&, std::string const&);

    static const std::size_t max_size = 32;

    //
    // id_placeholder
    //

    struct id_placeholder
    {
        enum state_enum { child, unresolved, resolved, generated };

        unsigned index;         // The poisition in the placeholder deque.
        state_enum generation_state;
                                // Placeholder's position in generation
                                // process.
        std::string unresolved_id;
                                // The id that would be generated without any
                                // duplicate handling.
        std::string id;         // The id so far.
        id_placeholder* parent; // Placeholder of the parent id.
                                // Only when generation_state == child
        id_category category;
        unsigned num_dots;      // Number of dots in the id.
                                // Normally equal to the section level.
        unsigned order;         // Order of the placeholders in the generated
                                // xml. Stored because it can be slightly
                                // different to the order they're generated
                                // in. e.g. for nested tables the cells
                                // are processed before the title id.
                                // Only set when processing ids.
        id_data* data;          // Assigned data shared by duplicate ids
                                // used to detect them. Only when
                                // generation_state == resolved

        id_placeholder(
                unsigned index,
                std::string const& id,
                id_category category,
                id_placeholder* parent_ = 0)
          : index(index),
            generation_state(parent_ ? child : unresolved),
            unresolved_id(parent_ ? parent_->unresolved_id + '.' + id : id),
            id(id),
            parent(parent_),
            category(category),
            num_dots(boost::range::count(id, '.') +
                (parent_ ? parent_->num_dots + 1 : 0)),
            order(0),
            data(0)
        {
        }

        std::string to_string()
        {
            return '$' + boost::lexical_cast<std::string>(index);
        }

        bool check_state() const
        {
            return (
                (generation_state == child) == (bool) parent &&
                (generation_state == resolved) == (bool) data);
        }

        bool check_state(state_enum s) const
        {
            return s == generation_state && check_state();
        }
    };

    //
    // id_state
    //

    struct file_info;
    struct doc_info;
    struct section_info;

    struct id_state
    {
        boost::shared_ptr<file_info> current_file;
        std::deque<id_placeholder> placeholders;

        // Placeholder methods

        id_placeholder* add_placeholder(
                std::string const&, id_category, id_placeholder* parent = 0);

        id_placeholder* get_placeholder(string_ref);

        // Events

        id_placeholder* start_file(
                unsigned compatibility_version,
                bool document_root,
                std::string const& include_doc_id,
                std::string const& id,
                value const& title);

        void end_file();

        id_placeholder* add_id(
                std::string const& id,
                id_category category);
        id_placeholder* old_style_id(
            std::string const& id,
            id_category category);
        id_placeholder* begin_section(
                std::string const& id,
                id_category category);
        void end_section();

private:
        id_placeholder* add_id_to_section(
                std::string const& id,
                id_category category,
                boost::shared_ptr<section_info> const& section);
        id_placeholder* create_new_section(
                std::string const& id,
                id_category category);

        void switch_section(id_placeholder*);
        void reswitch_sections(boost::shared_ptr<section_info> const&,
            boost::shared_ptr<section_info> const&);
        void restore_section();
    };

    struct file_info
    {
        boost::shared_ptr<file_info> parent;
        boost::shared_ptr<doc_info> document;

        bool document_root; // !parent || document != parent->document
        unsigned compatibility_version;
        boost::shared_ptr<section_info> switched_section;
        id_placeholder* original_placeholder;

        // The 1.1-1.5 document id would actually change per file due to
        // explicit ids in includes and a bug which would sometimes use the
        // document title instead of the id.
        std::string doc_id_1_1;

        file_info(boost::shared_ptr<file_info> const& parent,
                unsigned compatibility_version) :
            parent(parent), document(parent->document), document_root(false),
            compatibility_version(compatibility_version),
            switched_section(), original_placeholder()
        {}

        file_info(boost::shared_ptr<file_info> const& parent,
                boost::shared_ptr<doc_info> const& document,
                unsigned compatibility_version) :
            parent(parent), document(document), document_root(true),
            compatibility_version(compatibility_version),
            switched_section(), original_placeholder()
        {}
    };

    struct doc_info
    {
        boost::shared_ptr<section_info> current_section;
        std::string last_title_1_1;
        std::string section_id_1_1;

        doc_info() :
            current_section(), last_title_1_1(), section_id_1_1()
        {}
    };

    struct section_info
    {
        boost::shared_ptr<section_info> parent;
        unsigned compatibility_version;
        unsigned level;
        std::string id_1_1;
        id_placeholder* placeholder_1_6;

        section_info(boost::shared_ptr<section_info> const& parent,
                unsigned compatibility_version, std::string const& id) :
            parent(parent), compatibility_version(compatibility_version),
            level(parent ? parent->level + 1 : 1),
            id_1_1(), placeholder_1_6(0)
        {
            if (parent && compatibility_version < 106u) {
                id_1_1 = parent->id_1_1;
                if (!id_1_1.empty() && !id.empty())
                    id_1_1 += ".";
                id_1_1 += id;
            }
        }
    };

    //
    // id_manager
    //

    id_manager::id_manager()
      : state(new id_state)
    {
    }

    id_manager::~id_manager() {}

    void id_manager::start_file(
            unsigned compatibility_version,
            std::string const& include_doc_id,
            std::string const& id,
            value const& title)
    {
        state->start_file(compatibility_version, false, include_doc_id, id, title);
    }

    std::string id_manager::start_file_with_docinfo(
            unsigned compatibility_version,
            std::string const& include_doc_id,
            std::string const& id,
            value const& title)
    {
        return state->start_file(compatibility_version, true, include_doc_id,
            id, title)->to_string();
    }

    void id_manager::end_file()
    {
        state->end_file();
    }

    std::string id_manager::begin_section(std::string const& id,
            id_category category)
    {
        return state->begin_section(id, category)->to_string();
    }

    void id_manager::end_section()
    {
        return state->end_section();
    }

    int id_manager::section_level() const
    {
        return state->current_file->document->current_section->level;
    }

    std::string id_manager::old_style_id(std::string const& id, id_category category)
    {
        return state->old_style_id(id, category)->to_string();
    }

    std::string id_manager::add_id(std::string const& id, id_category category)
    {
        return state->add_id(id, category)->to_string();
    }

    std::string id_manager::add_anchor(std::string const& id, id_category category)
    {
        return state->add_placeholder(id, category)->to_string();
    }

    std::string id_manager::replace_placeholders_with_unresolved_ids(
            std::string const& xml) const
    {
        return replace_ids(*state, xml, false);
    }

    std::string id_manager::replace_placeholders(std::string const& xml) const
    {
        assert(!state->current_file);
        return process_ids(*state, xml);
    }

    unsigned id_manager::compatibility_version() const
    {
        return state->current_file->compatibility_version;
    }

    //
    // normalize_id
    //
    // Normalizes generated ids.
    //

    namespace
    {
        std::string normalize_id(
                std::string src_id,
                std::size_t prefix = 0,
                std::size_t size = max_size)
        {
            std::string id;
            id.swap(src_id);

            std::size_t src = prefix;
            std::size_t dst = prefix;
            size += prefix;

            if (src >= id.length()) {
                return id;
            }

            while (src < id.length() && id[src] == '_') {
                ++src;
            }

            if (src >= id.length()) {
                id += '_';
                return id;
            }

            while (src < id.length() && dst < size) {
                if (id[src] == '_') {
                    do {
                        ++src;
                    } while(src < id.length() && id[src] == '_');

                    if (src < id.length()) id[dst++] = '_';
                }
                else {
                    id[dst++] = id[src++];
                }
            }

            id.erase(dst);

            return id;
        }
    }

    //
    // id_state
    //

    id_placeholder* id_state::add_placeholder(
            std::string const& id, id_category category,
            id_placeholder* parent)
    {
        placeholders.push_back(id_placeholder(
            placeholders.size(), id, category, parent));
        return &placeholders.back();
    }

    id_placeholder* id_state::get_placeholder(string_ref value)
    {
        // If this isn't a placeholder id.
        if (value.size() <= 1 || *value.begin() != '$')
            return 0;

        unsigned index = boost::lexical_cast<int>(std::string(
                value.begin() + 1, value.end()));

        return &placeholders.at(index);
    }

    void id_state::switch_section(id_placeholder* p)
    {
        assert(!current_file->original_placeholder);
        current_file->switched_section = current_file->document->current_section;
        current_file->original_placeholder = current_file->switched_section->placeholder_1_6;
        current_file->switched_section->placeholder_1_6 = p;
    }

    void id_state::reswitch_sections(
        boost::shared_ptr<section_info> const& popped_section,
        boost::shared_ptr<section_info> const& parent_section)
    {
        boost::shared_ptr<file_info> file = current_file;
        boost::shared_ptr<file_info> first_switched_file;

        for (;;) {
            if (file->switched_section == popped_section)
            {
                first_switched_file = file;
                file->switched_section = parent_section;
            }

            if (file->document_root) break;
            file = file->parent;
        }

        if (first_switched_file) {
            first_switched_file->original_placeholder =
                parent_section->placeholder_1_6;
            parent_section->placeholder_1_6 =
                popped_section->placeholder_1_6;
        }
    }

    void id_state::restore_section()
    {
        if (current_file->original_placeholder) {
            current_file->switched_section->placeholder_1_6 =
                current_file->original_placeholder;
        }
    }

    id_placeholder* id_state::start_file(
            unsigned compatibility_version,
            bool document_root,
            std::string const& include_doc_id,
            std::string const& id,
            value const& title)
    {
        // Create new file

        boost::shared_ptr<file_info> parent = current_file;

        if (document_root) {
            current_file = boost::make_shared<file_info>(parent,
                    boost::make_shared<doc_info>(),
                    compatibility_version);
        }
        else {
            current_file =
                boost::make_shared<file_info>(parent, compatibility_version);
        }

        // Choose specified id to use. Prefer 'include_doc_id' (the id
        // specified in an 'include' element) unless backwards compatibility
        // is required.

        std::string initial_doc_id;

        if (document_root ||
            compatibility_version >= 106u ||
            (parent && parent->compatibility_version >= 106u)) {
            initial_doc_id = !include_doc_id.empty() ? include_doc_id : id;
        }
        else {
            initial_doc_id = !id.empty() ? id : include_doc_id;
        }

        // Set variables used for backwards compatible id generation.
        // They're a bit odd because of old bugs.

        if (document_root || compatibility_version < 106u) {
            // Note: this is done for older versions even if docinfo is
            // otherwise ignored.

            if (title.check())
                current_file->document->last_title_1_1 =
                    title.get_quickbook();

            current_file->doc_id_1_1 = !initial_doc_id.empty() ? initial_doc_id :
                detail::make_identifier(current_file->document->last_title_1_1);
        }
        else if (parent) {
            current_file->doc_id_1_1 = parent->doc_id_1_1;
        }

        if (document_root) {
            if (!initial_doc_id.empty()) {
                return create_new_section(id, id_category::explicit_section_id);
            }
            else if (!title.empty()) {
                return create_new_section(
                    detail::make_identifier(title.get_quickbook()),
                    id_category::generated_doc);
            }
            else if (compatibility_version >= 106u) {
                return create_new_section("doc", id_category::numbered);
            }
            else {
                return create_new_section("", id_category::generated_doc);
            }
        }
        else {
            // If an id was set for the file, then switch the current section
            // with a new section with this id. This will be maintained in
            // 'end_section' if the current section ends, and then the original
            // section restored in 'end_file'

            if (compatibility_version >= 106u && !initial_doc_id.empty()) {
                switch_section(add_id_to_section(initial_doc_id,
                    id_category::explicit_section_id,
                    boost::shared_ptr<section_info>()));
            }

            return 0;
        }
    }

    void id_state::end_file()
    {
        restore_section();
        current_file = current_file->parent;
    }

    id_placeholder* id_state::add_id(
            std::string const& id,
            id_category category)
    {
        return add_id_to_section(id, category,
            current_file->document->current_section);
    }

    id_placeholder* id_state::add_id_to_section(
            std::string const& id,
            id_category category,
            boost::shared_ptr<section_info> const& section)
    {
        std::string id_part = id;

        // Note: Normalizing id according to file compatibility version, but
        // adding to section according to section compatibility version.

        if (current_file->compatibility_version >= 106u &&
                category.c < id_category::explicit_id) {
            id_part = normalize_id(id);
        }

        if(!section || section->compatibility_version >= 106u) {
            return add_placeholder(id_part, category,
                section ? section->placeholder_1_6 : 0);
        }
        else {
            std::string const& qualified_id = section->id_1_1;

            std::string new_id;
            if (!section->placeholder_1_6)
                new_id = current_file->doc_id_1_1;
            if (!new_id.empty() && !qualified_id.empty()) new_id += '.';
            new_id += qualified_id;
            if (!new_id.empty() && !id_part.empty()) new_id += '.';
            new_id += id_part;

            return add_placeholder(new_id, category,
                section->placeholder_1_6);
        }
    }

    id_placeholder* id_state::old_style_id(
        std::string const& id,
        id_category category)
    {
        return current_file->compatibility_version < 103u ?
            add_placeholder(
                current_file->document->section_id_1_1 + "." + id, category) :
                add_id(id, category);
    }

    id_placeholder* id_state::begin_section(
            std::string const& id,
            id_category category)
    {
        current_file->document->section_id_1_1 = id;
        return create_new_section(id, category);
    }

    id_placeholder* id_state::create_new_section(
            std::string const& id,
            id_category category)
    {
        boost::shared_ptr<section_info> parent =
            current_file->document->current_section;

        boost::shared_ptr<section_info> new_section =
            boost::make_shared<section_info>(parent,
                current_file->compatibility_version, id);

        id_placeholder* p;

        if (new_section->compatibility_version >= 106u) {
            p = add_id_to_section(id, category, parent);
            new_section->placeholder_1_6 = p;
        }
        else if (new_section->compatibility_version >= 103u) {
            if (parent)
                new_section->placeholder_1_6 = parent->placeholder_1_6;

            std::string new_id;
            if (!new_section->placeholder_1_6) {
                new_id = current_file->doc_id_1_1;
                if (!new_section->id_1_1.empty()) new_id += '.';
            }
            new_id += new_section->id_1_1;

            p = add_placeholder(new_id, category,
                new_section->placeholder_1_6);
        }
        else {
            if (parent)
                new_section->placeholder_1_6 = parent->placeholder_1_6;

            std::string new_id;
            if (parent && !new_section->placeholder_1_6)
                new_id = current_file->doc_id_1_1 + '.';

            new_id += id;

            p = add_placeholder(new_id, category,
                new_section->placeholder_1_6);
        }

        current_file->document->current_section = new_section;

        return p;
    }

    void id_state::end_section()
    {
        boost::shared_ptr<section_info> popped_section =
            current_file->document->current_section;
        current_file->document->current_section = popped_section->parent;

        reswitch_sections(popped_section, popped_section->parent);
    }

    //
    // Xml subset parser used for finding id values.
    //
    // I originally tried to integrate this into the post processor
    // but that proved tricky. Alternatively it could use a proper
    // xml parser, but I want this to be able to survive badly
    // marked up escapes.
    //

    struct xml_processor
    {
        xml_processor();

        std::vector<std::string> id_attributes;

        struct callback {
            virtual void start(string_ref) {}
            virtual void id_value(string_ref) {}
            virtual void finish(string_ref) {}
            virtual ~callback() {}
        };

        void parse(std::string const&, callback&);
    };

    namespace
    {
        char const* id_attributes_[] =
        {
            "id",
            "linkend",
            "linkends",
            "arearefs"
        };
    }

    xml_processor::xml_processor()
    {
        static int const n_id_attributes = sizeof(id_attributes_)/sizeof(char const*);
        for (int i = 0; i != n_id_attributes; ++i)
        {
            id_attributes.push_back(id_attributes_[i]);
        }

        boost::sort(id_attributes);
    }

    template <typename Iterator>
    bool read(Iterator& it, Iterator end, char const* text)
    {
        for(Iterator it2 = it;; ++it2, ++text) {
            if (!*text) {
                it = it2;
                return true;
            }

            if (it2 == end || *it2 != *text)
                return false;
        }
    }

    template <typename Iterator>
    void read_past(Iterator& it, Iterator end, char const* text)
    {
        while (it != end && !read(it, end, text)) ++it;
    }

    bool find_char(char const* text, char c)
    {
        for(;*text; ++text)
            if (c == *text) return true;
        return false;
    }

    template <typename Iterator>
    void read_some_of(Iterator& it, Iterator end, char const* text)
    {
        while(it != end && find_char(text, *it)) ++it;
    }

    template <typename Iterator>
    void read_to_one_of(Iterator& it, Iterator end, char const* text)
    {
        while(it != end && !find_char(text, *it)) ++it;
    }

    void xml_processor::parse(std::string const& source, callback& c)
    {
        typedef std::string::const_iterator iterator;

        string_ref source_ref(source.begin(), source.end());
        c.start(source_ref);

        iterator it = source_ref.begin(), end = source_ref.end();

        for(;;)
        {
            read_past(it, end, "<");
            if (it == end) break;

            if (read(it, end, "!--quickbook-escape-prefix-->"))
            {
                read_past(it, end, "<!--quickbook-escape-postfix-->");
                continue;
            }

            switch(*it)
            {
            case '?':
                ++it;
                read_past(it, end, "?>");
                break;

            case '!':
                if (read(it, end, "!--"))
                    read_past(it, end, "-->");
                else
                    read_past(it, end, ">");
                break;

            default:
                if ((*it >= 'a' && *it <= 'z') ||
                        (*it >= 'A' && *it <= 'Z') ||
                        *it == '_' || *it == ':')
                {
                    read_to_one_of(it, end, " \t\n\r>");

                    for (;;) {
                        read_some_of(it, end, " \t\n\r");
                        iterator name_start = it;
                        read_to_one_of(it, end, "= \t\n\r>");
                        if (it == end || *it == '>') break;
                        string_ref name(name_start, it);
                        ++it;

                        read_some_of(it, end, "= \t\n\r");
                        if (it == end || (*it != '"' && *it != '\'')) break;

                        char delim = *it;
                        ++it;

                        iterator value_start = it;

                        it = std::find(it, end, delim);
                        if (it == end) break;
                        string_ref value(value_start, it);
                        ++it;

                        if (boost::find(id_attributes, name)
                                != id_attributes.end())
                        {
                            c.id_value(value);
                        }
                    }
                }
                else
                {
                    read_past(it, end, ">");
                }
            }
        }

        c.finish(source_ref);
    }

    //
    // process_ids
    //

    //
    // Data used for generating placeholders that have duplicates.
    //

    struct id_generation_data
    {
        id_generation_data(std::string const& src_id)
          : child_start(src_id.rfind('.') + 1),
            id(normalize_id(src_id, child_start, max_size - 1)),
                // 'max_size - 1' leaves a character to append
                // a number.
            count(0)
        {
            if (std::isdigit(id[id.length() - 1]))
            {
                if (child_length() < max_size - 1)
                    id += '_';
                else
                    reduce_id();
            }
        }

        void reduce_id()
        {
            assert(id.length() > child_start);
            std::size_t length = id.length() - 1;
            while(length > child_start && std::isdigit(id[length - 1])) --length;
            id.erase(length);
            count = 0;
        }

        std::size_t child_length() const
        {
            return id.length() - child_start;
        }

        std::size_t child_start;
        std::string id;
        int count;
    };

    // Created for all desired ids, either when resolving an id or due to
    // generating a new id to avoid duplicates.
    struct id_data
    {
        id_data()
          : category(id_category::numbered),
            used(false),
            generation_data()
        {}

        void update_category(id_category c)
        {
            if (c.c > category.c) category = c;
        }

        id_category category;   // The highest priority category of the
                                // placeholders that want to use this id.
        bool used;              // Whether this id has been used.
        boost::shared_ptr<id_generation_data> generation_data;
                                // If a duplicates are found, this is
                                // created to generate new ids.
                                //
                                // Many to one relationship, because truncation
                                // can lead to different ids contending for the
                                // same id prefix.
    };

    typedef boost::unordered_map<std::string, id_data> allocated_ids;
    typedef std::vector<id_placeholder*> placeholder_index;

    placeholder_index index_placeholders(id_state&, std::string const& xml);
    void resolve_id(id_placeholder&, allocated_ids&);
    void generate_id(id_placeholder&, allocated_ids&);

    std::string process_ids(id_state& state, std::string const& xml)
    {
        placeholder_index placeholders = index_placeholders(state, xml);

        typedef std::vector<id_placeholder*>::iterator iterator;

        iterator it = placeholders.begin(),
            end = placeholders.end();

        // Placeholder ids are processed in blocks of ids with
        // an equal number of dots.
        while (it != end) {
            unsigned num_dots = (*it)->num_dots;

            // ids can't clash with ids at a different num_dots, so
            // this only needs to track the id generation data
            // for a single num_dots at a time.
            allocated_ids ids;

            iterator it2 = it;
            do {
                resolve_id(**it2++, ids);
            } while(it2 != end && (*it2)->num_dots == num_dots);

            do {
                generate_id(**it++, ids);
            } while(it != it2);
        }

        return replace_ids(state, xml);
    }

    //
    // index_placeholders
    //
    // Create a sorted index of the placeholders, in order
    // to make numbering duplicates easy. A total order.
    //

    struct placeholder_compare
    {
        bool operator()(id_placeholder* x, id_placeholder* y) const
        {
            bool x_explicit = x->category.c >= id_category::explicit_id;
            bool y_explicit = y->category.c >= id_category::explicit_id;

            return
                x->num_dots < y->num_dots ? true :
                x->num_dots > y->num_dots ? false :
                x_explicit > y_explicit ? true :
                x_explicit < y_explicit ? false :
                x->order < y->order;
        }
    };

    struct number_placeholders_callback : xml_processor::callback
    {
        id_state& state;
        unsigned count;

        number_placeholders_callback(id_state& state)
          : state(state),
            count(0)
        {}

        void id_value(string_ref value)
        {
            id_placeholder* p = state.get_placeholder(value);
            number(p);
        }

        void number(id_placeholder* p)
        {
            if (p && !p->order) {
                number(p->parent);
                p->order = ++count;
            }
        }
    };

    placeholder_index index_placeholders(
            id_state& state,
            std::string const& xml)
    {
        xml_processor processor;
        number_placeholders_callback callback(state);
        processor.parse(xml, callback);

        placeholder_index sorted_placeholders;
        sorted_placeholders.reserve(state.placeholders.size());
        BOOST_FOREACH(id_placeholder& p, state.placeholders)
            if (p.order) sorted_placeholders.push_back(&p);
        boost::sort(sorted_placeholders, placeholder_compare());

        return sorted_placeholders;
    }

    //
    // resolve_id
    //
    // Convert child ids to full ids, and add to the
    // allocated ids (although not yet set in stone because
    // there might be duplicates).
    //
    // Note that the parent ids has to be generated before resolving
    // the child id.
    //

    void resolve_id(id_placeholder& p, allocated_ids& ids)
    {
        if (p.generation_state == id_placeholder::child)
        {
            assert(p.check_state());

            assert(p.parent->check_state(id_placeholder::generated));

            p.id = p.parent->id + "." + p.id;
            p.generation_state = id_placeholder::unresolved;
            p.parent = 0;
        }

        assert(p.check_state(id_placeholder::unresolved));

        id_data& data = ids.emplace(p.id, id_data()).first->second;
        data.update_category(p.category);

        p.data = &data;
        p.generation_state = id_placeholder::resolved;
    }

    //
    // generate_id
    //
    // Finally generate the final id.
    //

    void register_generation_data(id_placeholder&, allocated_ids&);

    void generate_id(id_placeholder& p, allocated_ids& ids)
    {
        assert(p.check_state(id_placeholder::resolved));

        // If the placeholder id is available, then update data
        // and return.
        if (p.category == p.data->category && !p.data->used &&
            p.category.c != id_category::numbered)
        {
            p.data->used = true;
            p.generation_state = id_placeholder::generated;
            p.data = 0;
            return;
        }

        if (!p.data->generation_data)
        {
            p.data->generation_data =
                boost::make_shared<id_generation_data>(p.id);
            register_generation_data(p, ids);
        }

        // Loop until an available id is found.
        for(;;)
        {
            id_generation_data& generation_data = *p.data->generation_data;

            std::string postfix =
                boost::lexical_cast<std::string>(generation_data.count++);

            if (generation_data.child_length() + postfix.length() > max_size) {
                // The resulting id is too long, so move to a shorter id.
                generation_data.reduce_id();
                register_generation_data(p, ids);
            }
            else {
                std::string id = generation_data.id + postfix;

                if (ids.find(id) == ids.end()) {
                    p.id.swap(id);
                    p.generation_state = id_placeholder::generated;
                    p.data = 0;
                    return;
                }
            }
        }
    }

    // Every time the generation id is changed, this is called to
    // check if that id is already in use.
    void register_generation_data(id_placeholder& p, allocated_ids& ids)
    {
        std::string const& id = p.data->generation_data->id;

        id_data& new_data = ids.emplace(id, id_data()).first->second;

        // If there is already generation_data for the new id then use that.
        // Otherwise use the placeholder's existing generation_data.
        if (new_data.generation_data)
            p.data->generation_data = new_data.generation_data;
        else
            new_data.generation_data = p.data->generation_data;
    }

    //
    // replace_ids
    //
    // Return a copy of the xml with all the placeholders replaced by
    // generated_ids.
    //

    struct replace_ids_callback : xml_processor::callback
    {
        id_state& state;
        bool use_resolved_ids;
        std::string::const_iterator source_pos;
        std::string result;

        replace_ids_callback(id_state& state, bool resolved)
          : state(state),
            use_resolved_ids(resolved),
            source_pos(),
            result()
        {}

        void start(string_ref xml)
        {
            source_pos = xml.begin();
        }

        void id_value(string_ref value)
        {
            if (id_placeholder* p = state.get_placeholder(value))
            {
                assert(!use_resolved_ids ||
                    p->check_state(id_placeholder::generated));
                std::string const& id = use_resolved_ids ?
                    p->id : p->unresolved_id;

                result.append(source_pos, value.begin());
                result.append(id.begin(), id.end());
                source_pos = value.end();
            }
        }

        void finish(string_ref xml)
        {
            result.append(source_pos, xml.end());
            source_pos = xml.end();
        }
    };

    std::string replace_ids(id_state& state, std::string const& xml,
            bool use_unresolved_ids)
    {
        xml_processor processor;
        replace_ids_callback callback(state, use_unresolved_ids);
        processor.parse(xml, callback);
        return callback.result;
    }
}


text:
<?php

namespace ZendTest\Mail\Transport;

use Zend\Mail\Transport\FileOptions;

/**
 * @category   Zend
 * @package    Zend_Mail
 * @subpackage UnitTests
 * @group      Zend_Mail
 */
class FileOptionsTest extends \PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        $this->options = new FileOptions();
    }

    public function testPathIsSysTempDirByDefault()
    {
        $this->assertEquals(sys_get_temp_dir(), $this->options->getPath());
    }

    public function testDefaultCallbackIsSetByDefault()
    {
        $callback = $this->options->getCallback();
        $this->assertTrue(is_callable($callback));
        $test     = call_user_func($callback, '');
        $this->assertRegExp('#^ZendMail_\d+_\d+\.tmp$#', $test);
    }

    public function testPathIsMutable()
    {
        $original = $this->options->getPath();
        $this->options->setPath(__DIR__);
        $test     = $this->options->getPath();
        $this->assertNotEquals($original, $test);
        $this->assertEquals(__DIR__, $test);
    }

    public function testCallbackIsMutable()
    {
        $original = $this->options->getCallback();
        $new      = function ($transport) {};
        $this->options->setCallback($new);
        $test     = $this->options->getCallback();
        $this->assertNotSame($original, $test);
        $this->assertSame($new, $test);
    }
}

redpajama_set_name: RedPajamaGithub


text:
define(function(require) {

	// Dependencies
	var $ = require('jquery');
    var __ = require('../localize/translated');

	// Add listeners to all listing delete links and all delete
	// links on edit pages
	function register() {
		$('.listing')
		.find('.delete-now:not([disabled]), .remove-now:not([disabled])')
		.add('.form-actions .delete:not([disabled])')
		.on('click', prompt);
	}

	// Prompt user to confirm deletes
	function prompt(e, bypass) {
		var $el = $(this);

		// Don't prevent if "bypass" was in the data.
		if (bypass === true) {
			var href = $el.attr('href');
			if (href && href != '#') return window.location.href = href;
			else return;
		}

		// Prevent default behavior
		e.preventDefault();
		e.stopImmediatePropagation();

		// Hide tooltips
		var title;
		if ($el.hasClass('js-tooltip')) {
			$el.tooltip('destroy');

			// Remove the title so that the popover doesn't have one. I don't
			// like how it looks and this is the only way to clear it.
			title = $el.data('original-title');
			$el.removeAttr('data-original-title');
		}

		// Show the dialog
		$el.popover({
			trigger: 'manual',
			content: '
'+__('chicken_switch.text')+'

 '+__('chicken_switch.confirm')+' '+__('chicken_switch.cancel')+'
',
			html: true,

			// If a butto, it's an edit button, so put the popup on top
			placement: $el.hasClass('btn') ? 'top' : 'left'

		}).popover('show');

		// Listen on clicks
		var data = {
			$target: $el,
			title: title
		};
		$(this).next('.popover').find('.btn-danger').on('click', null, data, destroy);
		$(this).next('.popover').find('.btn-default').on('click', null, data, close);

	}

	// Close the popup and retrigger the click on the target but bypass the prompt
	function destroy(e) {
		close(e);
		e.data.$target.trigger('click', true);
	}

	// Remove the prompt and re-add the tooltip
	function close(e) {
		e.data.$target.popover('hide');
		if (e.data.$target.hasClass('js-tooltip')) {
			e.data.$target.tooltip({
				animation: false,
				title: e.data.title
			});
		}
	}

	// Public API
	return {
		register: register
	};

});


text:
<!DOCTYPE html>

	<title>About the author · lou1306</title>
	<meta charset="utf-8">
	<link href="https://micro.blog/lou1306" rel="me" />
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<meta name="author" content="Luca Di Stefano">
	<meta name="description" content="Notes on CompSci et cetera">

	<meta name="generator" content="Hugo 0.105.0">

	<link rel="stylesheet" href="https://lou1306.github.io/css/main.css">
	<link rel="stylesheet" href="https://lou1306.github.io/css/style.css">

	<link rel="apple-touch-icon" href="https://lou1306.github.io/apple-touch-icon.png">
	<link rel="shortcut icon" href="https://lou1306.github.io/favicon.ico" type="image/x-icon">

	<link rel="canonical" href="https://lou1306.github.io/en/about/">

	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/solid.min.css">
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/brands.min.css">
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/fontawesome.min.css">
	<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jpswalsh/academicons@1/css/academicons.min.css">

	<link href="//fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,700italic,400italic" rel="stylesheet" type="text/css">

	<link href="/syntax.css" rel="stylesheet" type="text/css">

			lou1306

				 (it)

 About •

 CV •

 Schools •

•

About the author

    February 28, 2020 · Just one minute

This page also available in:

        Italian

I am a post-doc researcher at team CONVECS,
Inria Grenoble Rhône-Alpes.

My research focuses on specification and verification of agent-based models.
Software-based systems are becoming pervasive in our everyday life, so you want
them to be correct.
I am trying to develop languages and tools that could help us with that.

Other stuff

In 2020, I obtained a PhD in Computer Science
at Gran Sasso Science Institute (GSSI).

From December 2019 until October 2020, my research was funded by
the SysMA group at IMT Lucca, with
a short-term grant on
“Verification of Emerging Properties in Collective Adaptive Systems”.

I got my Master’s degree in Computer and Systems Engineering
(Ingegneria Informatica e Automatica) at
University of L’Aquila.

I am a Scout since 1998 and I’m a member of the Pattuglia Nazionale Informatica
(National Information Systems Patrol) of my association.

My interests

📚📚📚

📽📺

🎧🎶

👾🕹

Hobby: 🎸🎿💻

- Luca

Please enable JavaScript to view the comments powered by Disqus.
comments powered by Disqus

© 2022 - Luca Di Stefano

Hugo • Type Theme • Skeleton • Academicons


text:
using namespace std;

AbsProgramGenerator *AbsProgramGenerator::current_generator_ = NULL;

OutputMgr *
AbsProgramGenerator::GetOutputMgr()
{
	assert(current_generator_);
	return current_generator_->getOutputMgr();
}

AbsProgramGenerator *
AbsProgramGenerator::CreateInstance(int argc, char *argv[], unsigned long seed)
{
	if (CGOptions::dfs_exhaustive()) {
		AbsProgramGenerator::current_generator_ = new DFSProgramGenerator(argc, argv, seed);
	}
	else
	{
		AbsProgramGenerator::current_generator_ = new DefaultProgramGenerator(argc, argv, seed);
	}

	AbsProgramGenerator::current_generator_->initialize();

	if (!(CGOptions::dump_default_probabilities().empty())) {
		CGOptions::random_random(false);
		Probabilities *prob = Probabilities::GetInstance();
		prob->dump_default_probabilities(CGOptions::dump_default_probabilities());
		exit(0);
	}
	else if (!(CGOptions::dump_random_probabilities().empty())) {
		CGOptions::random_random(true);
		Probabilities *prob = Probabilities::GetInstance();
		prob->dump_actual_probabilities(CGOptions::dump_random_probabilities(), seed);
		exit(0);
	}

	Probabilities *prob = Probabilities::GetInstance();
	string msg;
	if (!(CGOptions::probability_configuration().empty())) {
		if (!prob->parse_configuration(msg, CGOptions::probability_configuration())) {
			cout << "parsing configuration file error:" << msg << endl;
			return NULL;
		}
	}

	return AbsProgramGenerator::current_generator_;
}

AbsProgramGenerator *
AbsProgramGenerator::GetInstance()
{
	assert(AbsProgramGenerator::current_generator_);
	return AbsProgramGenerator::current_generator_;
}

AbsProgramGenerator::AbsProgramGenerator()
{

}

AbsProgramGenerator::~AbsProgramGenerator()
{

}


text:
public class NaNCalculations {

  public static void main(String[] args) {

    float nan = 1f/0f;
    float num = 5f;

    System.out.println(nan);
    System.out.println(num);

    System.out.println(nan + num);
    System.out.println(num + nan);
    System.out.println(nan - num);
    System.out.println(num - nan);
    System.out.println(nan * num);
    System.out.println(num * nan);
    System.out.println(nan / num);
    System.out.println(num / nan);
    System.out.println(nan % num);
    System.out.println(num % nan);
    System.out.println(nan++);
    System.out.println(nan--);
    System.out.println(++nan);
    System.out.println(--nan);

    System.out.println(nan == nan);
    System.out.println(nan == num);
    System.out.println(nan >  nan);
    System.out.println(nan >  num);
    System.out.println(nan >= nan);
    System.out.println(nan >= num);
    System.out.println(nan <  nan);
    System.out.println(nan <  num);
    System.out.println(nan <= nan);
    System.out.println(nan <= num);
    System.out.println(nan != nan);
    System.out.println(nan != num);

  }

}


text:
@implementation OutputInformationController

//MARK: - controller test functions

- (void)addInformationToDataBase:(NSError **)errorPtr {
    NSMutableDictionary *newData;

    //first record
    newData = @{@"car": @"AA102", @"owner": @"Vasja"}.mutableCopy;
    [catalogListController addRecordWithDictData:newData withError:errorPtr];

    //second record
    newData = @{@"car": @"BB407", @"owner": @"Petja"}.mutableCopy;
    [catalogListController addRecordWithDictData:newData withError:errorPtr];

    //third record
    newData = @{@"car": @"CC540", @"owner": @"Galja"}.mutableCopy;
    [catalogListController addRecordWithDictData:newData withError:errorPtr];

    //fourth record
    newData = @{@"car": @"FF412", @"owner": @"Nadja"}.mutableCopy;
    [catalogListController addRecordWithDictData:newData withError:errorPtr];
}

- (void)deteleRecordFromDataBase:(NSError **)errorPtr {

    [catalogListController deteleRecord:6 withError:errorPtr];
    // Error must be there!!

    [catalogListController deteleRecord:1 withError:errorPtr];
};

- (void)editRecordInDataBase:(NSError **)errorPtr {

    NSDictionary *newData = @{@"car" : @"HH567",
                              @"owner" : @"Lena"
                              };
    [catalogListController editRecordWithDictData: newData
                                       atRowIndex: 0
                                        withError: errorPtr];

};

- (NSDictionary *)findRecordInDataBase:(NSError **)errorPtr {

    NSString *searchedNumber = @"F41";
    CarOwner *carOwnerSearched = [catalogListController findCarOwner: searchedNumber withError:errorPtr];

    NSDictionary *dataForReturn = @{@"car" : searchedNumber,
                                    @"owner" : carOwnerSearched.name//[carOwnerSearched gerOwnersName]
                                    };

    return dataForReturn;
};

- (NSString *)printSortedDataFromDataBase:(NSError **)errorPtr {

    NSString *logToReturn = @"";
    NSArray *arayForView = [catalogListController getSortedCatalog:errorPtr];

    logToReturn = [logToReturn stringByAppendingString: @"\nPrinting a sorted array"];
    for (CatalogRecord *object in arayForView) {

        logToReturn = [logToReturn stringByAppendingFormat:
                       @"\n  owner = '%@', car number = '%@'"
                       , object.owner.name
                       , object.car.number
                       ];
    };

    return logToReturn;
};

//MARK: - instanse functions

- (id)getSharedManagerCatalogListController {

    return [CatalogListController sharedManager];
}

- (id)init {

    catalogListController = [self getSharedManagerCatalogListController];

    return self;
};

@end


text:
// Copyright 2014 The Rector & Visitors of the University of Virginia
//
// 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.

using System;

namespace SensusService.Anonymization.Anonymizers
{
    public class DoubleRoundingHundredsAnonymizer : DoubleRoundingAnonymizer
    {
        public override string DisplayText
        {
            get
            {
                return "Round to Hundreds";
            }
        }

        public DoubleRoundingHundredsAnonymizer()
            : base(-2)
        {
        }
    }
}


text:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

			<title>Docs For Class Model_Address</title>
			<link rel="stylesheet" href="../../media/stylesheet.css" />
			<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'/>

Class Model_Address

Description

					Description |
													Vars (details)
						|											Methods

Address Model

			Located in /web/app/model/address.class.php (line 14)

Model
   |
   --Model_Address

Variable Summary

			Description |
						Vars (details)
							|
									Methods

					array
					$fields

					string
					$table

Variables

			Description |
										Vars

										|
									Methods

			array
			$fields
			 = array(
        'name'      => false,
        'street1'   => false,
        'street2'   => false,
        'city'      => false,
        'state'     => false,
        'zip'       => false
    )			(line 26)

List of modifiable database fields

var: associative array with bool values indicating changed fields

access: protected

Redefinition of:

Model::$fields

List of modifiable database fields

			string
			$table
			 =  'addresses'			(line 20)

Database table to use

var: table name

access: protected

Redefinition of:

Model::$table

Database table to use

Inherited Variables

Inherited from Model

								Model::$data

								Model::$db

								Model::$filter

								Model::$id

Methods

			Description |
															Vars (details)
																	Methods

Inherited Methods

Inherited From Model

													Model::__construct()

													Model::delete()

													Model::filterInsert()

													Model::filterSelect()

													Model::filterUpdate()

													Model::get()

													Model::getAll()

													Model::getCollection()

													Model::getCount()

													Model::id()

													Model::load()

													Model::parseFilters()

													Model::save()

													Model::set()

													Model::setFilter()

													Model::setMultiple()

		Documentation generated on Mon, 14 Nov 2011 11:54:43 -0800 by phpDocumentor 1.4.3


text:
% tpm2_policycommandcode(1) tpm2-tools | General Commands Manual
%
% JANUARY 2018

# NAME

**tpm2_policycommandcode**(1) - Restrict TPM object authorization to specific
TPM commands or operations.

# SYNOPSIS

**tpm2_policycommandcode** [*OPTIONS*] _COMMAND\_CODE_

# DESCRIPTION

**tpm2_policycommandcode**(1) Restricts TPM object authorization to specific
TPM commands or operations. Useful when you want to allow only specific commands
with the TPM object. As an argument it takes the _COMMAND_CODE_ as an integer
value. Requires support for extended sessions with resource manager.

# OPTIONS

  * **-S**, **--session**=_SESSION\_FILE_:

    A session file from **tpm2_startauthsession**(1)'s **-S** option.

   * **-o**, **--policy-file**=_POLICY\_FILE_:

    File to save the policy digest.
[common options](common/options.md)

[common tcti options](common/tcti.md)

# EXAMPLES

Start a *policy* session and extend it with a specific command like unseal.
Attempts to perform other operations would fail.

## Create an unseal-only policy
```
TPM_CC_UNSEAL=0x15E
tpm2_startauthsession -S session.dat
tpm2_policycommandcode -S session.dat -o policy.dat $TPM_CC_UNSEAL
tpm2_flushcontext -S session.dat
```

## Create the object with unseal-only auth policy
```
tpm2_createprimary -a o -o prim.ctx
tpm2_create -C prim.ctx -u sealkey.pub -r sealkey.priv -L policy.dat \
  -i- <<< "SEALED-SECRET"
```

# Try unseal operation
```
tpm2_load -C prim.ctx -u sealkey.pub -r sealkey.priv -n sealkey.name \
  -o sealkey.ctx
tpm2_startauthsession -a -S session.dat
tpm2_policycommandcode -S session.dat -o policy.dat $TPM_CC_UNSEAL
tpm2_unseal -p session:session.dat -c sealkey.ctx
tpm2_flushcontext -S session.dat
```

# Try any other operation
```
echo "Encrypt Me" > plain.txt
tpm2_encryptdecrypt -i plain.txt -o enc.txt -c sealkey.ctx
if [ $? != 0 ]; then
 echo "Expected that operations other than unsealing will fail"
fi
```

# RETURNS

0 on success or 1 on failure.

[footer](common/footer.md)


text:
CREATE TABLE IF NOT EXISTS push_rules_enable (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_name TEXT NOT NULL,
  rule_id TEXT NOT NULL,
  enabled TINYINT,
  UNIQUE(user_name, rule_id)
);

CREATE INDEX IF NOT EXISTS push_rules_enable_user_name on push_rules_enable (user_name);


text:
<?php
use dosamigos\tinymce\TinyMce;
use kartik\widgets\Alert;
use yii\helpers\Html;
use yii\widgets\ActiveForm;

$this->title = 'Описание страницы';
$this->params['breadcrumbs'][] = ['label' => 'Мастера', 'url' => ['/admin/mastersforwomen']];
$this->params['breadcrumbs'][] = $this->title;
?>

	<?if($success){
		echo Alert::widget([
			'type' => Alert::TYPE_SUCCESS,
			//'title' => 'Отзыв отравлен!',
			'icon' => 'glyphicon glyphicon-remove-sign',
			'body' => 'Изменения успешно сохранены!',
			'showSeparator' => true,
			'delay' => 5000,
			'options' => [
				'style' => 'position: fixed;top: 50px;right: 0;width: 400px;',
			],
		]);
	}?>
    <?php $form = ActiveForm::begin(); ?>
		<?= $form->field($model, 'text')->widget(TinyMce::className(), [
			'options' => ['rows' => 10],
			'language' => 'ru',
			'clientOptions' => [
				'plugins' => [
					"advlist autolink lists link charmap print preview anchor",
					"searchreplace visualblocks code fullscreen",
					"insertdatetime media table contextmenu paste"
				],
				'toolbar' => "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image"
			]
		]);?>

Для поисковых систем

		<?= $form->field($model, 'keywords')->textInput() ?>
		<?= $form->field($model, 'description')->textarea(['rows' => 6])?>

			<?= Html::submitButton('Сохранить', ['class' => 'btn btn-success']) ?>
			<?= Html::a('Назад', ['/admin/mastersforwomen'], ['class'=>'btn btn-primary']) ?>

    <?php ActiveForm::end(); ?>


text:
/*
 * EVE Swagger Interface
 * An OpenAPI for EVE Online
 *
 * The version of the OpenAPI document: 1.12
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

package net.troja.eve.esi.model;

import java.util.Objects;
import java.util.Arrays;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.IOException;
import java.io.Serializable;

/**
 * 200 ok object
 */
@ApiModel(description = "200 ok object")
public class CorporationWalletsResponse implements Serializable {
    private static final long serialVersionUID = 1L;

    public static final String SERIALIZED_NAME_BALANCE = "balance";
    @SerializedName(SERIALIZED_NAME_BALANCE)
    private Double balance;

    public static final String SERIALIZED_NAME_DIVISION = "division";
    @SerializedName(SERIALIZED_NAME_DIVISION)
    private Integer division;

    public CorporationWalletsResponse balance(Double balance) {

        this.balance = balance;
        return this;
    }

    /**
     * balance number
     *
     * @return balance
     **/
    @ApiModelProperty(required = true, value = "balance number")
    public Double getBalance() {
        return balance;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }

    public CorporationWalletsResponse division(Integer division) {

        this.division = division;
        return this;
    }

    /**
     * division integer minimum: 1 maximum: 7
     *
     * @return division
     **/
    @ApiModelProperty(required = true, value = "division integer")
    public Integer getDivision() {
        return division;
    }

    public void setDivision(Integer division) {
        this.division = division;
    }

    @Override
    public boolean equals(java.lang.Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        CorporationWalletsResponse corporationWalletsResponse = (CorporationWalletsResponse) o;
        return Objects.equals(this.balance, corporationWalletsResponse.balance)
                && Objects.equals(this.division, corporationWalletsResponse.division);
    }

    @Override
    public int hashCode() {
        return Objects.hash(balance, division);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("class CorporationWalletsResponse {\n");
        sb.append("    balance: ").append(toIndentedString(balance)).append("\n");
        sb.append("    division: ").append(toIndentedString(division)).append("\n");
        sb.append("}");
        return sb.toString();
    }

    /**
     * Convert the given object to string with each line indented by 4 spaces
     * (except the first line).
     */
    private String toIndentedString(java.lang.Object o) {
        if (o == null) {
            return "null";
        }
        return o.toString().replace("\n", "\n    ");
    }

}


text:
var
  restify = require('restify'),
  client  =  restify.createJsonClient({url: 'http://localhost:1337'});

describe('#roomy', function() {

  var handler = require('../handlers/roomy.js');

  it('Returns an object', function() {
    handler.should.be.an('object');
  });

  it('Contains a findAll function', function() {
    handler.should.include.keys('findAll');
    handler.findAll.should.be.a('function');
  });

  it('Contains a findOne function', function() {
    handler.should.include.keys('findOne');
    handler.findOne.should.be.a('function');
  });

  it('Contains a deleteAll function', function() {
    handler.should.include.keys('deleteAll');
    handler.deleteAll.should.be.a('function');
  });

  it('Contains a deleteOne function', function() {
    handler.should.include.keys('deleteOne');
    handler.deleteOne.should.be.a('function');
  });

  it('Contains a createOne function', function() {
    handler.should.include.keys('createOne');
    handler.createOne.should.be.a('function');
  });

  it('Contains an updateOne function', function() {
    handler.should.include.keys('updateOne');
    handler.updateOne.should.be.a('function');
  });
});


text:
package chroot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"testing"

	"github.com/containers/buildah/tests/testreport/types"
	"github.com/containers/buildah/util"
	"github.com/containers/storage/pkg/reexec"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/opencontainers/runtime-tools/generate"
)

const (
	reportCommand = "testreport"
)

func TestMain(m *testing.M) {
	if reexec.Init() {
		return
	}
	os.Exit(m.Run())
}

func testMinimal(t *testing.T, modify func(g *generate.Generator, rootDir, bundleDir string), verify func(t *testing.T, report *types.TestReport)) {
	g, err := generate.New("linux")
	if err != nil {
		t.Fatalf("generate.New(%q): %v", "linux", err)
	}

	tempDir, err := ioutil.TempDir("", "chroot-test")
	if err != nil {
		t.Fatalf("ioutil.TempDir(%q, %q): %v", "", "chrootTest", err)
	}
	defer os.RemoveAll(tempDir)
	info, err := os.Stat(tempDir)
	if err != nil {
		t.Fatalf("error checking permissions on %q: %v", tempDir, err)
	}
	if err = os.Chmod(tempDir, info.Mode()|0111); err != nil {
		t.Fatalf("error loosening permissions on %q: %v", tempDir, err)
	}

	rootDir := filepath.Join(tempDir, "root")
	if err := os.Mkdir(rootDir, 0711); err != nil {
		t.Fatalf("os.Mkdir(%q): %v", rootDir, err)
	}

	specPath := filepath.Join("..", "tests", reportCommand, reportCommand)
	specBinarySource, err := os.Open(specPath)
	if err != nil {
		t.Fatalf("open(%q): %v", specPath, err)
	}
	defer specBinarySource.Close()
	specBinary, err := os.OpenFile(filepath.Join(rootDir, reportCommand), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0711)
	if err != nil {
		t.Fatalf("open(%q): %v", filepath.Join(rootDir, reportCommand), err)
	}
	io.Copy(specBinary, specBinarySource)
	specBinary.Close()

	g.SetRootPath(rootDir)
	g.SetProcessArgs([]string{"/" + reportCommand})

	bundleDir := filepath.Join(tempDir, "bundle")
	if err := os.Mkdir(bundleDir, 0700); err != nil {
		t.Fatalf("os.Mkdir(%q): %v", bundleDir, err)
	}

	if modify != nil {
		modify(&g, rootDir, bundleDir)
	}

	uid, gid, err := util.GetHostRootIDs(g.Spec())
	if err != nil {
		t.Fatalf("GetHostRootIDs: %v", err)
	}
	if err := os.Chown(rootDir, int(uid), int(gid)); err != nil {
		t.Fatalf("os.Chown(%q): %v", rootDir, err)
	}

	output := new(bytes.Buffer)
	if err := RunUsingChroot(g.Spec(), bundleDir, new(bytes.Buffer), output, output); err != nil {
		t.Fatalf("run: %v: %s", err, output.String())
	}

	var report types.TestReport
	if json.Unmarshal(output.Bytes(), &report) != nil {
		t.Fatalf("decode: %v", err)
	}

	if verify != nil {
		verify(t, &report)
	}
}

func testNoop(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	testMinimal(t, nil, nil)
}

func testMinimalSkeleton(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	testMinimal(t,
		func(g *generate.Generator, rootDir, bundleDir string) {
		},
		func(t *testing.T, report *types.TestReport) {
		})
}

func TestProcessTerminal(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	for _, terminal := range []bool{false, true} {
		testMinimal(t,
			func(g *generate.Generator, rootDir, bundleDir string) {
				g.SetProcessTerminal(terminal)
			},
			func(t *testing.T, report *types.TestReport) {
				if report.Spec.Process.Terminal != terminal {
					t.Fatalf("expected terminal = %v, got %v", terminal, report.Spec.Process.Terminal)
				}
			})
	}
}

func TestProcessConsoleSize(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	for _, size := range [][2]uint{{80, 25}, {132, 50}} {
		testMinimal(t,
			func(g *generate.Generator, rootDir, bundleDir string) {
				g.SetProcessTerminal(true)
				g.SetProcessConsoleSize(size[0], size[1])
			},
			func(t *testing.T, report *types.TestReport) {
				if report.Spec.Process.ConsoleSize.Width != size[0] {
					t.Fatalf("expected console width = %v, got %v", size[0], report.Spec.Process.ConsoleSize.Width)
				}
				if report.Spec.Process.ConsoleSize.Height != size[1] {
					t.Fatalf("expected console height = %v, got %v", size[1], report.Spec.Process.ConsoleSize.Height)
				}
			})
	}
}

func TestProcessUser(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	for _, id := range []uint32{0, 1000} {
		testMinimal(t,
			func(g *generate.Generator, rootDir, bundleDir string) {
				g.SetProcessUID(id)
				g.SetProcessGID(id + 1)
				g.AddProcessAdditionalGid(id + 2)
			},
			func(t *testing.T, report *types.TestReport) {
				if report.Spec.Process.User.UID != id {
					t.Fatalf("expected UID %v, got %v", id, report.Spec.Process.User.UID)
				}
				if report.Spec.Process.User.GID != id+1 {
					t.Fatalf("expected GID %v, got %v", id+1, report.Spec.Process.User.GID)
				}
			})
	}
}

func TestProcessEnv(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	e := fmt.Sprintf("PARENT_TEST_PID=%d", syscall.Getpid())
	testMinimal(t,
		func(g *generate.Generator, rootDir, bundleDir string) {
			g.ClearProcessEnv()
			g.AddProcessEnv("PARENT_TEST_PID", fmt.Sprintf("%d", syscall.Getpid()))
		},
		func(t *testing.T, report *types.TestReport) {
			for _, ev := range report.Spec.Process.Env {
				if ev == e {
					return
				}
			}
			t.Fatalf("expected environment variable %q", e)
		})
}

func TestProcessCwd(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	testMinimal(t,
		func(g *generate.Generator, rootDir, bundleDir string) {
			if err := os.Mkdir(filepath.Join(rootDir, "/no-such-directory"), 0700); err != nil {
				t.Fatalf("mkdir(%q): %v", filepath.Join(rootDir, "/no-such-directory"), err)
			}
			g.SetProcessCwd("/no-such-directory")
		},
		func(t *testing.T, report *types.TestReport) {
			if report.Spec.Process.Cwd != "/no-such-directory" {
				t.Fatalf("expected %q, got %q", "/no-such-directory", report.Spec.Process.Cwd)
			}
		})
}

func TestProcessCapabilities(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	testMinimal(t,
		func(g *generate.Generator, rootDir, bundleDir string) {
			g.ClearProcessCapabilities()
		},
		func(t *testing.T, report *types.TestReport) {
			if len(report.Spec.Process.Capabilities.Permitted) != 0 {
				t.Fatalf("expected no permitted capabilities, got %#v", report.Spec.Process.Capabilities.Permitted)
			}
		})
	testMinimal(t,
		func(g *generate.Generator, rootDir, bundleDir string) {
			g.ClearProcessCapabilities()
			g.AddProcessCapabilityEffective("CAP_IPC_LOCK")
			g.AddProcessCapabilityPermitted("CAP_IPC_LOCK")
			g.AddProcessCapabilityInheritable("CAP_IPC_LOCK")
			g.AddProcessCapabilityBounding("CAP_IPC_LOCK")
			g.AddProcessCapabilityAmbient("CAP_IPC_LOCK")
		},
		func(t *testing.T, report *types.TestReport) {
			if len(report.Spec.Process.Capabilities.Permitted) != 1 {
				t.Fatalf("expected one permitted capability, got %#v", report.Spec.Process.Capabilities.Permitted)
			}
			if report.Spec.Process.Capabilities.Permitted[0] != "CAP_IPC_LOCK" {
				t.Fatalf("expected one capability CAP_IPC_LOCK, got %#v", report.Spec.Process.Capabilities.Permitted)
			}
		})
}

func TestProcessRlimits(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	for _, limit := range []int64{100 * 1024 * 1024 * 1024, 200 * 1024 * 1024 * 1024, syscall.RLIM_INFINITY} {
		testMinimal(t,
			func(g *generate.Generator, rootDir, bundleDir string) {
				g.ClearProcessRlimits()
				if limit != syscall.RLIM_INFINITY {
					g.AddProcessRlimits("rlimit_as", uint64(limit), uint64(limit))
				}
			},
			func(t *testing.T, report *types.TestReport) {
				var rlim *specs.POSIXRlimit
				for i := range report.Spec.Process.Rlimits {
					if strings.ToUpper(report.Spec.Process.Rlimits[i].Type) == "RLIMIT_AS" {
						rlim = &report.Spec.Process.Rlimits[i]
					}
				}
				if limit == syscall.RLIM_INFINITY && !(rlim == nil || (int64(rlim.Soft) == syscall.RLIM_INFINITY && int64(rlim.Hard) == syscall.RLIM_INFINITY)) {
					t.Fatalf("wasn't supposed to set limit on number of open files: %#v", rlim)
				}
				if limit != syscall.RLIM_INFINITY && rlim == nil {
					t.Fatalf("was supposed to set limit on number of open files")
				}
				if rlim != nil {
					if int64(rlim.Soft) != limit {
						t.Fatalf("soft limit was set to %d, not %d", rlim.Soft, limit)
					}
					if int64(rlim.Hard) != limit {
						t.Fatalf("hard limit was set to %d, not %d", rlim.Hard, limit)
					}
				}
			})
	}
}

func TestProcessNoNewPrivileges(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	for _, nope := range []bool{false, true} {
		testMinimal(t,
			func(g *generate.Generator, rootDir, bundleDir string) {
				g.SetProcessNoNewPrivileges(nope)
			},
			func(t *testing.T, report *types.TestReport) {
				if report.Spec.Process.NoNewPrivileges != nope {
					t.Fatalf("expected no-new-prives to be %v, got %v", nope, report.Spec.Process.NoNewPrivileges)
				}
			})
	}
}

func TestProcessOOMScoreAdj(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	for _, adj := range []int{0, 1, 2, 3} {
		testMinimal(t,
			func(g *generate.Generator, rootDir, bundleDir string) {
				g.SetProcessOOMScoreAdj(adj)
			},
			func(t *testing.T, report *types.TestReport) {
				adjusted := 0
				if report.Spec.Process.OOMScoreAdj != nil {
					adjusted = *report.Spec.Process.OOMScoreAdj
				}
				if adjusted != adj {
					t.Fatalf("expected oom-score-adj to be %v, got %v", adj, adjusted)
				}
			})
	}
}

func TestHostname(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	hostname := fmt.Sprintf("host%d", syscall.Getpid())
	testMinimal(t,
		func(g *generate.Generator, rootDir, bundleDir string) {
			g.SetHostname(hostname)
		},
		func(t *testing.T, report *types.TestReport) {
			if report.Spec.Hostname != hostname {
				t.Fatalf("expected %q, got %q", hostname, report.Spec.Hostname)
			}
		})
}

func TestMounts(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	testMinimal(t,
		func(g *generate.Generator, rootDir, bundleDir string) {
			g.AddMount(specs.Mount{
				Source:      "tmpfs",
				Destination: "/was-not-there-before",
				Type:        "tmpfs",
				Options:     []string{"ro,size=0"},
			})
		},
		func(t *testing.T, report *types.TestReport) {
			found := false
			for _, mount := range report.Spec.Mounts {
				if mount.Destination == "/was-not-there-before" && mount.Type == "tmpfs" {
					found = true
				}
			}
			if !found {
				t.Fatal("added mount not found")
			}
		})
}

func TestLinuxIDMapping(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	testMinimal(t,
		func(g *generate.Generator, rootDir, bundleDir string) {
			g.ClearLinuxUIDMappings()
			g.ClearLinuxGIDMappings()
			g.AddLinuxUIDMapping(uint32(syscall.Getuid()), 0, 1)
			g.AddLinuxGIDMapping(uint32(syscall.Getgid()), 0, 1)
		},
		func(t *testing.T, report *types.TestReport) {
			if len(report.Spec.Linux.UIDMappings) != 1 {
				t.Fatalf("expected 1 uid mapping, got %q", len(report.Spec.Linux.UIDMappings))
			}
			if report.Spec.Linux.UIDMappings[0].HostID != uint32(syscall.Getuid()) {
				t.Fatalf("expected host uid mapping to be %d, got %d", syscall.Getuid(), report.Spec.Linux.UIDMappings[0].HostID)
			}
			if report.Spec.Linux.UIDMappings[0].ContainerID != 0 {
				t.Fatalf("expected container uid mapping to be 0, got %d", report.Spec.Linux.UIDMappings[0].ContainerID)
			}
			if report.Spec.Linux.UIDMappings[0].Size != 1 {
				t.Fatalf("expected container uid map size to be 1, got %d", report.Spec.Linux.UIDMappings[0].Size)
			}
			if report.Spec.Linux.GIDMappings[0].HostID != uint32(syscall.Getgid()) {
				t.Fatalf("expected host uid mapping to be %d, got %d", syscall.Getgid(), report.Spec.Linux.GIDMappings[0].HostID)
			}
			if report.Spec.Linux.GIDMappings[0].ContainerID != 0 {
				t.Fatalf("expected container gid mapping to be 0, got %d", report.Spec.Linux.GIDMappings[0].ContainerID)
			}
			if report.Spec.Linux.GIDMappings[0].Size != 1 {
				t.Fatalf("expected container gid map size to be 1, got %d", report.Spec.Linux.GIDMappings[0].Size)
			}
		})
}

func TestLinuxIDMappingShift(t *testing.T) {
	if syscall.Getuid() != 0 {
		t.Skip("tests need to be run as root")
	}
	testMinimal(t,
		func(g *generate.Generator, rootDir, bundleDir string) {
			g.ClearLinuxUIDMappings()
			g.ClearLinuxGIDMappings()
			g.AddLinuxUIDMapping(uint32(syscall.Getuid())+1, 0, 1)
			g.AddLinuxGIDMapping(uint32(syscall.Getgid())+1, 0, 1)
		},
		func(t *testing.T, report *types.TestReport) {
			if len(report.Spec.Linux.UIDMappings) != 1 {
				t.Fatalf("expected 1 uid mapping, got %q", len(report.Spec.Linux.UIDMappings))
			}
			if report.Spec.Linux.UIDMappings[0].HostID != uint32(syscall.Getuid()+1) {
				t.Fatalf("expected host uid mapping to be %d, got %d", syscall.Getuid()+1, report.Spec.Linux.UIDMappings[0].HostID)
			}
			if report.Spec.Linux.UIDMappings[0].ContainerID != 0 {
				t.Fatalf("expected container uid mapping to be 0, got %d", report.Spec.Linux.UIDMappings[0].ContainerID)
			}
			if report.Spec.Linux.UIDMappings[0].Size != 1 {
				t.Fatalf("expected container uid map size to be 1, got %d", report.Spec.Linux.UIDMappings[0].Size)
			}
			if report.Spec.Linux.GIDMappings[0].HostID != uint32(syscall.Getgid()+1) {
				t.Fatalf("expected host uid mapping to be %d, got %d", syscall.Getgid()+1, report.Spec.Linux.GIDMappings[0].HostID)
			}
			if report.Spec.Linux.GIDMappings[0].ContainerID != 0 {
				t.Fatalf("expected container gid mapping to be 0, got %d", report.Spec.Linux.GIDMappings[0].ContainerID)
			}
			if report.Spec.Linux.GIDMappings[0].Size != 1 {
				t.Fatalf("expected container gid map size to be 1, got %d", report.Spec.Linux.GIDMappings[0].Size)
			}
		})
}


text:
class AddEditComment extends HTMLElement {
  constructor(playbackEngine, editedComment) {
    super();

    this.playbackEngine = playbackEngine;
    //when used for updating a comment (not a brand new comment)
    this.editedComment = editedComment;
    //hold the number of lines in the file getting a new/edited comment
    this.numLinesInFile = 0;

    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(this.getTemplate());
  }

  getTemplate() {
    const template = document.createElement('template');
    template.innerHTML = `

        <input type="text" id="commentTitle" placeholder="Comment Title (Optional)"></input>

        <st-show-hide-component name="Media" show="true">

        </st-show-hide-component>
        <st-show-hide-component name="Multiple Choice Question">

        </st-show-hide-component>
        <st-show-hide-component name="Tags">

        </st-show-hide-component>
        <st-show-hide-component id="deleteHideShow" name="Delete this Comment" class="inactive">

            Delete this comment (this can't be undone)
            <button id="deleteCommentButton">Delete</button>

        </st-show-hide-component>

          <button id="submitButton" class="controlButton"></button>
          <button id="cancelButton" class="controlButton">Cancel</button>

`;

    return template.content.cloneNode(true);
  }

  connectedCallback() {
    //add the lines above/below controls
    const surroundingText = this.shadowRoot.querySelector('#surroundingText');
    const linesAbove = new SurroundingLinesSelector(-90, 'Above', this.numLinesInFile);
    linesAbove.setAttribute('id', 'linesAboveSelector');

    const linesBelow = new SurroundingLinesSelector(90, 'Below', this.numLinesInFile);
    linesBelow.setAttribute('id', 'linesBelowSelector');
    surroundingText.appendChild(linesAbove);
    surroundingText.appendChild(linesBelow);

    //listen for changes in the lines above/below
    this.shadowRoot.addEventListener('surrounding-lines-change', event => {
      this.sendEventNotifyLinesAboveBelowChange();
      event.stopPropagation();
    });

    //delete comment button
    const deleteCommentButton = this.shadowRoot.querySelector('#deleteCommentButton');
    deleteCommentButton.addEventListener('click', this.sendEventDeleteComment);

    //main comment buttons
    const cancelButton = this.shadowRoot.querySelector('#cancelButton');
    cancelButton.addEventListener('click', this.sendEventCancelAddEditComment);

    const submitButton = this.shadowRoot.querySelector('#submitButton');
    submitButton.innerHTML = 'Add Comment';
    submitButton.addEventListener('click', this.sendEventAddEditComment);

    //update the placeholder text on focus and blur
    const commentTextContainer = this.shadowRoot.querySelector('#commentTextContainer');
    const commentText = new MultiLineTextInput('Describe the code at this point', '', 200);
    commentText.setAttribute('id', 'commentText');
    commentText.setFocus();
    commentTextContainer.appendChild(commentText);

    //if there is a comment associated with this component then fill the inputs with data from it
    if (this.editedComment) {
      this.updateEditCommentMode();
    } else { //no comment, fill with empty default values
      //init this component to be a new comment
      this.updateAddCommentMode();
    }

    this.shadowRoot.addEventListener('pause-all-vertical-media-containers', event => {
      //get all of the vertical media constainers and pause them
      const mediaContainers = this.shadowRoot.querySelectorAll('st-vertical-media-container');
      mediaContainers.forEach(mediaContainer => mediaContainer.pauseMedia());
    });

    //prevent normal text editing from firing any keyboard shortcuts
    this.shadowRoot.addEventListener('keydown', this.stopProp);
  }

  disconnectedCallback() {
    this.shadowRoot.removeEventListener('keydown', this.stopProp);
  }

  stopProp(event) {
    event.stopPropagation();
  }

  sendEventNotifyLinesAboveBelowChange = () => {
    //get the elements that have the requested lines above/below
    const linesAboveSelector = this.shadowRoot.querySelector('#linesAboveSelector');
    const linesBelowSelector = this.shadowRoot.querySelector('#linesBelowSelector');

    //generate and send an event with the above/below values
    const customEvent = new CustomEvent('lines-above-below-change', {
      detail: {
        linesAbove: linesAboveSelector.getValue(),
        linesBelow: linesBelowSelector.getValue()
      },
      bubbles: true,
      composed: true
    });
    this.dispatchEvent(customEvent);
  }

  updateForFileSelected() {
    //get the number of lines in the file (may be 0 if there is not an active file)
    this.numLinesInFile = this.playbackEngine.editorState.getNumLinesInFile(this.playbackEngine.activeFileId);

    //update the max values for the inputs
    const linesAboveSelector = this.shadowRoot.querySelector('#linesAboveSelector');
    linesAboveSelector.setMax(this.numLinesInFile);
    const linesBelowSelector = this.shadowRoot.querySelector('#linesBelowSelector');
    linesBelowSelector.setMax(this.numLinesInFile);
  }

  updateAddCommentMode() {
    const imagesVMC = this.shadowRoot.querySelector('#imagesVMC');
    const images = new VerticalMediaContainer([], 'image');
    imagesVMC.appendChild(images);

    const videosVMC = this.shadowRoot.querySelector('#videosVMC');
    const videos = new VerticalMediaContainer([], 'video');
    videosVMC.appendChild(videos);

    const audiosVMC = this.shadowRoot.querySelector('#audiosVMC');
    const audios = new VerticalMediaContainer([], 'audio');
    audiosVMC.appendChild(audios);

    //add an empty Q&A component
    const questionAnswerContainer = this.shadowRoot.querySelector('#questionAnswerContainer');
    const qAndA = new CreateMultipleChoiceQuestion(null);
    questionAnswerContainer.appendChild(qAndA);

    //empty tags
    const tagContainer = this.shadowRoot.querySelector('#tagContainer');
    const tags = new CommentTags([], this.playbackEngine);
    tagContainer.appendChild(tags);

    //set the lines above/below to 0
    const linesAboveSelector = this.shadowRoot.querySelector('#linesAboveSelector');
    linesAboveSelector.setValue(0);
    const linesBelowSelector = this.shadowRoot.querySelector('#linesBelowSelector');
    linesBelowSelector.setValue(0);

    //update the max lines above/below from an active file
    this.updateForFileSelected();

    //set the text and event handler for the submit button
    const submitButton = this.shadowRoot.querySelector('#submitButton');
    submitButton.innerHTML = 'Add Comment';
  }

  updateEditCommentMode() {
    //set the main comment text
    const commentText = this.shadowRoot.querySelector('#commentText');
    commentText.updateFormattedText(this.editedComment.commentText);
    //set the comment title
    const commentTitle = this.shadowRoot.querySelector('#commentTitle');
    commentTitle.setAttribute('value', this.editedComment.commentTitle ? this.editedComment.commentTitle : '');

    const imagesVMC = this.shadowRoot.querySelector('#imagesVMC');
    const images = new VerticalMediaContainer(this.editedComment.imageURLs, 'image');
    imagesVMC.appendChild(images);

    const videosVMC = this.shadowRoot.querySelector('#videosVMC');
    const videos = new VerticalMediaContainer(this.editedComment.videoURLs, 'video');
    videosVMC.appendChild(videos);

    const audiosVMC = this.shadowRoot.querySelector('#audiosVMC');
    const audios = new VerticalMediaContainer(this.editedComment.audioURLs, 'audio');
    audiosVMC.appendChild(audios);

    //add a Q&A component for an existing question
    const questionAnswerContainer = this.shadowRoot.querySelector('#questionAnswerContainer');
    const qAndA = new CreateMultipleChoiceQuestion(this.editedComment.questionCommentData);

    if (this.editedComment.questionCommentData && this.editedComment.questionCommentData.question.length > 0){
      const showHideComponent = this.shadowRoot.querySelector('[name="Multiple Choice Question"]');
      showHideComponent.setAttribute('show', 'true');
    }

    questionAnswerContainer.appendChild(qAndA);

    //if there are any tags in this comment
    if (this.editedComment.commentTags.length > 0) {
      //expand the show/hide
      const tagsShowHide = this.shadowRoot.querySelector('[name="Tags"]');
      tagsShowHide.setAttribute('show', 'true');
    }
    const tagContainer = this.shadowRoot.querySelector('#tagContainer');
    const tags = new CommentTags(this.editedComment.commentTags, this.playbackEngine);
    tagContainer.innerHTML = '';
    tagContainer.appendChild(tags);

    //set the lines above/below to what they are in the comment
    const linesAboveSelector = this.shadowRoot.querySelector('#linesAboveSelector');
    linesAboveSelector.setValue(this.editedComment.linesAbove);
    const linesBelowSelector = this.shadowRoot.querySelector('#linesBelowSelector');
    linesBelowSelector.setValue(this.editedComment.linesBelow);
    //update the max lines above/below from an active file
    this.updateForFileSelected();

    //add a delete button to all but the first comment
    const allComments = this.playbackEngine.commentInfo.flattenedComments;
    const firstComment = allComments[0];
    if (this.editedComment.id !== firstComment.id) {
      //show the delete comment button
      const deleteCommentButtonDiv = this.shadowRoot.querySelector('#deleteCommentButtonDiv');
      deleteCommentButtonDiv.classList.remove('inactive');

      const deleteHideShow = this.shadowRoot.querySelector('#deleteHideShow');
      deleteHideShow.classList.remove('inactive');
    }

    //set the text and event handler for the submit button
    const submitButton = this.shadowRoot.querySelector('#submitButton');
    submitButton.innerHTML = 'Edit Comment';
  }

  sendEventCancelAddEditComment = () => {
    //clear out a previously edited comment (if there is one)
    this.editedComment = null;

    //hide the delete comment button
    const deleteCommentButtonDiv = this.shadowRoot.querySelector('#deleteCommentButtonDiv');
    deleteCommentButtonDiv.classList.add('inactive');

    //generate and send an event upward to indicate adding/editing is complete
    const customEvent = new CustomEvent('cancel-add-edit-comment', {
      bubbles: true,
      composed: true
    });
    this.dispatchEvent(customEvent);
  }

  sendEventAddEditComment = (event) => {
    //attempt to get comment data
    const commentData = this.buildCommentObjectFromUI();

    //if it is not a valid comment
    if (commentData.status !== 'ok') {
      //display some error messages
      const errorMessages = this.shadowRoot.querySelector('#errorMessages');
      errorMessages.innerHTML = commentData.errorMessage;
    } else { //this is a valid comment
      //go through all of the media containers and commit any changes made to the media associated with the comment
      const allVMCs = this.shadowRoot.querySelectorAll('st-vertical-media-container');
      allVMCs.forEach(aVMC => {
        aVMC.commitChanges();
      });

      let eventType = '';
      //use the button text to determine what type of event to send
      if (event.target.innerHTML === 'Add Comment') {
        eventType = 'add-comment';
      } else {
        eventType = 'edit-comment';
      }

      //generate and send an event upward to indicate a new/edit comment has been added
      const customEvent = new CustomEvent(eventType, {
        detail: {
          comment: commentData.comment
        },
        bubbles: true,
        composed: true
      });
      this.dispatchEvent(customEvent);
    }
  }

  sendEventDeleteComment = () => {
    //go through all of the media containers and delete the media associated with the comment
    const allVMCs = this.shadowRoot.querySelectorAll('st-vertical-media-container');
    allVMCs.forEach(aVMC => {
      aVMC.deleteAll();
    });

    const event = new CustomEvent('delete-comment', {
      detail: {
        comment: this.editedComment
      },
      bubbles: true,
      composed: true
    });
    this.dispatchEvent(event);
  }

  buildCommentObjectFromUI() {
    //status of the validation
    const retVal = {
      status: 'bad comment',
      comment: null,
      errorMessage: ''
    };

    //comment text
    const commentText = this.shadowRoot.querySelector('#commentText');

    //comment title
    const commentTitle = this.shadowRoot.querySelector('#commentTitle');

    //comment question
    const createMultipleChoiceQuestion = this.shadowRoot.querySelector('st-create-multiple-choice-question');
    const qAndA = createMultipleChoiceQuestion.getMultipleChoiceQuestionData();

    //media
    const imagesVMC = this.shadowRoot.querySelector('#imagesVMC').children[0];
    const videosVMC = this.shadowRoot.querySelector('#videosVMC').children[0];
    const audiosVMC = this.shadowRoot.querySelector('#audiosVMC').children[0];
    const imageURLs = imagesVMC.getURLsInOrder();
    const videoURLs = videosVMC.getURLsInOrder();
    const audioURLs = audiosVMC.getURLsInOrder();

    //tags
    const commentTags = this.shadowRoot.querySelector('st-comment-tags');
    //get all of the tags specified by the user
    const allTagsSet = new Set(commentTags.getAllTags());
    //handle automatic tagging of media and questions
    if(imageURLs.length === 0) {
      allTagsSet.delete('image');
    } else { //there is at least one image
      allTagsSet.add('image');
    }
    if(videoURLs.length === 0) {
      allTagsSet.delete('video');
    } else { //there is at least one video
      allTagsSet.add('video');
    }
    if(audioURLs.length === 0) {
      allTagsSet.delete('audio');
    } else { //there is at least one audio
      allTagsSet.add('audio');
    }
    if (qAndA.questionState === 'valid question') {
      allTagsSet.add('question');
    } else {
      allTagsSet.delete('question');
    }

    //if there is a comment title or some comment text
    if (commentTitle.value.trim() !== '' || commentText.getFormattedText() !== '') {
      //if the question is ok, then this is a good comment
      if (qAndA.questionState === 'valid question' || qAndA.questionState === 'no question') {
        retVal.status = 'ok';
      }
    } else { //there is no comment title or comment text
      //if there is some media
      if (imageURLs.length > 0 || videoURLs.length > 0 || audioURLs.length > 0) {
        //if the question is ok, then this is a good comment
        if (qAndA.questionState === 'valid question' || qAndA.questionState === 'no question') {
          retVal.status = 'ok';
        }
      } else if (qAndA.questionState === 'valid question') { //no text, no media, but there is a valid question
        retVal.status = 'ok';
      }
    }
    //if its a valid comment
    if (retVal.status === 'ok') {
      const mostRecentEvent = this.playbackEngine.getMostRecentEvent();
      const activeFilePath = this.playbackEngine.getActiveFilePath();

      const comment = {
        id: this.editedComment ? this.editedComment.id : null,
        displayCommentEvent: this.editedComment ? this.editedComment.displayCommentEvent : mostRecentEvent,
        developerGroupId: this.editedComment ? this.editedComment.developerGroupId : null,
        timestamp: this.editedComment ? this.editedComment.timestamp : new Date().getTime(),
        commentText: commentText.getFormattedText(),
        commentTitle: commentTitle.value,
        selectedCodeBlocks: [], //this will be set in CodeView
        viewableBlogText: '', //this will be set in CodeView
        imageURLs: imageURLs,
        videoURLs: videoURLs,
        audioURLs: audioURLs,
        linesAbove: 0, //this will be set in CodeView
        linesBelow: 0, //this will be set in CodeView
        currentFilePath: this.editedComment ? this.editedComment.currentFilePath : activeFilePath,
        commentTags: [...allTagsSet], //all distinct tags with automatically added tags
        questionCommentData: qAndA.questionState === 'valid question' ? qAndA.questionData : null
      };
      //store the comment
      retVal.comment = comment;
    } else { //there's something wrong with this comment
      //add an error message
      if (commentTitle.value.trim() === '' || commentText.getFormattedText() === '') {
        retVal.errorMessage = 'A comment must have some text describing it or some media associated with it. ';
      }
      //error with question
      if (qAndA.questionState !== 'valid question' && qAndA.questionState !== 'no question') {
        retVal.errorMessage += qAndA.errorMessage;
      }
    }
    return retVal;
  }
}

window.customElements.define('st-add-edit-comment', AddEditComment);


text:
rothar-shared-lib
=================

rothar-shared-lib


text:
#Requires -Version 3.0
#Requires -Module AzureRM.Resources
#Requires -Module Azure.Storage
#Requires -Module @{ModuleName="AzureRm.Profile";ModuleVersion="3.0"}

Param(
    [string] [Parameter(Mandatory = $true)] $ArtifactStagingDirectory,
    [string] [Parameter(Mandatory = $true)][alias("ResourceGroupLocation")] $Location,
    [string] $ResourceGroupName = (Split-Path $ArtifactStagingDirectory -Leaf),
    [switch] $UploadArtifacts,
    [string] $StorageAccountName,
    [string] $StorageContainerName = $ResourceGroupName.ToLowerInvariant() + '-stageartifacts',
    [string] $TemplateFile = $ArtifactStagingDirectory + '\mainTemplate.json',
    [string] $TemplateParametersFile = $ArtifactStagingDirectory + '.\azuredeploy.parameters.json',
    [string] $DSCSourceFolder = $ArtifactStagingDirectory + '.\DSC',
    [switch] $BuildDscPackage,
    [switch] $ValidateOnly,
    [string] $DebugOptions = "None",
    [string] $DeploymentName = ([IO.Path]::GetFileNameWithoutExtension($TemplateFile) + '-' + ((Get-Date).ToUniversalTime()).ToString('MMdd-HHmm')),
    [switch] $Dev
)

try {
    [Microsoft.Azure.Common.Authentication.AzureSession]::ClientFactory.AddUserAgent("AzureQuickStarts-$UI$($host.name)".replace(" ", "_"), "1.0")
}
catch { }

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version 3

function Format-ValidationOutput {
    param ($ValidationOutput, [int] $Depth = 0)
    Set-StrictMode -Off
    return @($ValidationOutput | Where-Object { $_ -ne $null } | ForEach-Object { @('  ' * $Depth + ': ' + $_.Message) + @(Format-ValidationOutput @($_.Details) ($Depth + 1)) })
}

$OptionalParameters = New-Object -TypeName Hashtable
$TemplateArgs = New-Object -TypeName Hashtable
$ArtifactStagingDirectory = ($ArtifactStagingDirectory.TrimEnd('/')).TrimEnd('\')

# if the template file isn't found, try the another default
if (!(Test-Path $TemplateFile)) {
    $TemplateFile = $ArtifactStagingDirectory + '\azuredeploy.json'
}

Write-Host "Using template file:  $TemplateFile"

#try a few different default options for param files when the -dev switch is use
if ($Dev) {
    $TemplateParametersFile = $TemplateParametersFile.Replace('azuredeploy.parameters.json', 'azuredeploy.parameters.dev.json')
    if (!(Test-Path $TemplateParametersFile)) {
        $TemplateParametersFile = $TemplateParametersFile.Replace('azuredeploy.parameters.dev.json', 'azuredeploy.parameters.1.json')
    }
}

Write-Host "Using parameter file: $TemplateParametersFile"

if (!$ValidateOnly) {
    $OptionalParameters.Add('DeploymentDebugLogLevel', $DebugOptions)
}

$TemplateFile = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $TemplateFile))
$TemplateParametersFile = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $TemplateParametersFile))

$TemplateJSON = Get-Content $TemplateFile -Raw | ConvertFrom-Json

$TemplateSchema = $TemplateJson | Select-Object -expand '$schema' -ErrorAction Ignore

if ($TemplateSchema -like '*subscriptionDeploymentTemplate.json*') {
    $deploymentScope = "Subscription"
}
else {
    $deploymentScope = "ResourceGroup"
}

Write-Host "Running a $deploymentScope scoped deployment..."

$ArtifactsLocationParameter = $TemplateJson | Select-Object -expand 'parameters' -ErrorAction Ignore | Select-Object -Expand '_artifactsLocation' -ErrorAction Ignore

#if the switch is set or the standard parameter is present in the template, upload all artifacts
if ($UploadArtifacts -Or $ArtifactsLocationParameter -ne $null) {
    # Convert relative paths to absolute paths if needed
    $ArtifactStagingDirectory = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $ArtifactStagingDirectory))
    $DSCSourceFolder = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $DSCSourceFolder))

    # Parse the parameter file and update the values of artifacts location and artifacts location SAS token if they are present
    $JsonParameters = Get-Content $TemplateParametersFile -Raw | ConvertFrom-Json
    if (($JsonParameters | Get-Member -Type NoteProperty 'parameters') -ne $null) {
        $JsonParameters = $JsonParameters.parameters
    }
    $ArtifactsLocationName = '_artifactsLocation'
    $ArtifactsLocationSasTokenName = '_artifactsLocationSasToken'
    $OptionalParameters[$ArtifactsLocationName] = $JsonParameters | Select-Object -Expand $ArtifactsLocationName -ErrorAction Ignore | Select-Object -Expand 'value' -ErrorAction Ignore
    $OptionalParameters[$ArtifactsLocationSasTokenName] = $JsonParameters | Select-Object -Expand $ArtifactsLocationSasTokenName -ErrorAction Ignore | Select-Object -Expand 'value' -ErrorAction Ignore

    # Create DSC configuration archive
    if ((Test-Path $DSCSourceFolder) -and ($BuildDscPackage)) {
        $DSCSourceFilePaths = @(Get-ChildItem $DSCSourceFolder -File -Filter '*.ps1' | ForEach-Object -Process { $_.FullName })
        foreach ($DSCSourceFilePath in $DSCSourceFilePaths) {
            $DSCArchiveFilePath = $DSCSourceFilePath.Substring(0, $DSCSourceFilePath.Length - 4) + '.zip'
            Publish-AzureRmVMDscConfiguration $DSCSourceFilePath -OutputArchivePath $DSCArchiveFilePath -Force -Verbose
        }
    }

    # Create a storage account name if none was provided
    if ($StorageAccountName -eq '') {
        $StorageAccountName = 'stage' + ((Get-AzureRmContext).Subscription.Id).Replace('-', '').substring(0, 19)
    }

    $StorageAccount = (Get-AzureRmStorageAccount | Where-Object { $_.StorageAccountName -eq $StorageAccountName })

    # Create the storage account if it doesn't already exist
    if ($StorageAccount -eq $null) {
        $StorageResourceGroupName = 'ARM_Deploy_Staging'
        New-AzureRmResourceGroup -Location "$Location" -Name $StorageResourceGroupName -Force
        $StorageAccount = New-AzureRmStorageAccount -StorageAccountName $StorageAccountName -Type 'Standard_LRS' -ResourceGroupName $StorageResourceGroupName -Location "$Location"
    }

    $ArtifactStagingLocation = $StorageAccount.Context.BlobEndPoint + $StorageContainerName + "/"

    # Generate the value for artifacts location if it is not provided in the parameter file
    if ($OptionalParameters[$ArtifactsLocationName] -eq $null) {
        #if the defaultValue for _artifactsLocation is using the template location, use the defaultValue, otherwise set it to the staging location
        $defaultValue = $ArtifactsLocationParameter | Select-Object -Expand 'defaultValue' -ErrorAction Ignore
        if ($defaultValue -like '*deployment().properties.templateLink.uri*') {
            $OptionalParameters.Remove($ArtifactsLocationName)
        }
        else {
            $OptionalParameters[$ArtifactsLocationName] = $ArtifactStagingLocation
        }
    }

    # Copy files from the local storage staging location to the storage account container
    New-AzureStorageContainer -Name $StorageContainerName -Context $StorageAccount.Context -ErrorAction SilentlyContinue *>&1

    $ArtifactFilePaths = Get-ChildItem $ArtifactStagingDirectory -Recurse -File | ForEach-Object -Process { $_.FullName }
    foreach ($SourcePath in $ArtifactFilePaths) {

        if ($SourcePath -like "$DSCSourceFolder*" -and $SourcePath -like "*.zip" -or !($SourcePath -like "$DSCSourceFolder*")) {
            #When using DSC, just copy the DSC archive, not all the modules and source files
            $blobName = ($SourcePath -ireplace [regex]::Escape($ArtifactStagingDirectory), "").TrimStart("/").TrimStart("\")
            Set-AzureStorageBlobContent -File $SourcePath -Blob $blobName -Container $StorageContainerName -Context $StorageAccount.Context -Force
        }
    }
    # Generate a 4 hour SAS token for the artifacts location if one was not provided in the parameters file
    if ($OptionalParameters[$ArtifactsLocationSasTokenName] -eq $null) {
        $OptionalParameters[$ArtifactsLocationSasTokenName] = (New-AzureStorageContainerSASToken -Container $StorageContainerName -Context $StorageAccount.Context -Permission r -ExpiryTime (Get-Date).AddHours(4))
    }

    $TemplateArgs.Add('TemplateUri', $ArtifactStagingLocation + (Get-ChildItem $TemplateFile).Name + $OptionalParameters[$ArtifactsLocationSasTokenName])

    $OptionalParameters[$ArtifactsLocationSasTokenName] = ConvertTo-SecureString $OptionalParameters[$ArtifactsLocationSasTokenName] -AsPlainText -Force

}
else {

    $TemplateArgs.Add('TemplateFile', $TemplateFile)

}

$TemplateArgs.Add('TemplateParameterFile', $TemplateParametersFile)

# Create the resource group only when it doesn't already exist - and only in RG scoped deployments
if ($deploymentScope -eq "ResourceGroup") {
    if ((Get-AzureRmResourceGroup -Name $ResourceGroupName -Location $Location -Verbose -ErrorAction SilentlyContinue) -eq $null) {
        New-AzureRmResourceGroup -Name $ResourceGroupName -Location $Location -Verbose -Force -ErrorAction Stop
    }
}
if ($ValidateOnly) {
    if ($deploymentScope -eq "Subscription") {
        #subscription scoped deployment
        $ErrorMessages = Format-ValidationOutput (Test-AzureRmDeployment -Location $Location @TemplateArgs @OptionalParameters)
    }
    else {
        #resourceGroup deployment
        $ErrorMessages = Format-ValidationOutput (Test-AzureRmResourceGroupDeployment -ResourceGroupName $ResourceGroupName @TemplateArgs @OptionalParameters)
    }
    if ($ErrorMessages) {
        Write-Output '', 'Validation returned the following errors:', @($ErrorMessages), '', 'Template is invalid.'
    }
    else {
        Write-Output '', 'Template is valid.'
    }
}
else {
    if ($deploymentScope -eq "Subscription") {
        #subscription scoped deployment
        New-AzureRmDeployment -Name $DeploymentName `
            -Location $Location `
            @TemplateArgs `
            @OptionalParameters `
            -Verbose `
            -ErrorVariable ErrorMessages
    }
    else {
        New-AzureRmResourceGroupDeployment -Name $DeploymentName `
            -ResourceGroupName $ResourceGroupName `
            @TemplateArgs `
            @OptionalParameters `
            -Force -Verbose `
            -ErrorVariable ErrorMessages
    }
    if ($ErrorMessages) {
        Write-Output '', 'Template deployment returned the following errors:', @(@($ErrorMessages) | ForEach-Object { $_.Exception.Message.TrimEnd("`r`n") })
    }
}