text:
import os
import shutil
import unittest

import bpy

from math import pi
from mathutils import Euler
from mathutils import Vector
from mmd_tools.core import pmx
from mmd_tools.core.model import Model
from mmd_tools.core.pmd.importer import import_pmd_to_pmx
from mmd_tools.core.pmx.importer import PMXImporter

TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
SAMPLES_DIR = os.path.join(os.path.dirname(TESTS_DIR), 'samples')

class TestPmxExporter(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        '''
        Clean up output from previous tests
        '''
        output_dir = os.path.join(TESTS_DIR, 'output')
        for item in os.listdir(output_dir):
            if item.endswith('.OUTPUT'):
                continue  # Skip the placeholder
            item_fp = os.path.join(output_dir, item)
            if os.path.isfile(item_fp):
                os.remove(item_fp)
            elif os.path.isdir(item_fp):
                shutil.rmtree(item_fp)

    def setUp(self):
        '''
        '''
        import logging
        logger = logging.getLogger()
        logger.setLevel('ERROR')

    #********************************************
    # Utils
    #********************************************

    def __vector_error(self, vec0, vec1):
        return (Vector(vec0) - Vector(vec1)).length

    def __quaternion_error(self, quat0, quat1):
        angle = quat0.rotation_difference(quat1).angle % pi
        assert(angle >= 0)
        return min(angle, pi-angle)

    #********************************************
    # Header & Informations
    #********************************************

    def __check_pmx_header_info(self, source_model, result_model, import_types):
        '''
        Test pmx model info, header
        '''
        # Informations ================

        self.assertEqual(source_model.name, result_model.name)
        self.assertEqual(source_model.name_e, result_model.name_e)
        self.assertEqual(source_model.comment.replace('\r', ''), result_model.comment.replace('\r', ''))
        self.assertEqual(source_model.comment_e.replace('\r', ''), result_model.comment_e.replace('\r', ''))

        # Header ======================

        if source_model.header:
            source_header = source_model.header
            result_header = result_model.header
            self.assertEqual(source_header.sign, result_header.sign)
            self.assertEqual(source_header.version, result_header.version)
            self.assertEqual(source_header.encoding.index, result_header.encoding.index)
            self.assertEqual(source_header.encoding.charset, result_header.encoding.charset)
            if 'MESH' in import_types:
                self.assertEqual(source_header.additional_uvs, result_header.additional_uvs)
                self.assertEqual(source_header.vertex_index_size, result_header.vertex_index_size)
                self.assertEqual(source_header.texture_index_size, result_header.texture_index_size)
                self.assertEqual(source_header.material_index_size, result_header.material_index_size)
            if 'ARMATURE' in import_types:
                self.assertEqual(source_header.bone_index_size, result_header.bone_index_size)
            if 'MORPHS' in import_types:
                self.assertEqual(source_header.morph_index_size, result_header.morph_index_size)
            if 'PHYSICS' in import_types:
                self.assertEqual(source_header.rigid_index_size, result_header.rigid_index_size)

    #********************************************
    # Mesh
    #********************************************

    def __get_pmx_textures(self, textures):
        ret = []
        for t in textures:
            path = t.path
            path = os.path.basename(path)
            ret.append(path)
        return ret

    def __get_texture(self, tex_id, textures):
        if 0 <= tex_id < len(textures):
            return textures[tex_id]
        return tex_id

    def __get_toon_texture(self, tex_id, textures, is_shared):
        return tex_id if is_shared else self.__get_texture(tex_id, textures)

    def __check_pmx_mesh(self, source_model, result_model):
        '''
        Test pmx textures, materials, vertices, faces
        '''
        # textures ====================
        # TODO

        source_textures = self.__get_pmx_textures(source_model.textures)
        result_textures = self.__get_pmx_textures(result_model.textures)
        self.assertEqual(len(source_textures), len(result_textures))
        for tex0, tex1 in zip(sorted(source_textures), sorted(result_textures)):
            self.assertEqual(tex0, tex1)

        # materials ===================

        source_materials = source_model.materials
        result_materials = result_model.materials
        self.assertEqual(len(source_materials), len(result_materials))

        source_table = sorted(source_materials, key=lambda x: x.name)
        result_table = sorted(result_materials, key=lambda x: x.name)
        for mat0, mat1 in zip(source_table, result_table):
            msg = mat0.name
            self.assertEqual(mat0.name, mat1.name, msg)
            self.assertEqual(mat0.name_e or mat0.name, mat1.name_e, msg)
            self.assertEqual(mat0.diffuse, mat1.diffuse, msg)
            self.assertEqual(mat0.specular, mat1.specular, msg)
            self.assertEqual(mat0.shininess, mat1.shininess, msg)
            self.assertEqual(mat0.ambient, mat1.ambient, msg)
            self.assertEqual(mat0.is_double_sided, mat1.is_double_sided, msg)
            self.assertEqual(mat0.enabled_drop_shadow, mat1.enabled_drop_shadow, msg)
            self.assertEqual(mat0.enabled_self_shadow_map, mat1.enabled_self_shadow_map, msg)
            self.assertEqual(mat0.enabled_self_shadow, mat1.enabled_self_shadow, msg)
            self.assertEqual(mat0.enabled_toon_edge, mat1.enabled_toon_edge, msg)
            self.assertEqual(mat0.edge_color, mat1.edge_color, msg)
            self.assertEqual(mat0.edge_size, mat1.edge_size, msg)
            self.assertEqual(mat0.comment, mat1.comment, msg)
            self.assertEqual(mat0.vertex_count, mat1.vertex_count, msg)

            tex0 = self.__get_texture(mat0.texture, source_textures)
            tex1 = self.__get_texture(mat1.texture, result_textures)
            self.assertEqual(tex0, tex1, msg)

            self.assertEqual(mat0.sphere_texture_mode, mat1.sphere_texture_mode, msg)
            sph0 = self.__get_texture(mat0.sphere_texture, source_textures)
            sph1 = self.__get_texture(mat1.sphere_texture, result_textures)
            self.assertEqual(sph0, sph1, msg)

            self.assertEqual(mat0.is_shared_toon_texture, mat1.is_shared_toon_texture, msg)
            toon0 = self.__get_toon_texture(mat0.toon_texture, source_textures, mat0.is_shared_toon_texture)
            toon1 = self.__get_toon_texture(mat1.toon_texture, result_textures, mat1.is_shared_toon_texture)
            self.assertEqual(toon0, toon1, msg)

        # vertices & faces ============
        # TODO

        source_vertices = source_model.vertices
        result_vertices = result_model.vertices
        #self.assertEqual(len(source_vertices), len(result_vertices))

        source_faces = source_model.faces
        result_faces = result_model.faces
        self.assertEqual(len(source_faces), len(result_faces))

        for f0, f1 in zip(source_faces, result_faces):
            seq0 = [source_vertices[i] for i in f0]
            seq1 = [result_vertices[i] for i in f1]
            for v0, v1 in zip(seq0, seq1):
                self.assertLess(self.__vector_error(v0.co, v1.co), 1e-6)
                self.assertLess(self.__vector_error(v0.uv, v1.uv), 1e-6)
                #self.assertLess(self.__vector_error(v0.normal, v1.normal), 1e-3)

                self.assertEqual(v0.additional_uvs, v1.additional_uvs)
                self.assertEqual(v0.edge_scale, v1.edge_scale)
                #self.assertEqual(v0.weight.weights, v1.weight.weights)
                #self.assertEqual(v0.weight.bones, v1.weight.bones)

    #********************************************
    # Armature
    #********************************************

    def __get_bone(self, bone_id, bones):
        if bone_id is not None and 0 <= bone_id < len(bones):
            return bones[bone_id]
        return bone_id

    def __get_bone_name(self, bone_id, bones):
        if bone_id is not None and 0 <= bone_id < len(bones):
            return bones[bone_id].name
        return bone_id

    def __get_bone_display_connection(self, bone, bones):
        displayConnection = bone.displayConnection
        if displayConnection == -1 or displayConnection == [0.0, 0.0, 0.0]:
            return [0.0, 0.0, 0.0]
        if isinstance(displayConnection, int):
            tail_bone = self.__get_bone(displayConnection, bones)
            if self.__get_bone_name(tail_bone.parent, bones) == bone.name and not tail_bone.isMovable:
                return tail_bone.name
            return list(Vector(tail_bone.location) - Vector(bone.location))
        return displayConnection

    def __check_pmx_bones(self, source_model, result_model):
        '''
        Test pmx bones
        '''
        source_bones = source_model.bones
        result_bones = result_model.bones
        self.assertEqual(len(source_bones), len(result_bones))

        # check bone order
        bone_order0 = [x.name for x in source_bones]
        bone_order1 = [x.name for x in result_bones]
        self.assertEqual(bone_order0, bone_order1)

        for bone0, bone1 in zip(source_bones, result_bones):
            msg = bone0.name
            self.assertEqual(bone0.name, bone1.name, msg)
            self.assertEqual(bone0.name_e or bone0.name, bone1.name_e, msg)
            self.assertLess(self.__vector_error(bone0.location, bone1.location), 1e-6, msg)

            parent0 = self.__get_bone_name(bone0.parent, source_bones)
            parent1 = self.__get_bone_name(bone1.parent, result_bones)
            self.assertEqual(parent0, parent1, msg)

            self.assertEqual(bone0.transform_order, bone1.transform_order, msg)
            self.assertEqual(bone0.isRotatable, bone1.isRotatable, msg)
            self.assertEqual(bone0.isMovable, bone1.isMovable, msg)
            self.assertEqual(bone0.visible, bone1.visible, msg)
            self.assertEqual(bone0.isControllable, bone1.isControllable, msg)
            self.assertEqual(bone0.isIK, bone1.isIK, msg)
            self.assertEqual(bone0.transAfterPhis, bone1.transAfterPhis, msg)
            self.assertEqual(bone0.externalTransKey, bone1.externalTransKey, msg)

            self.assertEqual(bone0.axis, bone1.axis, msg)
            if bone0.localCoordinate and bone1.localCoordinate:
                self.assertEqual(bone0.localCoordinate.x_axis, bone1.localCoordinate.x_axis, msg)
                self.assertEqual(bone0.localCoordinate.z_axis, bone1.localCoordinate.z_axis, msg)
            else:
                self.assertEqual(bone0.localCoordinate, bone1.localCoordinate, msg)

            self.assertEqual(bone0.hasAdditionalRotate, bone1.hasAdditionalRotate, msg)
            self.assertEqual(bone0.hasAdditionalLocation, bone1.hasAdditionalLocation, msg)
            if bone0.additionalTransform and bone1.additionalTransform:
                at_target0, at_infl0 = bone0.additionalTransform
                at_target1, at_infl1 = bone1.additionalTransform
                at_target0 = self.__get_bone_name(at_target0, source_bones)
                at_target1 = self.__get_bone_name(at_target1, result_bones)
                self.assertEqual(at_target0, at_target1, msg)
                self.assertLess(abs(at_infl0 - at_infl1), 1e-4, msg)
            else:
                self.assertEqual(bone0.additionalTransform, bone1.additionalTransform, msg)

            target0 = self.__get_bone_name(bone0.target, source_bones)
            target1 = self.__get_bone_name(bone1.target, result_bones)
            self.assertEqual(target0, target1, msg)
            self.assertEqual(bone0.loopCount, bone1.loopCount, msg)
            self.assertEqual(bone0.rotationConstraint, bone1.rotationConstraint, msg)
            self.assertEqual(len(bone0.ik_links), len(bone1.ik_links), msg)
            for link0, link1 in zip(bone0.ik_links, bone1.ik_links):
                target0 = self.__get_bone_name(link0.target, source_bones)
                target1 = self.__get_bone_name(link1.target, result_bones)
                self.assertEqual(target0, target1, msg)

                maximumAngle0 = link0.maximumAngle
                maximumAngle1 = link1.maximumAngle
                if maximumAngle0 and maximumAngle1:
                    self.assertLess(self.__vector_error(maximumAngle0, maximumAngle1), 1e-6, msg)
                else:
                    self.assertEqual(maximumAngle0, maximumAngle1, msg)

                minimumAngle0 = link0.minimumAngle
                minimumAngle1 = link1.minimumAngle
                if minimumAngle0 and minimumAngle1:
                    self.assertLess(self.__vector_error(minimumAngle0, minimumAngle1), 1e-6, msg)
                else:
                    self.assertEqual(minimumAngle0, minimumAngle1, msg)

        for bone0, bone1 in zip(source_bones, result_bones):
            msg = bone0.name
            displayConnection0 = self.__get_bone_display_connection(bone0, source_bones)
            displayConnection1 = self.__get_bone_display_connection(bone1, result_bones)
            if isinstance(displayConnection0, list) and isinstance(displayConnection1, list):
                self.assertLess(self.__vector_error(displayConnection0, displayConnection1), 1e-4, msg)
            else:
                self.assertEqual(displayConnection0, displayConnection1, msg)

    #********************************************
    # Physics
    #********************************************

    def __get_rigid_name(self, rigid_id, rigids):
        if rigid_id is not None and 0 <= rigid_id < len(rigids):
            return rigids[rigid_id].name
        return rigid_id

    def __check_pmx_physics(self, source_model, result_model):
        '''
        Test pmx rigids, joints
        '''
        # rigids ======================

        source_rigids = source_model.rigids
        result_rigids = result_model.rigids
        self.assertEqual(len(source_rigids), len(result_rigids))

        source_bones = source_model.bones
        result_bones = result_model.bones

        source_table = sorted(source_rigids, key=lambda x: x.name)
        result_table = sorted(result_rigids, key=lambda x: x.name)
        for rigid0, rigid1 in zip(source_table, result_table):
            msg = rigid0.name
            self.assertEqual(rigid0.name, rigid1.name, msg)
            self.assertEqual(rigid0.name_e, rigid1.name_e, msg)

            bone0 = self.__get_bone_name(rigid0.bone, source_bones)
            bone1 = self.__get_bone_name(rigid0.bone, source_bones)
            self.assertEqual(bone0, bone1, msg)

            self.assertEqual(rigid0.collision_group_number, rigid1.collision_group_number, msg)
            self.assertEqual(rigid0.collision_group_mask, rigid1.collision_group_mask, msg)

            self.assertEqual(rigid0.type, rigid1.type, msg)
            if rigid0.type == 0: # SHAPE_SPHERE
                self.assertEqual(rigid0.size[0], rigid1.size[0], msg)
            elif rigid0.type == 1: # SHAPE_BOX
                self.assertEqual(rigid0.size, rigid1.size, msg)
            elif rigid0.type == 2: # SHAPE_CAPSULE
                self.assertLess(self.__vector_error(rigid0.size[0:2], rigid1.size[0:2]), 1e-6, msg)

            self.assertLess(self.__vector_error(rigid0.location, rigid1.location), 1e-6, msg)
            rigid0_rotation = Euler(rigid0.rotation,'YXZ').to_quaternion()
            rigid1_rotation = Euler(rigid1.rotation,'YXZ').to_quaternion()
            self.assertLess(self.__quaternion_error(rigid0_rotation, rigid1_rotation), 1e-6, msg)
            self.assertEqual(rigid0.mass, rigid1.mass, msg)
            self.assertEqual(rigid0.velocity_attenuation, rigid1.velocity_attenuation, msg)
            self.assertEqual(rigid0.rotation_attenuation, rigid1.rotation_attenuation, msg)
            self.assertEqual(rigid0.bounce, rigid1.bounce, msg)
            self.assertEqual(rigid0.friction, rigid1.friction, msg)
            self.assertEqual(rigid0.mode, rigid1.mode, msg)

        # joints ======================

        source_joints = source_model.joints
        result_joints = result_model.joints
        self.assertEqual(len(source_joints), len(result_joints))

        source_table = sorted(source_joints, key=lambda x: x.name)
        result_table = sorted(result_joints, key=lambda x: x.name)
        for joint0, joint1 in zip(source_table, result_table):
            msg = joint0.name
            self.assertEqual(joint0.name, joint1.name, msg)
            self.assertEqual(joint0.name_e, joint1.name_e, msg)
            self.assertEqual(joint0.mode, joint1.mode, msg)

            src_rigid0 = self.__get_rigid_name(joint0.src_rigid, source_rigids)
            src_rigid1 = self.__get_rigid_name(joint1.src_rigid, result_rigids)
            self.assertEqual(src_rigid0, src_rigid1, msg)

            dest_rigid0 = self.__get_rigid_name(joint0.dest_rigid, source_rigids)
            dest_rigid1 = self.__get_rigid_name(joint1.dest_rigid, result_rigids)
            self.assertEqual(dest_rigid0, dest_rigid1, msg)

            self.assertEqual(joint0.location, joint1.location, msg)
            joint0_rotation = Euler(joint0.rotation,'YXZ').to_quaternion()
            joint1_rotation = Euler(joint1.rotation,'YXZ').to_quaternion()
            self.assertLess(self.__quaternion_error(joint0_rotation, joint1_rotation), 1e-6, msg)
            self.assertEqual(joint0.maximum_location, joint1.maximum_location, msg)
            self.assertEqual(joint0.minimum_location, joint1.minimum_location, msg)
            self.assertEqual(joint0.maximum_rotation, joint1.maximum_rotation, msg)
            self.assertEqual(joint0.minimum_rotation, joint1.minimum_rotation, msg)
            self.assertEqual(joint0.spring_constant, joint1.spring_constant, msg)
            self.assertEqual(joint0.spring_rotation_constant, joint1.spring_rotation_constant, msg)

    #********************************************
    # Morphs
    #********************************************
    def __get_material(self, index, materials):
        if 0 <= index < len(materials):
            return materials[index]
        class _dummy:
            name = None
        return _dummy

    def __check_pmx_morphs(self, source_model, result_model):
        '''
        Test pmx morphs
        '''
        source_morphs = source_model.morphs
        result_morphs = result_model.morphs
        self.assertEqual(len(source_morphs), len(result_morphs))

        source_table = {}
        for m in source_morphs:
            source_table.setdefault(type(m), []).append(m)
        result_table = {}
        for m in result_morphs:
            result_table.setdefault(type(m), []).append(m)

        self.assertEqual(source_table.keys(), result_table.keys(), 'types mismatch')

        #source_vertices = source_model.vertices
        #result_vertices = result_model.vertices

        # VertexMorph =================
        # TODO

        source = source_table.get(pmx.VertexMorph, [])
        result = result_table.get(pmx.VertexMorph, [])
        self.assertEqual(len(source), len(result))
        for m0, m1 in zip(source, result):
            msg = 'VertexMorph %s'%m0.name
            self.assertEqual(m0.name, m1.name, msg)
            self.assertEqual(m0.name_e, m1.name_e, msg)
            self.assertEqual(m0.category, m1.category, msg)
            #self.assertEqual(len(m0.offsets), len(m1.offsets), msg)

        # UVMorph =====================
        # TODO

        source = source_table.get(pmx.UVMorph, [])
        result = result_table.get(pmx.UVMorph, [])
        self.assertEqual(len(source), len(result))
        for m0, m1 in zip(source, result):
            msg = 'UVMorph %s'%m0.name
            self.assertEqual(m0.name, m1.name, msg)
            self.assertEqual(m0.name_e, m1.name_e, msg)
            self.assertEqual(m0.category, m1.category, msg)
            self.assertEqual(len(m0.offsets), len(m1.offsets), msg)
            #for s0, s1 in zip(m0.offsets, m1.offsets):
            #    self.assertEqual(s0.index, s1.index, msg)
            #    self.assertEqual(s0.offset, s1.offset, msg)

        # BoneMorph ===================

        source_bones = source_model.bones
        result_bones = result_model.bones

        source = source_table.get(pmx.BoneMorph, [])
        result = result_table.get(pmx.BoneMorph, [])
        self.assertEqual(len(source), len(result))
        for m0, m1 in zip(source, result):
            msg = 'BoneMorph %s'%m0.name
            self.assertEqual(m0.name, m1.name, msg)
            self.assertEqual(m0.name_e, m1.name_e, msg)
            self.assertEqual(m0.category, m1.category, msg)
            # the source may contains invalid data
            source_offsets = [m for m in m0.offsets if 0 <= m.index < len(source_bones)]
            result_offsets = m1.offsets
            self.assertEqual(len(source_offsets), len(result_offsets), msg)
            for s0, s1 in zip(source_offsets, result_offsets):
                bone0 = source_bones[s0.index]
                bone1 = result_bones[s1.index]
                self.assertEqual(bone0.name, bone1.name, msg)
                self.assertLess(self.__vector_error(s0.location_offset, s1.location_offset), 1e-5, msg)
                self.assertLess(self.__vector_error(s0.rotation_offset, s1.rotation_offset), 1e-5, msg)

        # MaterialMorph ===============

        source_materials = source_model.materials
        result_materials = result_model.materials

        source = source_table.get(pmx.MaterialMorph, [])
        result = result_table.get(pmx.MaterialMorph, [])
        self.assertEqual(len(source), len(result))
        for m0, m1 in zip(source, result):
            msg = 'MaterialMorph %s'%m0.name
            self.assertEqual(m0.name, m1.name, msg)
            self.assertEqual(m0.name_e, m1.name_e, msg)
            self.assertEqual(m0.category, m1.category, msg)
            source_offsets = m0.offsets
            result_offsets = m1.offsets
            self.assertEqual(len(source_offsets), len(result_offsets), msg)
            for s0, s1 in zip(source_offsets, result_offsets):
                mat0 = self.__get_material(s0.index, source_materials)
                mat1 = self.__get_material(s1.index, result_materials)
                self.assertEqual(mat0.name, mat1.name, msg)
                self.assertEqual(s0.offset_type, s1.offset_type, msg)
                self.assertEqual(s0.diffuse_offset, s1.diffuse_offset, msg)
                self.assertEqual(s0.specular_offset, s1.specular_offset, msg)
                self.assertEqual(s0.shininess_offset, s1.shininess_offset, msg)
                self.assertEqual(s0.ambient_offset, s1.ambient_offset, msg)
                self.assertEqual(s0.edge_color_offset, s1.edge_color_offset, msg)
                self.assertEqual(s0.edge_size_offset, s1.edge_size_offset, msg)
                self.assertEqual(s0.texture_factor, s1.texture_factor, msg)
                self.assertEqual(s0.sphere_texture_factor, s1.sphere_texture_factor, msg)
                self.assertEqual(s0.toon_texture_factor, s1.toon_texture_factor, msg)

        # GroupMorph ==================

        source = source_table.get(pmx.GroupMorph, [])
        result = result_table.get(pmx.GroupMorph, [])
        self.assertEqual(len(source), len(result))
        for m0, m1 in zip(source, result):
            msg = 'GroupMorph %s'%m0.name
            self.assertEqual(m0.name, m1.name, msg)
            self.assertEqual(m0.name_e, m1.name_e, msg)
            self.assertEqual(m0.category, m1.category, msg)
            # the source may contains invalid data
            source_offsets = [m for m in m0.offsets if 0 <= m.morph < len(source_morphs)]
            result_offsets = m1.offsets
            self.assertEqual(len(source_offsets), len(result_offsets), msg)
            for s0, s1 in zip(source_offsets, result_offsets):
                morph0 = source_morphs[s0.morph]
                morph1 = result_morphs[s1.morph]
                self.assertEqual(morph0.name, morph1.name, msg)
                self.assertEqual(morph0.category, morph1.category, msg)
                self.assertEqual(s0.factor, s1.factor, msg)

    #********************************************
    # Display
    #********************************************

    def __check_pmx_display_data(self, source_model, result_model, check_morphs):
        '''
        Test pmx display
        '''
        source_display = source_model.display
        result_display = result_model.display
        self.assertEqual(len(source_display), len(result_display))

        for source, result in zip(source_display, result_display):
            self.assertEqual(source.name, result.name)
            self.assertEqual(source.name_e, result.name_e)
            self.assertEqual(source.isSpecial, result.isSpecial)

            source_items = source.data
            if not check_morphs:
                source_items = [i for i in source_items if i[0] == 0]
            result_items = result.data

            self.assertEqual(len(source_items), len(result_items))
            for item0, item1 in zip(source_items, result_items):
                disp_type0, index0 = item0
                disp_type1, index1 = item1
                self.assertEqual(disp_type0, disp_type1)
                if disp_type0 == 0:
                    bone_name0 = source_model.bones[index0].name
                    bone_name1 = result_model.bones[index1].name
                    self.assertEqual(bone_name0, bone_name1)
                elif disp_type0 == 1:
                    morph0 = source_model.morphs[index0]
                    morph1 = result_model.morphs[index1]
                    self.assertEqual(morph0.name, morph1.name)
                    self.assertEqual(morph0.category, morph1.category)

    #********************************************
    # Test Function
    #********************************************

    def __get_import_types(self, types):
        types = types.copy()
        if 'PHYSICS' in types:
            types.add('ARMATURE')
        if 'DISPLAY' in types:
            types.add('ARMATURE')
        if 'MORPHS' in types:
            types.add('ARMATURE')
            types.add('MESH')
        return types

    def __list_sample_files(self, file_types):
        ret = []
        for file_type in file_types:
            file_ext ='.' + file_type
            for root, dirs, files in os.walk(os.path.join(SAMPLES_DIR, file_type)):
                for name in files:
                    if name.lower().endswith(file_ext):
                        ret.append(os.path.join(root, name))
        return ret

    def test_pmx_exporter(self):
        '''
        '''
        input_files = self.__list_sample_files(('pmd', 'pmx'))
        if len(input_files) < 1:
            self.fail('required pmd/pmx sample file(s)!')

        check_types = set()
        check_types.add('MESH')
        check_types.add('ARMATURE')
        check_types.add('PHYSICS')
        check_types.add('MORPHS')
        check_types.add('DISPLAY')

        import_types = self.__get_import_types(check_types)

        print('\n    Check: %s | Import: %s'%(str(check_types), str(import_types)))

        for test_num, filepath in enumerate(input_files):
            print('\n     - %2d/%d | filepath: %s'%(test_num+1, len(input_files), filepath))
            try:
                bpy.ops.wm.read_homefile() # reload blender startup file
                if not bpy.context.user_preferences.addons.get('mmd_tools', None):
                    bpy.ops.wm.addon_enable(module='mmd_tools') # make sure addon 'mmd_tools' is enabled

                file_loader = pmx.load
                if filepath.lower().endswith('.pmd'):
                    file_loader = import_pmd_to_pmx
                source_model = file_loader(filepath)
                PMXImporter().execute(
                    pmx=source_model,
                    types=import_types,
                    scale=1,
                    clean_model=False,
                    renameBones=False,
                    )
                bpy.context.scene.update()
            except Exception:
                self.fail('Exception happened during import %s'%filepath)
            else:
                try:
                    output_pmx = os.path.join(TESTS_DIR, 'output', '%d.pmx'%test_num)
                    bpy.ops.mmd_tools.export_pmx(
                        filepath=output_pmx,
                        copy_textures=False,
                        sort_materials=False,
                        log_level='ERROR',
                        )
                except Exception:
                    self.fail('Exception happened during export %s'%output_pmx)
                else:
                    self.assertTrue(os.path.isfile(output_pmx), 'File was not created')  # Is this a race condition?

                    try:
                        result_model = pmx.load(output_pmx)
                    except:
                        self.fail('Failed to load output file %s'%output_pmx)

                    self.__check_pmx_header_info(source_model, result_model, import_types)

                    if 'MESH' in check_types:
                        self.__check_pmx_mesh(source_model, result_model)

                    if 'ARMATURE' in check_types:
                        self.__check_pmx_bones(source_model, result_model)

                    if 'PHYSICS' in check_types:
                        self.__check_pmx_physics(source_model, result_model)

                    if 'MORPHS' in check_types:
                        self.__check_pmx_morphs(source_model, result_model)

                    if 'DISPLAY' in check_types:
                        self.__check_pmx_display_data(source_model, result_model, 'MORPHS' in check_types)

if __name__ == '__main__':
    import sys
    sys.argv = [__file__] + (sys.argv[sys.argv.index('--') + 1:] if '--' in sys.argv else [])
    unittest.main()


text:
class MonsterShip : public xpcc::Protothread
{
public:
	MonsterShip(xpcc::glcd::Point start, xpcc::GraphicDisplay *display);

	void
	draw();

protected:
	bool
	run();

	xpcc::glcd::Point position;
	xpcc::GraphicDisplay *display;
	xpcc::Timeout<> timer;

	uint8_t i;
	bool visible;
	bool hidden;
};

#endif // MONSTER_SHIP_HPP

redpajama_set_name: RedPajamaGithub


text:
/****************************************************************************************/
/*************** [COLOR CODES] DARK PURPLE:#382F44 LIGHT PURPLE:#5B4C7C *****************/
/****************************************************************************************/

WalletFrame {
border-image: url(':/images/walletFrame_bg') 0 0 0 0 stretch stretch;
border-top:0px solid #000;
margin:0;
padding:0;
}

QStatusBar {
background-color:#ffffff;
}

.QFrame {
background-color:transparent;
border:0px solid #fff;
}

QMenuBar {
background-color:#fff;
}

QMenuBar::item {
background-color:#fff;
color:#333;
}

QMenuBar::item:selected {
background-color:#f8f6f6;
}

QMenu {
background-color:#f8f6f6;
}

QMenu::item {
color:#333;
}

QMenu::item:selected {
background-color:#f2f0f0;
color:#333;
}

QToolBar {
background-color:#382F44;
border:0px solid #000;
padding:0;
margin:0;
}

QToolBar > QToolButton {
background-color:#382F44;
border:0px solid #333;
min-height:2.5em;
padding: 0em 1em;
font-weight:bold;
color:#fff;
}

QToolBar > QToolButton:checked {
background-color:#fff;
color:#333;
font-weight:bold;
}

QMessageBox {
background-color:#F8F6F6;
}

QProgressBar {
color:#d2d2d2;
border:2px solid grey;
border-radius:5px;
background-color:transparent;
}

QProgressBar::chunk {
background-color:#5B4C7C;
width: 20px;
}

QTabWidget {
background-color:#f2f2f2;
}

QTabWidget::pane {
background-color:#f2f2f2;
border:1px solid #c2c2c2;
}

QTabBar {
background-color:#f2f2f2;
color:#333;
}

QTabBar::tab:hover:!selected {
background-color:#dedbe5;
}

QWidget {
selection-background-color:#5B4C7C; /* Object highlight color */
selection-color:#fff;
outline:0; /* Remove Annoying Focus Rectangle */
}

QGroupBox {
background-color:#fcfcfc;
color:#333;
}

QToolTip {
background-color:#dedbe5;
color:#333;
}

QTextEdit {
background-color:#fff;
color:#333;
}

/****************************************************************************************/

QLabel { /* Base Text Size & Color */
font-size:12px;
color:#333333;
}

.QRadioButton { /* Radio Button Labels */
color:#333333;
}

.QCheckBox { /* Checkbox Labels */
color:#333333;
background-color:transparent;
}

.QCheckBox:hover {
background-color:transparent;
}

.QValidatedLineEdit, .QLineEdit { /* Text Entry Fields */
border: 1px solid #5B4C7C;
font-size:11px;
min-height:25px;
outline:0;
padding:3px;
background-color:#fcfcfc;
color:#333333;
}

.QLineEdit:!focus {
font-size:12px;
}

.QValidatedLineEdit:disabled, .QLineEdit:disabled {
background-color:#f2f2f2;
}

/****************************************************************************************/

QPushButton { /* Global Button Style */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #64ACD2, stop: .1 #382f44, stop: .95 #382f44, stop: 1 #1D80B5);
border:0;
border-radius:3px;
color:#ffffff;
font-size:12px;
font-weight:bold;
padding-left:25px;
padding-right:25px;
padding-top:5px;
padding-bottom:5px;
}

QPushButton:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #5B4C9A, stop: .1 #5B4C86, stop: .95 #5B4C86, stop: 1 #5B4C7C);
}

QPushButton:focus {
border:none;
outline:none;
}

QPushButton:pressed {
border:1px solid #333;
}

QComboBox { /* Dropdown Menus */
border:1px solid #5B4C7C;
padding: 3px 5px 3px 5px;
background:#fcfcfc;
min-height:25px;
color:#818181;
}

QComboBox:checked {
background:#f2f2f2;
}

QComboBox:editable {
background:#382f44;
color:#616161;
border:0px solid transparent;
}

QComboBox::drop-down {
width:25px;
border:0px;
}

QComboBox::down-arrow {
border-image: url(':/images/downArrow') 0 0 0 0 stretch stretch;
}

QComboBox QListView {
background:#fff;
border:1px solid #333;
padding-right:1px;
padding-left:1px;
color:#818181;
}

QComboBox QAbstractItemView::item {
margin:4px;
}

QComboBox::item {
color:#818181;
}

QComboBox::item:alternate {
background:#fff;
}

QComboBox::item:selected {
border:0px solid transparent;
background:#f2f2f2;
}

QComboBox::indicator {
background-color:transparent;
selection-background-color:transparent;
color:transparent;
selection-color:transparent;
}

QAbstractSpinBox {
border:1px solid #5B4C7C;
padding: 3px 5px 3px 5px;
background:#fcfcfc;
min-height:25px;
color:#818181;
}

QAbstractSpinBox::up-button {
subcontrol-origin: border;
subcontrol-position: top right;
width:21px;
background:#fcfcfc;
border-left:0px;
border-right:1px solid #5B4C7C;
border-top:1px solid #5B4C7C;
border-bottom:0px;
padding-right:1px;
padding-left:5px;
padding-top:2px;
}

QAbstractSpinBox::up-arrow {
image:url(':/images/upArrow_small');
}

QAbstractSpinBox::down-button {
subcontrol-origin: border;
subcontrol-position: bottom right;
width:21px;
background:#fcfcfc;
border-top:0px;
border-left:0px;
border-right:1px solid #5B4C7C;
border-bottom:1px solid #5B4C7C;
padding-right:1px;
padding-left:5px;
padding-bottom:2px;
}

QAbstractSpinBox::down-arrow {
image:url(':/images/downArrow_small');
}

/****************************************************************************************/

QHeaderView { /* Table Header */
background-color:transparent;
outline:0;
}

QHeaderView::section { /* Table Header Sections */
qproperty-alignment:center;
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #382F44, stop: 1 #5B4C7C);
color:#fff;
min-height:25px;
font-weight:bold;
font-size:11px;
border:0;
border-right:1px solid #fff;
padding-left:5px;
padding-right:5px;
padding-top:2px;
padding-bottom:2px;
}

QHeaderView::section:last {
border-right:0;
}

.QScrollArea {
background:transparent;
border:0;
}

.QTableView { /* Table - has to be selected as a class otherwise it throws off QCalendarWidget */
background:transparent;
border:1px solid #333;
}

QTableView::item { /* Table Item */
background-color:#fcfcfc;
font-size:12px;
}

QTableView::item:selected { /* Table Item Selected */
background-color:#5B4C7C;
color:#fff;
}

QTableWidget { /* Table Background */
background-color:red; /*#fcfcfc;*/
border:1px solid #333;
}

QTableWidget:focus, QTableView, QTableView:focus { /* Remove focus outline */
outline:0;
background:transparent;
}

QTreeWidget { /* Tree Background */
background-color:#fcfcfc;
alternate-background-color:#f2f2f2;
color:#333;
}

QScrollBar { /* Scroll Bar */

}

QScrollBar:vertical { /* Vertical Scroll Bar Attributes */
border:0;
background:#ffffff;
width:18px;
margin:18px 0px 18px 0px;
}

QScrollBar:horizontal { /* Horizontal Scroll Bar Attributes */
border:0;
background:#ffffff;
height:18px;
margin:0px 18px 0px 18px;
}

QScrollBar::handle:vertical { /* Scroll Bar Slider - vertical */
background:#e0e0e0;
min-height:10px;
}

QScrollBar::handle:horizontal { /* Scroll Bar Slider - horizontal */
background:#e0e0e0;
min-width:10px;
}

QScrollBar::add-page, QScrollBar::sub-page { /* Scroll Bar Background */
background:#F8F6F6;
}

QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical, QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { /* Define Arrow Button Dimensions */
background-color:#F8F6F6;
border: 1px solid #f2f0f0;
width:16px;
height:16px;
}

QScrollBar::add-line:vertical:pressed, QScrollBar::sub-line:vertical:pressed, QScrollBar::add-line:horizontal:pressed, QScrollBar::sub-line:horizontal:pressed {
background-color:#e0e0e0;
}

QScrollBar::sub-line:vertical { /* Vertical - top button position */
subcontrol-position:top;
subcontrol-origin: margin;
}

QScrollBar::add-line:vertical { /* Vertical - bottom button position */
subcontrol-position:bottom;
subcontrol-origin: margin;
}

QScrollBar::sub-line:horizontal { /* Vertical - left button position */
subcontrol-position:left;
subcontrol-origin: margin;
}

QScrollBar::add-line:horizontal { /* Vertical - right button position */
subcontrol-position:right;
subcontrol-origin: margin;
}

QScrollBar:up-arrow, QScrollBar:down-arrow, QScrollBar:left-arrow, QScrollBar:right-arrow { /* Arrows Icon */
width:10px;
height:10px;
}

QScrollBar:up-arrow {
background-image: url(':/images/upArrow_small');
}

QScrollBar:down-arrow {
background-image: url(':/images/downArrow_small');
}

QScrollBar:left-arrow {
background-image: url(':/images/leftArrow_small');
}

QScrollBar:right-arrow {
background-image: url(':/images/rightArrow_small');
}

QSlider::handle:horizontal {
background-color:#f2f2f2;
border:1px solid #c2c2c2;
border-radius:10px;
}

QSlider::sub-page:horizontal {
background-color:#5B4C7C;
border-color:#c2c2c2;
border-radius:3px;
}

QSlider::add-page:horizontal {
background-color:#dedede;
border:1px solid #c2c2c2;
border-radius:3px;
}

/**************************** DIALOG BOXES **********************************************/

QDialog {
background-color: #f8f6f6;
}

QDialog .QTabWidget {
border-bottom:1px solid #333;
}

QDialog .QTabWidget::pane {
border: 1px solid #d7d7d7;
}

QDialog .QTabWidget QTabBar::tab {
background-color:#f2f0f0;
color:#333;
padding-left:10px;
padding-right:10px;
padding-top:5px;
padding-bottom:5px;
border-top: 1px solid #d7d7d7;
}

QDialog .QTabWidget QTabBar::tab:first {
border-left: 1px solid #d7d7d7;
}

QDialog .QTabWidget QTabBar::tab:last {
border-right: 1px solid #d7d7d7;
}

QDialog .QTabWidget QTabBar::tab:selected, QDialog .QTabWidget QTabBar::tab:hover {
background-color:#ffffff;
color:#333;
}

QDialog .QTabWidget .QWidget {
background-color:#fff;
color:#333;
}

QDialog .QTabWidget .QWidget QAbstractSpinBox {
min-height:15px;
}

QDialog .QTabWidget .QWidget QAbstractSpinBox::down-button {
width:15px;
}

QDialog .QTabWidget .QWidget QAbstractSpinBox::up-button {
width:15px;
}

QDialog .QTabWidget .QWidget QComboBox {
min-height:15px;
}

/**************************** FILE MENU *************************************************/

/* Dialog: Open URI */
QDialog#OpenURIDialog QLabel#label { /* URI Label */
font-weight:bold;
}

QDialog#OpenURIDialog QPushButton#selectFileButton { /* ... Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QDialog#OpenURIDialog QPushButton#selectFileButton:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QDialog#OpenURIDialog QPushButton#selectFileButton:pressed {
border:1px solid #9e9e9e;
}

/**************************** SIGN/ VERIFY MESSAGE DIALOG *******************************/

QDialog#SignVerifyMessageDialog QPushButton#addressBookButton_SM { /* Address Book Button */
background-color:transparent;
padding-left:10px;
padding-right:10px;
}

QDialog#SignVerifyMessageDialog QPlainTextEdit { /* Message Signing Text */
border:1px solid #5B4C7C;
background-color:#fff;
}

QDialog#SignVerifyMessageDialog QPushButton#pasteButton_SM { /* Paste Button */
/* qproperty-icon: url(":/icons/editpaste"); */
background-color:transparent;
padding-left:15px;
}

QDialog#SignVerifyMessageDialog QLineEdit:!focus { /* Font Hack */
font-size:10px;
}

QDialog#SignVerifyMessageDialog QPushButton#copySignatureButton_SM { /* Copy Button */
/* qproperty-icon: url(":/icons/editcopy"); */
background-color:transparent;
padding-left:10px;
padding-right:10px;
}

QDialog#SignVerifyMessageDialog QPushButton#clearButton_SM { /* Clear Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QDialog#SignVerifyMessageDialog QPushButton#clearButton_SM:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QDialog#SignVerifyMessageDialog QPushButton#clearButton_SM:pressed {
border:1px solid #9e9e9e;
}

QDialog#SignVerifyMessageDialog QPushButton#addressBookButton_VM { /* Verify Message - Address Book Button */
background-color:transparent;
border:0;
padding-left:10px;
padding-right:10px;
}

QDialog#SignVerifyMessageDialog QPushButton#clearButton_VM { /* Verify Message - Clear Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QDialog#SignVerifyMessageDialog QPushButton#clearButton_VM:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QDialog#SignVerifyMessageDialog QPushButton#clearButton_VM:pressed {
border:1px solid #9e9e9e;
}

/**************************** SEND AND RECEIVE DIALOG ***********************************/

QWidget#AddressBookPage {
background-color:#F8F6F6;
}

QWidget#AddressBookPage QPushButton#newAddress { /* New Address Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QWidget#AddressBookPage QPushButton#newAddress:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QWidget#AddressBookPage QPushButton#newAddress:pressed {
border:1px solid #9e9e9e;
}

QWidget#AddressBookPage QPushButton#copyAddress { /* Copy Address Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QWidget#AddressBookPage QPushButton#copyAddress:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QWidget#AddressBookPage QPushButton#copyAddress:pressed {
border:1px solid #9e9e9e;
}

QWidget#AddressBookPage QPushButton#deleteAddress { /* Delete Address Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QWidget#AddressBookPage QPushButton#deleteAddress:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QWidget#AddressBookPage QPushButton#deleteAddress:pressed {
border:1px solid #9e9e9e;
}

QWidget#AddressBookPage QTableView { /* Address Listing */
font-size:12px;
}

QWidget#AddressBookPage QHeaderView::section { /* Min width for Windows fix */
min-width:260px;
}

/**************************** Privacy Dialog ********************************************/

QWidget#PrivacyDialog QFrame {
background-color:transparent;
border:1px solid #000;
}

QWidget#PrivacyDialog QFrame#labelMintStatus {
/* background-color:transparent; */
background-color:#eee;
/* border-style: inset; */
/* border-width:2; */
/* border:2px solid #00; */
border: 1px inset gray;
}

QWidget#PrivacyDialog QLabel {
border:0px solid #000;
}

QWidget#PrivacyDialog QLabel#zPIVLabel {
font-size:14px;
color:#ffffff;
background-color:#5B4C7C;
}

QWidget#PrivacyDialog QLabel#oPIVLabel {
font-size:14px;
color:#ffffff;
background-color:#5B4C7C;
}

QWidget#PrivacyDialog QPushButton {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #64ACD2, stop: .1 #382f44, stop: .95 #382f44, stop: 1 #1D80B5);
border:0;
border-radius:3px;
color:#ffffff;
font-size:12px;
font-weight:normal;
padding-left:5px;
padding-right:5px;
padding-top:5px;
padding-bottom:5px;
}

QWidget#PrivacyDialog QPushButton:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #5B4C9A, stop: .1 #5B4C86, stop: .95 #5B4C86, stop: 1 #5B4C7C);
}

QWidget#PrivacyDialog QPushButton:focus {
border:none;
outline:none;
}

QWidget#PrivacyDialog QPushButton:pressed {
border:1px solid #333;
}

QWidget#PrivacyDialog QProgressBar#obfuscationProgress { /* Obfuscation Completion */
border: 1px solid #818181;
border-radius: 1px;
margin-right:43px;
text-align: right;
color:#818181;
}

QWidget#PrivacyDialog QProgressBar#obfuscationProgress::chunk {
background-color: #382f44;
width:1px;
}

/**************************** SETTINGS MENU *********************************************/

/* Encrypt Wallet and Change Passphrase Dialog */
QDialog#AskPassphraseDialog QLabel#passLabel1, QDialog#AskPassphraseDialog QLabel#passLabel2, QDialog#AskPassphraseDialog QLabel#passLabel3 {
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:170px;
min-height:33px; /* base width of 25px for QLineEdit, plus padding and border */
}

/* Options Dialog */
QDialog#OptionsDialog QValueComboBox, QDialog#OptionsDialog QSpinBox {
margin-top:5px;
margin-bottom:5px;
}

QDialog#OptionsDialog QValidatedLineEdit, QDialog#OptionsDialog QValidatedLineEdit:disabled, QDialog#OptionsDialog QLineEdit, QDialog#OptionsDialog QLineEdit:disabled {
qproperty-alignment: 'AlignVCenter | AlignLeft';
min-height:20px;
margin-top:0px;
margin-bottom:0px;
padding-top:1px;
padding-bottom:1px;
}

QDialog#OptionsDialog > QLabel {
qproperty-alignment: 'AlignVCenter';
min-height:20px;
}

QDialog#OptionsDialog QWidget#tabDisplay QValueComboBox {
margin-top:0px;
margin-bottom:0px;
}

QDialog#OptionsDialog QLabel#label_3 { /* Translations Missing? Label */
qproperty-alignment: 'AlignVCenter | AlignCenter';
color:#818181;
padding-bottom:8px;
}

QDialog#OptionsDialog QCheckBox {
min-height:20px;
}

QDialog#OptionsDialog QCheckBox#displayAddresses {
min-height:33px;
}

/**************************** TOOLS MENU ************************************************/

QDialog#RPCConsole QWidget#tab_info QLabel#label_11, QDialog#RPCConsole QWidget#tab_info QLabel#label_10 { /* Margin between Network and Block Chain headers */
qproperty-alignment: 'AlignBottom';
min-height:25px;
min-width:180px;
}

QDialog#RPCConsole QWidget#tab_peers QLabel#peerHeading { /* Peers Info Header */
color:#382f44;
}

QDialog#RPCConsole QTableView#peerWidget::item { /* Peers Table Items */
color:#333;
}

QDialog#RPCConsole QPushButton#openDebugLogfileButton {
max-width:60px;
}

QDialog#RPCConsole QTextEdit#messagesWidget { /* Console Messages Window */
border:0;
background-color:#fcfcfc;
color:#333;
}

QDialog#RPCConsole QLineEdit#lineEdit { /* Console Input */
margin-right:5px;
}

QDialog#RPCConsole QPushButton#clearButton { /* Console Clear Button */
background-color:transparent;
padding-left:10px;
padding-right:10px;
}

QDialog#RPCConsole .QGroupBox #line { /* Network In Line */
background-color:#5B4C7C;
}

QDialog#RPCConsole .QGroupBox #line_2 { /* Network Out Line */
background:#fff;
}

/**************************** HELP MENU *************************************************/

/* Command Line Options Dialog */
QDialog#HelpMessageDialog QScrollArea * {
background-color:#fff;
}

QDialog#HelpMessageDialog QScrollBar:vertical, QDialog#HelpMessageDialog QScrollBar:horizontal {
border:0;
}

/* About Pivx Dialog */
QDialog#AboutDialog QLabel#label, QDialog#AboutDialog QLabel#copyrightLabel, QDialog#AboutDialog QLabel#label_2 { /* About Pivx Contents */
margin-left:10px;
}

QDialog#AboutDialog QLabel#label_2 { /* Margin for About Pivx text */
margin-right:10px;
}

/* Edit Address Dialog */
QDialog#EditAddressDialog QLabel {
qproperty-alignment: 'AlignVCenter | AlignRight';
min-height:27px;
font-weight:normal;
padding-right:5px;
}

/**************************** OVERVIEW SCREEN *******************************************/

QWidget .QFrame#frame { /* Wallet Balance */
min-width:490px;
}

QWidget .QFrame#frame > .QLabel {
min-width:190px;
font-weight:normal;
min-height:30px;
}

QWidget .QFrame#frame .QLabel#label_5 { /* PIV Balances Label */
qproperty-alignment: 'AlignVCenter | AlignLeft';
min-width:160px;
background-color:transparent;
color:#382F44;
font-weight:bold;
font-size:14px;
margin-right:5px;
padding-right:5px;
}

QWidget .QFrame#frame .QLabel#labelWalletStatus { /* Wallet Sync Status */
qproperty-alignment: 'AlignVCenter | AlignLeft';
margin-left:3px;
}

QWidget .QFrame#frame .QLabel#labelSpendable { /* Spendable Header */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
margin-left:18px;
}

QWidget .QFrame#frame .QLabel#labelWatchonly { /* Watch-only Header */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
margin-left:16px;
}

QWidget .QFrame#frame .QLabel#labelBalanceText { /* Available Balance Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
min-height:35px;
}

QWidget .QFrame#frame .QLabel#labelBalance { /* Available Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:bold;
color:#5B4C7C;
margin-left:0px;
}

QWidget .QFrame#frame .QLabel#labelWatchAvailable { /* Watch-only Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
margin-left:16px;
}

QWidget .QFrame#frame .QLabel#labelPendingText { /* Pending Balance Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
min-height:35px;
}

QWidget .QFrame#frame .QLabel#labelUnconfirmed { /* Pending Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
margin-left:0px;
}

QWidget .QFrame#frame .QLabel#labelWatchPending { /* Watch-only Pending Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
margin-left:16px;
}

QWidget .QFrame#frame .QLabel#labelImmatureText { /* Immature Balance Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
min-height:35px;
}

QWidget .QFrame#frame .QLabel#labelImmature { /* Immature Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
margin-left:0px;
}

QWidget .QFrame#frame .QLabel#labelWatchImmature { /* Watch-only Immature Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
margin-left:16px;
}

QWidget .QFrame#frame .QLabel#labelTotalText { /* Total Balance Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
min-height:35px;
}

QWidget .QFrame#frame .QLabel#labelTotal { /* Total Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
margin-left:0px;
}

QWidget .QFrame#frame .QLabel#labelWatchTotal { /* Watch-only Total Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
margin-left:16px;
}

QWidget .QFrame#frame_4 .QLabel#label_5z_3 { /* Zerocoin Balance Label */
qproperty-alignment: 'AlignVCenter | AlignLeft';
min-width:160px;
background-color:transparent;
color:#382F44;
font-weight:bold;
font-size:14px;
margin-right:5px;
padding-right:5px;
}

QWidget .QFrame#frame_4 .QLabel#labelzBalanceText { /* Available zPIV Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
/* min-height:35px; */
}

QWidget .QFrame#frame_4 .QLabel#labelzBalance { /* Available zPIV Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:bold;
color:#5B4C7C;
margin-left:0px;
}

QWidget .QFrame#frame_4 .QLabel#labelzBalanceUnconfirmedText { /* Unconfirmed zPIV Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
/* min-height:35px; */
}

QWidget .QFrame#frame_4 .QLabel#labelzBalanceUnconfirmed { /* Unconfirmed zPIV Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:bold;
color:#5B4C7C;
margin-left:0px;
}

QWidget .QFrame#frame_4 .QLabel#labelzBalanceMatureText { /* Mature zPIV Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
/* min-height:35px; */
}

QWidget .QFrame#frame_4 .QLabel#labelzBalanceMature { /* Mature zPIV Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:bold;
color:#5B4C7C;
margin-left:0px;
}

QWidget .QFrame#frame_4 .QLabel#labelzBalanceImmatureText { /* Immature zPIV Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
/* min-height:35px; */
}

QWidget .QFrame#frame_4 .QLabel#labelzBalanceImmature { /* Immature zPIV Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:bold;
color:#5B4C7C;
margin-left:0px;
}

QWidget .QFrame#frame_3 .QLabel#label_5z { /* Combined Balance Label */
qproperty-alignment: 'AlignVCenter | AlignLeft';
min-width:160px;
background-color:transparent;
color:#382F44;
font-weight:bold;
font-size:14px;
margin-right:5px;
padding-right:5px;
}

QWidget .QFrame#frame_3 .QLabel#labelBalanceTextz { /* Available PIV Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
/* min-height:35px; */
}

QWidget .QFrame#frame_3 .QLabel#labelBalancez { /* Available PIV Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:bold;
color:#5B4C7C;
margin-left:0px;
}

QWidget .QFrame#frame_3 .QLabel#labelzBalanceTextz { /* Available zPIV Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
/* min-height:35px; */
}

QWidget .QFrame#frame_3 .QLabel#labelzBalancez { /* Available zPIV Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:bold;
color:#5B4C7C;
margin-left:0px;
}

QWidget .QFrame#frame_3 .QLabel#labelLockedBalanceText { /* Available zPIV Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
/* min-height:35px; */
}

QWidget .QFrame#frame_3 .QLabel#labelLockedBalance { /* Available zPIV Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:normal;
color:#5B4C7C;
margin-left:0px;
}

QWidget .QFrame#frame_3 .QLabel#labelUnLockedBalanceText { /* Available zPIV Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
/* min-height:35px; */
}

QWidget .QFrame#frame_3 .QLabel#labelUnLockedBalance { /* Available zPIV Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:normal;
color:#5B4C7C;
margin-left:0px;
}

QWidget .QFrame#frame_3 .QLabel#labelTotalTextz { /* Available total Label */
qproperty-alignment: 'AlignVCenter | AlignRight';
min-width:160px;
background-color:#5B4C7C;
color:#fff;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:14px;
/* min-height:35px; */
}

QWidget .QFrame#frame_3 .QLabel#labelTotalz { /* Available total Balance */
qproperty-alignment: 'AlignVCenter | AlignLeft';
font-size:12px;
font-weight:bold;
color:#5B4C7C;
margin-left:0px;
}

/**************************** RECENT TRANSACTIONS ***************************************/

QWidget .QFrame#frame_2 { /* Transactions Widget */
min-width:410px;
min-height:100px;
margin-right:20px;
margin-left:0;
margin-top:0;
background-image: url(':/images/pivx_logo_horizontal');
background-repeat:none;
}

QWidget .QFrame#frame_2 .QLabel#label_4 { /* Recent Transactions Label */
min-width:180px;
color:#382f44;
margin-left:67px;
margin-top:83px;
margin-right:5px;
padding-right:5px;
font-weight:bold;
font-size:15px;
min-height:24px;
}

QWidget .QFrame#frame_2 .QLabel#labelTransactionsStatus { /* Recent Transactions Sync Status */
qproperty-alignment: 'AlignBottom | AlignRight';
min-width:93px;
margin-top:83px;
margin-left:16px;
margin-right:5px;
min-height:16px;
}

QWidget .QFrame#frame_2 QListView { /* Transaction List */
font-weight:normal;
font-size:12px;
max-width:369px;
margin-top:12px;
margin-left:0px; /* CSS Voodoo - set to -66px to hide default transaction icons */
}

/**************************** SEND DIALOG ***********************************************/

QDialog#SendCoinsDialog {
background-color:#fff;
}

QDialog#SendCoinsDialog .QFrame#frameCoinControl { /* Coin Control Section */
}

QDialog#SendCoinsDialog .QFrame#frameCoinControl > .QLabel { /* Default Font Color and  Size */
font-weight:normal;
}

QDialog#SendCoinsDialog .QFrame#frameCoinControl .QPushButton#pushButtonCoinControl { /* Coin Control Inputs Button */
padding-left:10px;
padding-right:10px;
min-height:25px;
}

QDialog#SendCoinsDialog .QFrame#frameCoinControl .QLabel#labelCoinControlFeatures { /* Coin Control Header */
color:#382f44;
font-weight:bold;
font-size:14px;
}

QDialog#SendCoinsDialog .QFrame#frameCoinControl .QWidget#widgetCoinControl { /* Coin Control Inputs */
}

QDialog#SendCoinsDialog .QFrame#frameCoinControl .QWidget#widgetCoinControl > .QLabel { /* Coin Control Inputs Labels */
padding:2px;
}

QDialog#SendCoinsDialog .QFrame#frameCoinControl .QCheckBox#checkBoxCoinControlChange { /* Custom Change Label */
}

QDialog#SendCoinsDialog .QFrame#frameCoinControl .QValidatedLineEdit#lineEditCoinControlChange { /* Custom Change Address */
}

QDialog#SendCoinsDialog .QFrame#frameCoinControl .QLabel#labelCoinControlChangeLabel { /* Custom Change Address Validation Label */
font-weight:normal;
qproperty-margin:-6;
margin-right:112px;
}

QDialog#SendCoinsDialog .QScrollArea#scrollArea .QWidget#scrollAreaWidgetContents { /* Send To Widget */
background:transparent;
}

QDialog#SendCoinsDialog .QPushButton#sendButton { /* Send Button */
padding-left:10px;
padding-right:10px;
}

QDialog#SendCoinsDialog .QPushButton#clearButton { /* Clear Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QDialog#SendCoinsDialog .QPushButton#clearButton:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QDialog#SendCoinsDialog .QPushButton#clearButton:pressed {
border:1px solid #9e9e9e;
}

QDialog#SendCoinsDialog .QPushButton#addButton { /* Add Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QDialog#SendCoinsDialog .QPushButton#addButton:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QDialog#SendCoinsDialog .QPushButton#addButton:pressed {
border:1px solid #9e9e9e;
}

QDialog#SendCoinsDialog .QCheckBox#checkUseObfuscation { /* Obfuscation Checkbox */
color:#616161;
font-weight:bold;
background:transparent;
/*background: qradialgradient(cx:0.5, cy:0.5, radius: 0.5, fx:0.5, fy:0.5, stop:0 rgba(248, 246, 246, 128), stop: 1 rgba(0, 0, 0, 0));*/
border-radius:5px;
padding-top:20px;
padding-bottom:18px;
}

QDialog#SendCoinsDialog .QCheckBox#checkSwiftTX { /* SwiftTX Checkbox */
color:#616161;
font-weight:bold;
background:transparent;
/*background: qradialgradient(cx:0.5, cy:0.5, radius: 0.5, fx:0.5, fy:0.5, stop:0 rgba(248, 246, 246, 128), stop: 1 rgba(0, 0, 0, 0));*/
border-radius:5px;
padding-top:20px;
padding-bottom:18px;
margin-left:10px;
margin-right:20px;
}

/* This QLabel uses name = "label" which conflicts with Address Book -> New Address */
/* To maximize backwards compatibility this formatting has been removed */

QDialog#SendCoinsDialog QLabel#label {
/*margin-left:20px;
margin-right:-2px;
padding-right:-2px;
color:#616161;
font-size:14px;
font-weight:bold;
border-radius:5px;
padding-top:20px;
padding-bottom:20px;*/
min-height:27px;
}

QDialog#SendCoinsDialog QLabel#labelBalance {
margin-left:0px;
padding-left:0px;
color:#333333;
/* font-weight:bold;
border-radius:5px;
padding-top:20px;
padding-bottom:20px; */
min-height:27px;
}

/**************************** SEND COINS ENTRY ******************************************/

QStackedWidget#SendCoinsEntry .QFrame#SendCoins > .QLabel { /* Send Coin Entry Labels */
background-color:#5B4C7C;
min-width:102px;
font-weight:bold;
font-size:11px;
color:#ffffff;
min-height:25px;
margin-right:5px;
padding-right:5px;
}

QStackedWidget#SendCoinsEntry .QFrame#SendCoins .QLabel#amountLabel {
background-color:#6a6a6a;
}

QStackedWidget#SendCoinsEntry .QValidatedLineEdit#payTo { /* Pay To Input Field */
}

QStackedWidget#SendCoinsEntry .QToolButton { /* General Settings for Pay To Icons */
background-color:transparent;
padding-left:5px;
padding-right:5px;
border: 0;
outline: 0;
}

QStackedWidget#SendCoinsEntry .QToolButton#addressBookButton { /* Address Book Button */
padding-left:10px;
}

QStackedWidget#SendCoinsEntry .QToolButton#addressBookButton {
}

QStackedWidget#SendCoinsEntry .QToolButton#pasteButton {
}

QStackedWidget#SendCoinsEntry .QToolButton#deleteButton {
}

QStackedWidget#SendCoinsEntry .QLineEdit#addAsLabel { /* Pay To Input Field */
}

/**************************** COIN CONTROL POPUP ****************************************/

QDialog#CoinControlDialog .QLabel#labelCoinControlQuantityText { /* Coin Control Quantity Label */
min-height:30px;
padding-left:15px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlQuantity { /* Coin Control Quantity */
min-height:30px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlBytesText { /* Coin Control Bytes Label */
padding-left:15px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlBytes { /* Coin Control Bytes */
}

QDialog#CoinControlDialog .QLabel#labelCoinControlAmountText { /* Coin Control Amount Label */
min-height:30px;
padding-left:15px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlAmount { /* Coin Control Amount */
min-height:30px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlPriorityText { /* Coin Control Priority Label */
padding-left:15px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlPriority { /* Coin Control Priority */
}

QDialog#CoinControlDialog .QLabel#labelCoinControlFeeText { /* Coin Control Fee Label */
min-height:30px;
padding-left:15px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlFee { /* Coin Control Fee */
min-height:30px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlLowOutputText { /* Coin Control Low Output Label */
padding-left:15px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlLowOutput { /* Coin Control Low Output */
}

QDialog#CoinControlDialog .QLabel#labelCoinControlAfterFeeText { /* Coin Control After Fee Label */
min-height:30px;
padding-left:15px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlAfterFee { /* Coin Control After Fee */
min-height:30px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlChangeText { /* Coin Control Change Label */
padding-left:15px;
}

QDialog#CoinControlDialog .QLabel#labelCoinControlChange { /* Coin Control Change */

}

QDialog#CoinControlDialog .QFrame#frame .QPushButton#pushButtonSelectAll { /* (un)select all button */
padding-left:10px;
padding-right:10px;
min-height:25px;
}

QDialog#CoinControlDialog .QFrame#frame .QPushButton#pushButtonToggleLock { /* Toggle lock state button */
padding-left:10px;
padding-right:10px;
min-height:25px;
}

QDialog#CoinControlDialog .QDialogButtonBox#buttonBox QPushButton { /* Coin Control 'OK' button */
}

QDialog#CoinControlDialog .QFrame#frame .QRadioButton#radioTreeMode { /* Coin Control Tree Mode Selector */
color:#818181;
background-color:transparent;
}

QDialog#CoinControlDialog .QFrame#frame .QRadioButton#radioListMode { /* Coin Control List Mode Selector */
color:#818181;
background-color:transparent;
}

QDialog#CoinControlDialog QHeaderView::section:first { /* Bug Fix: the number "1" displays in this table for some reason... */
color:transparent;
}

QDialog#CoinControlDialog .CoinControlTreeWidget#treeWidget { /* Coin Control Widget Container */
outline:0;
background-color:#ffffff;
border:1px solid #333;
}

QDialog#CoinControlDialog .CoinControlTreeWidget#treeWidget::item {
}

QDialog#CoinControlDialog .CoinControlTreeWidget#treeWidget::item:selected { /* Coin Control Item (selected) */
background-color:#5B4C7C;
color:#fff;
}

QDialog#CoinControlDialog .CoinControlTreeWidget#treeWidget::item:checked { /* Coin Control Item (checked) */
background-color:#f7f7f7;
color:#fff;
}

QDialog#CoinControlDialog .CoinControlTreeWidget#treeWidget::branch:selected { /* Coin Control Branch Icon */
background-image: url(':/images/qtreeview_selected');
background-repeat:no-repeat;
background-position:center;
background-color:#f7f7f7;
color:#333;
}

QDialog#CoinControlDialog .CoinControlTreeWidget#treeWidget::branch:checked { /* Coin Control Branch Icon */
background-image: url(':/images/qtreeview_selected');
background-repeat:no-repeat;
background-position:center;
background-color:#f7f7f7;
color:#333;
}

QDialog#CoinControlDialog .CoinControlTreeWidget#treeWidget::seperator {

}

QDialog#CoinControlDialog .CoinControlTreeWidget#treeWidget::indicator { /* Coin Control Widget Checkbox */

}

/**************************** RECEIVE COINS *********************************************/

QWidget#ReceiveCoinsDialog {
background-color:#fff;
}

QWidget#ReceiveCoinsDialog .QFrame#frame2 .QLabel#label_2 { /* Label Label */
background-color:#5B4C7C;
min-width:102px;
color:#ffffff;
font-weight:bold;
font-size:11px;
padding-right:5px;
}

QWidget#ReceiveCoinsDialog .QFrame#frame2 .QLabel#label { /* Amount Label */
background-color:#6a6a6a;
min-width:102px;
color:#ffffff;
font-weight:bold;
font-size:11px;
padding-right:5px;
}

QWidget#ReceiveCoinsDialog .QFrame#frame2 .QLabel#label_3 { /* Message Label */
background-color:#5B4C7C;
min-width:102px;
color:#ffffff;
font-weight:bold;
font-size:11px;
padding-right:5px;
}

QWidget#ReceiveCoinsDialog .QFrame#frame2 QPushButton#clearButton { /* Clear Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QWidget#ReceiveCoinsDialog .QFrame#frame2 QPushButton#clearButton:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QWidget#ReceiveCoinsDialog .QFrame#frame2 QPushButton#clearButton:pressed {
border:1px solid #9e9e9e;
}

QWidget#ReceiveCoinsDialog .QFrame#frame QPushButton#showRequestButton { /* Show Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QWidget#ReceiveCoinsDialog .QFrame#frame QPushButton#showRequestButton:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QWidget#ReceiveCoinsDialog .QFrame#frame QPushButton#showRequestButton:pressed {
border:1px solid #9e9e9e;
}

QWidget#ReceiveCoinsDialog .QFrame#frame QPushButton#removeRequestButton { /* Remove Button */
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(250, 250, 250, 128), stop: .95 rgba(250, 250, 250, 255), stop: 1 #ebebeb);
border:1px solid #d2d2d2;
color:#616161;
padding-left:10px;
padding-right:10px;
}

QWidget#ReceiveCoinsDialog .QFrame#frame QPushButton#removeRequestButton:hover {
background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: .01 #f6f6f6, stop: .1 rgba(240, 240, 240, 255), stop: .95 rgba(240, 240, 240, 255), stop: 1 #ebebeb);
color:#333;
}

QWidget#ReceiveCoinsDialog .QFrame#frame QPushButton#removeRequestButton:pressed {
border:1px solid #9e9e9e;
}

QWidget#ReceiveCoinsDialog .QFrame#frame .QLabel#label_6 { /* Requested Payments History Label */
color:#382f44;
font-weight:bold;
font-size:14px;
}

/**************************** RECEIVE COINS DIALOG **************************************/

QDialog#ReceiveRequestDialog QTextEdit { /* Contents of Receive Coin Dialog */
border:1px solid #d7d7d7;
}

/**************************** TRANSACTIONS PAGE *****************************************/

TransactionView QLineEdit { /* Address Filter */
margin-bottom:2px;
margin-right:1px;
min-width:111px;
text-align:center;
}

TransactionView QComboBox {
margin-bottom:1px;
margin-right:1px;
}

QLabel#transactionSumLabel { /* Example of setObjectName for widgets without name */
color:#333333;
font-weight:bold;
}

QLabel#transactionSum { /* Example of setObjectName for widgets without name */
color:#333333;
}

/**************************** TRANSACTION DETAILS DIALOG ********************************/

QDialog#TransactionDescDialog QTextEdit { /* Contents of Receive Coin Dialog */
border:1px solid #d7d7d7;
}

/********************************* CALENDAR WIDGET **************************************/

QCalendarView {
border:1px solid #333;
}

QCalendarWidget QWidget#qt_calendar_navigationbar { /* Calendar widget navigation bar */
background-color:#5B4C7C;
font-weight:bold;
}

QCalendarWidget QAbstractItemView { /* Calendar widget days */
alternate-background-color:#dedbe5;
background-color:#fff;
}

QCalendarWidget QAbstractItemView:enabled { /* Calendar widget weekdays in month */
color:#333;
}

QCalendarWidget QAbstractItemView:disabled { /* Calendar widget days not in month */
color:#818181;
}


text:
<!doctype html>

    <title>Code coverage report for ethereum.js/lib/web3/filter.js</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="../../../prettify.css">
    <link rel="stylesheet" href="../../../base.css">

Code coverage report for ethereum.js/lib/web3/filter.js

        Statements: 86.25% (69 / 80)
        Branches: 68.42% (26 / 38)
        Functions: 82.61% (19 / 23)
        Lines: 86.25% (69 / 80)
        Ignored: none

All files » ethereum.js/lib/web3/ » filter.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

1
1
1

1

24
9

15

15
12

3

1

16
7

9

9
9
22

9

1

8
3

3

3
3

1

8
7

7
7
7
7

8

1
16
16
16
64

16
16
16
16
16
16
16
16

16

16

16

16
4

1
8

8
8
8

8

1
8

8
8

1
3
3
3
3

3
3

3

1

/*
    This file is part of ethereum.js.

    ethereum.js is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ethereum.js is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with ethereum.js.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file filter.js
 * @authors:
 *   Jeffrey Wilcke <jeff@ethdev.com>
 *   Marek Kotewicz <marek@ethdev.com>
 *   Marian Oancea <marian@ethdev.com>
 *   Fabian Vogelsteller <fabian@ethdev.com>
 *   Gav Wood <g@ethdev.com>
 * @date 2014
 */

var RequestManager = require('./requestmanager');
var formatters = require('./formatters');
var utils = require('../utils/utils');

/**
* Converts a given topic to a hex string, but also allows null values.
*
* @param {Mixed} value
* @return {String}
*/
var toTopic = function(value){

    if(value === null || typeof value === 'undefined')
        return null;

    value = String(value);

    if(value.indexOf('0x') === 0)
        return value;
    else
        return utils.fromAscii(value);
};

/// This method should be called on options object, to verify deprecated properties && lazy load dynamic ones
/// @param should be string or object
/// @returns options string or object
var getOptions = function (options) {

    if (utils.isString(options)) {
        return options;
    }

    options = options || {};

    // make sure topics, get converted to hex
    options.topics = options.topics || [];
    options.topics = options.topics.map(function(topic){
        return (utils.isArray(topic)) ? topic.map(toTopic) : toTopic(topic);
    });

    // lazy load
    return {
        topics: options.topics,
        to: options.to,
        address: options.address,
        fromBlock: formatters.inputBlockNumberFormatter(options.fromBlock),
        toBlock: formatters.inputBlockNumberFormatter(options.toBlock)
    };
};

/**
Adds the callback and sets up the methods, to iterate over the results.

@method getLogsAtStart
@param {Object} self
@param {funciton}
*/
var getLogsAtStart = function(self, callback){
    // call getFilterLogs for the first watch callback start
    if (!utils.isString(self.options)) {
        self.get(function (err, messages) {
            // don't send all the responses to all the watches again... just to self one
            Iif (err) {
                callback(err);
            }

            messages.forEach(function (message) {
                callback(null, message);
            });
        });
    }
};

/**
Adds the callback and sets up the methods, to iterate over the results.

@method pollFilter
@param {Object} self
*/
var pollFilter = function(self) {

    var onMessage = function (error, messages) {
        Iif (error) {
            return self.callbacks.forEach(function (callback) {
                callback(error);
            });
        }

        messages.forEach(function (message) {
            message = self.formatter ? self.formatter(message) : message;
            self.callbacks.forEach(function (callback) {
                callback(null, message);
            });
        });
    };

    RequestManager.getInstance().startPolling({
        method: self.implementation.poll.call,
        params: [self.filterId],
    }, self.filterId, onMessage, self.stopWatching.bind(self));

};

var Filter = function (options, methods, formatter, callback) {
    var self = this;
    var implementation = {};
    methods.forEach(function (method) {
        method.attachToObject(implementation);
    });
    this.options = getOptions(options);
    this.implementation = implementation;
    this.filterId = null;
    this.callbacks = [];
    this.pollFilters = [];
    this.formatter = formatter;
    this.implementation.newFilter(this.options, function(error, id){
        Iif(error) {
            self.callbacks.forEach(function(cb){
                cb(error);
            });
        } else {
            self.filterId = id;

            // get filter logs for the already existing watch calls
            self.callbacks.forEach(function(cb){
                getLogsAtStart(self, cb);
            });
            Iif(self.callbacks.length > 0)
                pollFilter(self);

            // start to watch immediately
            if(callback) {
                return self.watch(callback);
            }
        }
    });

};

Filter.prototype.watch = function (callback) {
    this.callbacks.push(callback);

    Eif(this.filterId) {
        getLogsAtStart(this, callback);
        pollFilter(this);
    }

    return this;
};

Filter.prototype.stopWatching = function () {
    RequestManager.getInstance().stopPolling(this.filterId);
    // remove filter async
    this.implementation.uninstallFilter(this.filterId, function(){});
    this.callbacks = [];
};

Filter.prototype.get = function (callback) {
    var self = this;
    Eif (utils.isFunction(callback)) {
        this.implementation.getLogs(this.filterId, function(err, res){
            Iif (err) {
                callback(err);
            } else {
                callback(null, res.map(function (log) {
                    return self.formatter ? self.formatter(log) : log;
                }));
            }
        });
    } else {
        var logs = this.implementation.getLogs(this.filterId);
        return logs.map(function (log) {
            return self.formatter ? self.formatter(log) : log;
        });
    }

    return this;
};

module.exports = Filter;

Generated by istanbul at Tue Jul 07 2015 12:06:22 GMT+0200 (CEST)


text:
var _manifest;
var _manifestError;
var _offlineView;
var _mainView;
var _zIndex = 10000;
var _enableOfflineSupport = true;
var _lastKnownLocation;
var _lastKnownLocationFailed = false;
var _whiteList = [];

function bridgeNativeEvent(e) {
    _mainView.invokeScriptAsync('eval', "cordova && cordova.fireDocumentEvent('" + e.type + "', null, true);").start();
}

//document.addEventListener('backbutton', bridgeNativeEvent, false);
document.addEventListener('pause', bridgeNativeEvent, false);
document.addEventListener('resume', bridgeNativeEvent, false);

// creates a webview to host content
function configureHost(url, zOrder, display) {
    var webView = document.createElement(cordova.platformId === 'windows8' ? 'iframe' : 'x-ms-webview');
    var style = webView.style;
    style.position = 'absolute';
    style.top = 0;
    style.left = 0;
    style.zIndex = zOrder;
    style.width = '100%';
    style.height = '100%';
    if (display) {
        style.display = display;
    }

    if (url) {
        webView.src = url;
    }

    webView.addEventListener("MSWebViewNavigationCompleted", navigationCompletedEvent, false);
    webView.addEventListener("MSWebViewNavigationStarting", navigationStartingEvent, false);

    document.body.appendChild(webView);

    return webView;
}

// handles webview's navigation starting event
function navigationStartingEvent(evt) {
    if (handleCordovaExecCalls(evt)) {
        evt.stopImmediatePropagation();
        evt.preventDefault();
        return;
    }

    if (evt.uri && evt.uri !== "") {
        var isInWhitelist = false;
        for (var i = 0; i < _whiteList.length; i++) {
            var rule = _whiteList[i];
            if (rule.test(evt.uri)) {
                isInWhitelist = true;
                break;
            }
        }

        // if the url to navigate to does not match any of the rules in the whitelist, open it outside de app
        if (!isInWhitelist) {
            evt.stopImmediatePropagation();
            evt.preventDefault();
            console.log("Whitelist rejection: url='" + evt.uri + "'");
            Windows.System.Launcher.launchUriAsync(new Windows.Foundation.Uri(evt.uri));
        }
    }
}

// handles webview's navigation completed event
function navigationCompletedEvent(evt) {
    if (evt.uri && evt.uri !== "") {
        if (evt.isSuccess) {
            _lastKnownLocationFailed = false;
            if (_offlineView) {
                _offlineView.style.display = 'none';
            }
        } else {
            _lastKnownLocationFailed = true;
        }

        _lastKnownLocation = evt.uri;
    }
}

function domContentLoadedEvent(evt) {
    console.log('Finished loading URL: ' + _mainView.src);

    hideExtendedSplashScreen();

    // inject Cordova
    if (isCordovaEnabled()) {
        var cordova = _manifest.mjs_cordova || {};

        var pluginMode = cordova.plugin_mode || 'client';
        var cordovaBaseUrl = (cordova.base_url || '').trim();
        if (cordovaBaseUrl.indexOf('/', cordovaBaseUrl.length - 1) === -1) {
            cordovaBaseUrl += '/';
        }

        _mainView.invokeScriptAsync('eval', 'window.hostedWebApp = { \'platform\': \'windows\', \'pluginMode\': \'' + pluginMode + '\', \'cordovaBaseUrl\': \'' + cordovaBaseUrl + '\'};').start();

        var scriptsToInject = [];
        if (pluginMode === 'client') {
            scriptsToInject.push('cordova.js');
        }

        scriptsToInject.push('hostedapp-bridge.js');
        injectScripts(scriptsToInject);
    }

    // inject import scripts
    if (_manifest && _manifest.mjs_import_scripts && _manifest.mjs_import_scripts instanceof Array) {
        var scriptFiles = _manifest.mjs_import_scripts
            .filter(isMatchingRuleForPage)
            .map(function (item) {
                return item.src;
            });

        if (scriptFiles.length) {
            injectScripts(scriptFiles);
        }
    }
}

// checks if Cordova runtime environment is enabled for the current page
function isCordovaEnabled() {
    var allow = true;
    var enableCordova = false;
    var accessRules = _manifest.mjs_api_access;
    if (accessRules) {
        accessRules.forEach(function (rule) {
            if (isMatchingRuleForPage(rule, true)) {
                var access = rule.access;
                if (!access || access === 'cordova') {
                    enableCordova = true;
                }
                else if (access === 'none') {
                    allow = false;
                }
                else {
                    console.log('Unsupported API access type \'' + access + '\' found in mjs_api_access rule.');
                }
            }
        });
    }

    return enableCordova && allow;
}

// check if an API access or custom script match rule applies to the current page
function isMatchingRuleForPage(rule, checkPlatform) {

    // ensure rule applies to current platform
    if (checkPlatform) {
        if (rule.platform && rule.platform.split(',')
            .map(function (item) { return item.trim(); })
            .indexOf('windows') < 0) {
                return false;
            }
    }

    // ensure rule applies to current page
    var match = rule.match;
    if (match) {
        if (typeof match === 'string' && match.length) {
            match = [match];
        }

        return match.some(function (item) { return convertPatternToRegex(item).test(_mainView.src); });
    }

    return true;
}

// handles network connectivity change events
function connectivityEvent(evt) {
    console.log('Received a network connectivity change notification. The device is currently ' + evt.type + '.');
    if (_enableOfflineSupport) {
        if (evt.type === 'offline') {
            _offlineView.style.display = 'block';
        } else if (evt.type === 'online') {
            if (_lastKnownLocationFailed) {
                if (_lastKnownLocation) {
                    console.log("Reload last known location: '" + _lastKnownLocation + "'");
                    _mainView.src = _lastKnownLocation;
                }
            } else {
                _offlineView.style.display = 'none';
            }
        }
    }
}

// sets up a secondary webview to host the offline page
function configureOfflineSupport(offlinePage) {
    var offlinePageUrl = '///www/' + offlinePage;
    var url = new Windows.Foundation.Uri('ms-appx:' + offlinePageUrl);
    Windows.Storage.StorageFile.getFileFromApplicationUriAsync(url).then(
        function (file) {
            _offlineView = configureHost('ms-appx-web:' + offlinePageUrl, 10001, 'none');
        },
        function (err) {
            var message = 'It looks like you are offline. Please reconnect to use this application.';
            var offlinePageTemplate = '
' + message + '
';
            _offlineView = configureHost(null, _zIndex + 1, 'none');

            if (cordova.platformId === 'windows8') {
                _offlineView.style.backgroundColor = 'white';
                _offlineView.contentDocument.write(offlinePageTemplate);
            } else {
                _offlineView.navigateToString(offlinePageTemplate);
            }
        }).done(function () {
            document.addEventListener('offline', connectivityEvent, false);
            document.addEventListener('online', connectivityEvent, false);
        });
}

// escapes regular expression reserved symbols
function escapeRegex(str) {
    return ("" + str).replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
}

// converts a string pattern to a regular expression
function convertPatternToRegex(pattern, excludeLineStart, excludeLineEnd) {
    var isNot = (pattern[0] == '!');
    if (isNot) { pattern = pattern.substr(1) };

    var regexBody = escapeRegex(pattern);

    excludeLineStart = !!excludeLineStart;
    excludeLineEnd = !!excludeLineEnd;

    regexBody = regexBody.replace(/\\\?/g, ".?").replace(/\\\*/g, ".*?");
    if (isNot) { regexBody = "((?!" + regexBody + ").)*"; }
    if (!excludeLineStart) { regexBody = "^" + regexBody; }
    if (!excludeLineEnd) { regexBody += "\/?$"; }

    return new RegExp(regexBody);
}

// initializes an array of the access rules defined in the manifest
function configureWhiteList(manifest) {
    if (manifest) {
        // Add base access rule based on the start_url and the scope
        var baseUrlPattern = new Windows.Foundation.Uri(manifest.start_url);
        if (manifest.scope && manifest.scope.length) {
            baseUrlPattern = baseUrlPattern.combineUri(manifest.scope);
        }

        baseUrlPattern = baseUrlPattern.combineUri('*');
        _whiteList.push(convertPatternToRegex(baseUrlPattern.absoluteUri));

        // add additional access rules from mjs_access_whitelist
        // TODO: mjs_access_whitelist is deprecated. Should be removed in future versions
        if (manifest.mjs_access_whitelist && manifest.mjs_access_whitelist instanceof Array) {
            manifest.mjs_access_whitelist.forEach(function (rule) {
                _whiteList.push(convertPatternToRegex(rule.url));
            });
        }

        // add additional access rules from mjs_extended_scope
        if (manifest.mjs_extended_scope && manifest.mjs_extended_scope instanceof Array) {
            manifest.mjs_extended_scope.forEach(function (rule) {
                _whiteList.push(convertPatternToRegex(rule));
            });
        }
    }
}

// hides the extended splash screen
function hideExtendedSplashScreen() {
    var extendedSplashScreen = document.getElementById("extendedSplashScreen");
    extendedSplashScreen.style.display = "none";
}

// handle the hardware backbutton
function navigateBack(e) {
    if (!_mainView.canGoBack) {
        return false;
    }

    try {
        _mainView.goBack();
    } catch (err) {
        return false;
    }

    return true;
}

var exec = require('cordova/exec');

function injectScripts(files, successCallback, errorCallback) {

    var script = (arguments.length > 3 && typeof arguments[3] === 'string') ? arguments[3] : '';
    var fileList = (arguments.length > 4 && typeof arguments[4] === 'string') ? arguments[4] : '';

    if (typeof files === 'string') {
        files = files.length ? [files] : [];
    }

    var fileName = files.shift();
    if (!fileName) {
        var asyncOp = _mainView.invokeScriptAsync('eval', script);
        asyncOp.oncomplete = function () { successCallback && successCallback(true); };
        asyncOp.onerror = function (err) {
            console.log('Error injecting script file(s): ' + fileList + ' - ' + asyncOp.error);
            errorCallback && errorCallback(err);
        };

        asyncOp.start()
        return;
    }

    console.log('Injecting script file: ' + fileName);
    var uri = new Windows.Foundation.Uri('ms-appx:///www/', fileName);

    var onSuccess = function (content) {
        script += '\r\n//# sourceURL=' + fileName + '\r\n' + content;
        injectScripts(files, successCallback, errorCallback, script, (fileList ? ', ' : '') + fileName);
    };

    var onError = function (err) {
        console.log('Error retrieving script file from app package: ' + fileName + ' - ' + err);
        if (errorCallback) {
            errorCallback(err);
        }
    };

    if (uri.schemeName == 'ms-appx') {
        Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri)
            .done(function (file) {
                Windows.Storage.FileIO.readTextAsync(file)
                    .done(onSuccess, onError);
            }, onError);
    } else {
        var httpClient = new Windows.Web.Http.HttpClient();
        httpClient.getStringAsync(uri).done(onSuccess, onError);
        httpClient.close();
    }
}

function handleCordovaExecCalls(evt) {
    if (evt.uri) {
        var targetUri = new Windows.Foundation.Uri(evt.uri);
        if (targetUri.host === '.cordova' && targetUri.path === '/exec') {
            var service = targetUri.queryParsed.getFirstValueByName('service');
            var action = targetUri.queryParsed.getFirstValueByName('action');
            var args = JSON.parse(decodeURIComponent(targetUri.queryParsed.getFirstValueByName('args')));
            var callbackId = targetUri.queryParsed.getFirstValueByName('callbackId');

            var success, fail;
            if (callbackId !== '0') {
                success = function (args) {
                    var params = args ? '"' + encodeURIComponent(JSON.stringify(args)) + '"' : '';
                    var script = 'cordova.callbacks["' + callbackId + '"].success(' + params + ');';
                    _mainView.invokeScriptAsync('eval', script).start();
                };

                fail = function (err) {
                    var params = args ? '"' + encodeURIComponent(JSON.stringify(err)) + '"' : '';
                    var script = 'cordova.callbacks["' + callbackId + '"].fail(' + params + ');';
                    _mainView.invokeScriptAsync('eval', script).start();
                };
            }

            exec(success, fail, service, action, args);

            return true;
        }
    }

    return false;
}

module.exports = {
    // loads the W3C manifest file and parses it
    loadManifest: function (successCallback, errorCallback, args) {
        var manifestFileName = (args && args instanceof Array && args.length > 0) ? args[0] : 'manifest.json';
        var configFile = 'ms-appx:///www/' + manifestFileName;
        var uri = new Windows.Foundation.Uri(configFile);
        Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).then(
            function (file) {
                Windows.Storage.FileIO.readTextAsync(file).then(function (data) {
                    try {
                        _manifest = JSON.parse(data);
                        cordova.fireDocumentEvent("manifestLoaded", { manifest: _manifest });
                        successCallback &&  successCallback(_manifest);
                    } catch (err) {
                        _manifestError = 'Error parsing manifest file: ' + manifestFileName + ' - ' + err.message;
                        console.log(_manifestError);
                    }
                });
            },
            function (err) {
                _manifestError = 'Error reading manifest file: ' + manifestFileName + ' - ' + err;
                console.log(_manifestError);
                errorCallback && errorCallback(err);
            });
    },

    // returns the currently loaded manifest
    getManifest: function (successCallback, errorCallback) {
        if (_manifest) {
            successCallback && successCallback(_manifest);
        } else {
            errorCallback && errorCallback(new Error(_manifestError));
        }
    },

    // enables offline page support
    enableOfflinePage: function (successCallback, errorCallback) {
        _enableOfflineSupport = true;
        successCallback && successCallback();
    },

    // disables offline page support
    disableOfflinePage: function (successCallback, errorCallback) {
        _enableOfflineSupport = false;
        successCallback && successCallback();
    },

    getWebView: function () {
      return _mainView;
    },

    injectPluginScript: function (successCallback, errorCallback, file) {
        injectScripts(file, successCallback, errorCallback);
    }
}; // exports

cordova.commandProxy.add('HostedWebApp', module.exports);

module.exports.loadManifest(
    function (manifest) {
        if (manifest.mjs_offline_feature === false) {
            _enableOfflineSupport = false;
        } else {
            configureOfflineSupport('offline.html');
        }
        configureWhiteList(manifest);
        _mainView = configureHost(manifest ? manifest.start_url : 'about:blank', _zIndex);
        _mainView.addEventListener("MSWebViewDOMContentLoaded", domContentLoadedEvent, false);

        cordova.fireDocumentEvent("webviewCreated", { webView: _mainView });
        WinJS.Application.onbackclick = navigateBack;
    });


text:
<?php

/**
 * @class GetNotificationsTest
 * ===========================
 *
 * Creates two users and has the second follow, upvote, mention, and remix
 * the first. Then logs in as the first and makes sure there are four
 * notifications from the API.
 *
 * Ryff API <http://www.github.com/RyffProject/ryff-api>
 * Released under the Apache License 2.0.
 */

class GetNotificationsTest extends Test {
    /**
     * Overrides abstract function in Test.
     *
     * @return string
     */
    public function get_message() {
        return "Get Notifications test";
    }

    /**
     * Overrides abstract function in Test.
     */
    protected function setup() {
        $this->state["user1"] = $this->env->get_test_user();
        $this->state["user2"] = $this->env->get_test_user();

        //Follow notification
        Follow::add($this->state["user1"]->id, $this->state["user2"]->id);

        //Mention notification
        $this->env->get_test_post(
            $this->state["user2"]->id, array(), array(),
            array($this->state["user1"]->username)
        );

        //Remix notification
        $this->state["post"] = $this->env->get_test_post($this->state["user1"]->id);
        $this->env->get_test_post($this->state["user2"]->id, array($this->state["post"]->id));

        //Upvote notification
        Upvote::add($this->state["post"]->id, $this->state["user2"]->id);

        $this->state["num_notifications"] = 4;
        $this->env->log_user_in($this->state["user1"]->username);
    }

    /**
     * Overrides abstract function in Test.
     *
     * @return boolean
     */
    protected function test() {
        $output = true;
        $results = $this->env->post_to_api("get-notifications");
        if (!$results) {
            $output = false;
        } else if (property_exists($results, "error")) {
            echo "{$results->error}\n";
            echo "Failed to get notifications.\n";
            $output = false;
        } else if (count($results->notifications) !== $this->state["num_notifications"]) {
            echo "Failed to get all four types of notification.\n";
            $output = false;
        }
        return $output;
    }

    /**
     * Overrides abstract function in Test.
     */
    protected function teardown() {
        User::delete($this->state["user1"]->id);
        User::delete($this->state["user2"]->id);
    }
}