A bug with JSON-format strings in UserData values?

I seem to be able to distress the current macOS and iOS builds by using JSON-format strings in notes and userData values.

In iOS, all seems fine until I attempt to edit JSON-format user data strings through the GUI - doing this appears to freeze the app entirely for several minutes.

On macOS, the JSON-format note string is clearly shown in the mouse-over toolTip display, but the GUI text control for editing the note is unresponsive and displays nothing.

( If this can be fixed – I can see that it might be diffcult – it would certainly be helpful - at the moment I’m relying on JSON-formatted user data and notes for a couple of things, including smuggling return values out through the omniJS event-horizon on iOS. )

To reproduce this problem:

  1. Select all or several shapes and lines on an iOS or macOS canvas
  2. Load and call the omniJS function below, which places a JSON dump of the graphics in a canvas background userData item keyed as ‘omniJS’
  3. Try to view and edit that JSON string in the iOS or macOS GUI.

Test code behind disclosure triangle below (just a function definition, call as copyAsJSON(), e.g. from the console, after pasting and entering.

omniJS test code
    // OG () -> omniJS JSON
    function copyAsJSON() {
        'use strict';

        // GENERIC FUNCTIONS -----------------------------------------------------

        // concatMap :: (a -> [b]) -> [a] -> [b]
        const concatMap = (f, xs) => [].concat.apply([], xs.map(f));

        // cons :: a -> [a] -> [a]
        const cons = (x, xs) => [x].concat(xs);

        // curry :: ((a, b) -> c) -> a -> b -> c
        const curry = f => a => b => f(a, b);

        // drop :: Int -> [a] -> [a]
        // drop :: Int -> String -> String
        const drop = (n, xs) => xs.slice(n);

        // elem :: Eq a => a -> [a] -> Bool
        const elem = (x, xs) => xs.indexOf(x) !== -1;

        // filter :: (a -> Bool) -> [a] -> [a]
        const filter = (f, xs) => xs.filter(f);

        // foldl :: (b -> a -> b) -> b -> [a] -> b
        const foldl = (f, a, xs) => xs.reduce(f, a);

        // intercalate :: String -> [a] -> String
        const intercalate = (s, xs) => xs.join(s);

        // log :: a -> IO ()
        const log = (...args) =>
            console.log(
                args
                .map(JSON.stringify)
                .join(' -> ')
            );

        // map :: (a -> b) -> [a] -> [b]
        const map = (f, xs) => xs.map(f);

        // partition :: Predicate -> List -> (Matches, nonMatches)
        // partition :: (a -> Bool) -> [a] -> ([a], [a])
        const partition = (p, xs) =>
            xs.reduce((a, x) =>
                p(x) ? [a[0].concat(x), a[1]] : [a[0], a[1].concat(x)], [
                    [],
                    []
                ]);

        // show :: Int -> a -> Indented String
        // show :: a -> String
        const show = (...x) =>
            JSON.stringify.apply(
                null, x.length > 1 ? [x[1], null, x[0]] : x
            );

        // sort :: Ord a => [a] -> [a]
        const sort = xs => xs.slice()
            .sort();

        // toUpper :: Text -> Text
        const toUpper = s => s.toUpperCase();

        // unlines :: [String] -> String
        const unlines = xs => xs.join('\n');

        // COPY AS JAVASCRIPT ----------------------------------------------------

        // constructorArgs :: {TypeName : [ArgumentName]}
        const constructorArgs = {
            'Group': ['graphics'],
            'Rect': ['x', 'y', 'width', 'height'],
            'Point': ['x', 'y'],
            'Size': ['width', 'height'],
            'SubGraph': ['graphics'],
            'Table': ['graphic']
        };

        // enums :: [String]
        const enums = [
            "autosizing", "columnAlignment", "fillType", "hopType",
            "textHorizontalAlignment", "lineType", "rowAlignment",
            "strokeCap", "strokeJoin", "strokePattern", "strokeType",
            "textVerticalPlacement", "imageSizing"
        ];

        // protoChain :: OG Graphic -> [(String, Prototype)]
        const protoChain = g => {
            const pChain = protoType => {
                const k = protoType.constructor.name;
                return k === 'Object' ? [] : cons(
                    [k, protoType], pChain(Object.getPrototypeOf(protoType))
                );
            };
            return pChain(Object.getPrototypeOf(g));
        };

        // graphicDict :: Canvas -> OG Graphic -> Int -> Dictionary
        const graphicDict = (cnv, g) => {
            const
                strType = Object.getPrototypeOf(g)
                .constructor.name,
                ks = sort(concatMap(([s, p]) =>
                    filter(k => !elem(k, ['id', 'constructor']) &&
                        (typeof g[k] !== 'function'),
                        Object.getOwnPropertyNames(p)
                    ), protoChain(g)));
            return foldl(
                (a, k) => (a[k] = valueJS(cnv, g, strType, k), a), {
                    id: g.id
                },
                ks
            );
        };

        // valueJS :: Canvas -> OG Object -> String -> String -> String
        const valueJS = (cnv, o, strObjType, k) => {
            const
                prop = o[k],
                strPropType = typeof prop;
            const geo = o.geometry;

            return prop === null ? null : (
                strPropType === 'object' ? (() => {
                    const
                        type = Object.getPrototypeOf(prop),
                        strType = type.constructor.name,
                        mbArgs = elem(strType, Object.keys(constructorArgs)) ? ({
                            just: constructorArgs[strType]
                        }) : {
                            nothing: true
                        };

                    return strType === 'Array' ? arrayJS(
                        cnv, strObjType, k, prop
                    ) : !mbArgs.nothing ? (
                        foldl((a, x) => (a.args[x] = prop[x], a), {
                            type: strType,
                            args: {}
                        }, mbArgs.just)
                    ) : elem(k, enums) ? (
                        enumJS(type, strType, prop)
                    ) : strType === 'Color' ? (

                        foldl((a, x) => (a.args[x] = prop[x], a), {
                            type: 'Color.RGB',
                            args: {}
                        }, ['red', 'green', 'blue'])


                    ) : strType === 'Shape' ? (
                        prop.id
                    ) : strType === 'URL' ? (
                        prop.toString()
                    ) : strType === 'Layer' ? (
                        prop.name
                    ) : prop;
                })() : (strPropType === 'string' ? prop : prop));
        };

        // arrayJS :: Canvas, String -> String -> [OG Value] -> [a]
        const arrayJS = (cnv, strObjType, strKey, xs) =>
            xs.length > 0 ? (
                () => {
                    const strType = Object.getPrototypeOf(xs[0])
                        .constructor.name;
                    return (
                        strObjType !== 'Group' || strKey !== 'graphics'
                    ) ? (
                        strType === 'Point' ? (
                            map(p => ({
                                type: 'Point',
                                args: {
                                    x: p.x,
                                    y: p.y
                                }
                            }), xs)
                        ) : map(x => typeof x === 'object' ? x.id : x, xs)
                    ) : map(x => graphicDict(cnv, x), xs);
                }
            )() : '';

        // enumJS :: prototype -> Constructor Name -> OG Value -> String
        const enumJS = (type, strType, prop) =>
            foldl((a, x) => {
                const strEnum = strType + '.' + x;
                return a.nothing ? (
                    prop === eval(strEnum) ? {
                        just: strEnum
                    } : a
                ) : a;
            }, {
                nothing: true
            }, drop(2, Object.getOwnPropertyNames(type.constructor)))
            .just;

        // isLine :: Graphic -> Bool
        const isLine = g =>
            Object.getPrototypeOf(g)
            .constructor.name === 'Line';

        // MAIN --------------------------------------------------------------
        const
            oSeln = document.windows[0].selection,
            cnv = oSeln.canvas,
            gs = oSeln.graphics,
            [lines, shapes] = partition(isLine, gs),
            strJSON = show(2, {
                shapes: map(x => graphicDict(cnv, x), shapes),
                lines: map(x => graphicDict(cnv, x), lines)
            });

        // Save a return value in Canvas background user-data
        return (
            cnv.background.setUserData('omniJSON', strJSON),
            strJSON
        );
    };

I just tried this without reproducing the problem. If you are still encountering issues in OmniGraffle 3.5 for iOS, can you provide some sample metadata I can put on an object or objects that causes the issue? I have this working on iOS and it seems to be pretty instant. How many objects with how much metadata? If I can reproduce the problem I might be able to suggest a workaround if I can identify what is causing the slowness.

Thanks,
Lanette

I think it may have been fixed for some time.

Somebody just copy-pasted an old post, perhaps at random ? I’ve flagged it.

Thank you. I hid the other post & closed the topic. Glad this is solved now.